Merge "Add BluetoothLe#updateServices" into androidx-main
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 8a971d6..a760252 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -6,7 +6,7 @@
 # Owners for each library group:
 /activity*          @sanura-njaka @jbw0033 @ianhanniballake
 /appcompat/*        @alanv
-/biometric/*        @dlam
+/biometric/*        @jbolinger
 /collection/*       @dlam
 /compose/compiler/* @jimgoog @lelandrichardson
 /compose/runtime/*  @jimgoog @lelandrichardson
diff --git a/.github/ci-control/ci-config.json b/.github/ci-control/ci-config.json
index dfb2af6..b7c25eb 100644
--- a/.github/ci-control/ci-config.json
+++ b/.github/ci-control/ci-config.json
@@ -14,6 +14,7 @@
     "main" : {
         "exclude" : [
           "compose-runtime",
+          "core",
           "room"
         ],
         "default": true
diff --git a/appactions/builtintypes/builtintypes/api/current.txt b/appactions/builtintypes/builtintypes/api/current.txt
index fe310a0..719047a 100644
--- a/appactions/builtintypes/builtintypes/api/current.txt
+++ b/appactions/builtintypes/builtintypes/api/current.txt
@@ -408,7 +408,7 @@
     method public final androidx.appactions.builtintypes.properties.Name? getName();
     method public final String? getNamespace();
     method protected abstract String getSelfTypeName();
-    method public final String? getTelephone();
+    method public final String? getTelephoneNumber();
     method public final int hashCode();
     method public final Builder toBuilder();
     method protected abstract Builder toBuilderWithAdditionalPropertiesOnly();
@@ -420,7 +420,7 @@
     property public final androidx.appactions.builtintypes.properties.Name? name;
     property public final String? namespace;
     property protected abstract String selfTypeName;
-    property public final String? telephone;
+    property public final String? telephoneNumber;
   }
 
   public abstract static class AbstractPerson.Builder<Self extends androidx.appactions.builtintypes.types.AbstractPerson.Builder<Self, Built>, Built extends androidx.appactions.builtintypes.types.AbstractPerson<Built, Self>> implements androidx.appactions.builtintypes.types.Person.Builder<Self> {
@@ -436,7 +436,7 @@
     method public final Self setIdentifier(String? text);
     method public final Self setName(androidx.appactions.builtintypes.properties.Name? name);
     method public final Self setNamespace(String? namespace);
-    method public final Self setTelephone(String? text);
+    method public final Self setTelephoneNumber(String? text);
     method public final String toString();
     property protected abstract java.util.Map<java.lang.String,java.lang.Object> additionalProperties;
     property protected abstract String selfTypeName;
@@ -679,8 +679,8 @@
     property protected abstract String selfTypeName;
   }
 
-  @androidx.appsearch.annotation.Document(name="bit:Alarm") public interface Alarm extends androidx.appactions.builtintypes.types.Thing {
-    method public static androidx.appactions.builtintypes.types.Alarm.Builder<?> Builder();
+  @androidx.appsearch.annotation.Document(name="bit:Alarm", parent={Thing::class}) public interface Alarm extends androidx.appactions.builtintypes.types.Thing {
+    method @androidx.appsearch.annotation.Document.BuilderProducer public static androidx.appactions.builtintypes.types.Alarm.Builder<?> Builder();
     method @androidx.appsearch.annotation.Document.DocumentProperty public androidx.appactions.builtintypes.types.Schedule? getAlarmSchedule();
     method @androidx.appsearch.annotation.Document.BooleanProperty public Boolean? isAlarmEnabled();
     method public androidx.appactions.builtintypes.types.Alarm.Builder<?> toBuilder();
@@ -697,7 +697,7 @@
   }
 
   public static final class Alarm.Companion {
-    method public androidx.appactions.builtintypes.types.Alarm.Builder<?> Builder();
+    method @androidx.appsearch.annotation.Document.BuilderProducer public androidx.appactions.builtintypes.types.Alarm.Builder<?> Builder();
   }
 
   public static final class Alarm.DisambiguatingDescriptionValue extends androidx.appactions.builtintypes.properties.DisambiguatingDescription.CanonicalValue {
@@ -710,8 +710,8 @@
   public static final class Alarm.DisambiguatingDescriptionValue.Companion {
   }
 
-  @androidx.appsearch.annotation.Document(name="bit:CommonExecutionStatus") public interface CommonExecutionStatus extends androidx.appactions.builtintypes.types.ExecutionStatus {
-    method public static androidx.appactions.builtintypes.types.CommonExecutionStatus.Builder<?> Builder();
+  @androidx.appsearch.annotation.Document(name="bit:CommonExecutionStatus", parent={ExecutionStatus::class}) public interface CommonExecutionStatus extends androidx.appactions.builtintypes.types.ExecutionStatus {
+    method @androidx.appsearch.annotation.Document.BuilderProducer public static androidx.appactions.builtintypes.types.CommonExecutionStatus.Builder<?> Builder();
     method public androidx.appactions.builtintypes.types.CommonExecutionStatus.Builder<?> toBuilder();
     field public static final androidx.appactions.builtintypes.types.CommonExecutionStatus.Companion Companion;
   }
@@ -721,7 +721,7 @@
   }
 
   public static final class CommonExecutionStatus.Companion {
-    method public androidx.appactions.builtintypes.types.CommonExecutionStatus.Builder<?> Builder();
+    method @androidx.appsearch.annotation.Document.BuilderProducer public androidx.appactions.builtintypes.types.CommonExecutionStatus.Builder<?> Builder();
   }
 
   public final class DayOfWeek {
@@ -754,8 +754,8 @@
     method public default R wednesday();
   }
 
-  @androidx.appsearch.annotation.Document(name="bit:ExecutionStatus") public interface ExecutionStatus extends androidx.appactions.builtintypes.types.Intangible {
-    method public static androidx.appactions.builtintypes.types.ExecutionStatus.Builder<?> Builder();
+  @androidx.appsearch.annotation.Document(name="bit:ExecutionStatus", parent={Intangible::class}) public interface ExecutionStatus extends androidx.appactions.builtintypes.types.Intangible {
+    method @androidx.appsearch.annotation.Document.BuilderProducer public static androidx.appactions.builtintypes.types.ExecutionStatus.Builder<?> Builder();
     method public androidx.appactions.builtintypes.types.ExecutionStatus.Builder<?> toBuilder();
     field public static final androidx.appactions.builtintypes.types.ExecutionStatus.Companion Companion;
   }
@@ -765,11 +765,11 @@
   }
 
   public static final class ExecutionStatus.Companion {
-    method public androidx.appactions.builtintypes.types.ExecutionStatus.Builder<?> Builder();
+    method @androidx.appsearch.annotation.Document.BuilderProducer public androidx.appactions.builtintypes.types.ExecutionStatus.Builder<?> Builder();
   }
 
-  @androidx.appsearch.annotation.Document(name="bit:GenericErrorStatus") public interface GenericErrorStatus extends androidx.appactions.builtintypes.types.CommonExecutionStatus {
-    method public static androidx.appactions.builtintypes.types.GenericErrorStatus.Builder<?> Builder();
+  @androidx.appsearch.annotation.Document(name="bit:GenericErrorStatus", parent={CommonExecutionStatus::class}) public interface GenericErrorStatus extends androidx.appactions.builtintypes.types.CommonExecutionStatus {
+    method @androidx.appsearch.annotation.Document.BuilderProducer public static androidx.appactions.builtintypes.types.GenericErrorStatus.Builder<?> Builder();
     method public androidx.appactions.builtintypes.types.GenericErrorStatus.Builder<?> toBuilder();
     field public static final androidx.appactions.builtintypes.types.GenericErrorStatus.Companion Companion;
   }
@@ -779,11 +779,11 @@
   }
 
   public static final class GenericErrorStatus.Companion {
-    method public androidx.appactions.builtintypes.types.GenericErrorStatus.Builder<?> Builder();
+    method @androidx.appsearch.annotation.Document.BuilderProducer public androidx.appactions.builtintypes.types.GenericErrorStatus.Builder<?> Builder();
   }
 
-  @androidx.appsearch.annotation.Document(name="bit:Intangible") public interface Intangible extends androidx.appactions.builtintypes.types.Thing {
-    method public static androidx.appactions.builtintypes.types.Intangible.Builder<?> Builder();
+  @androidx.appsearch.annotation.Document(name="bit:Intangible", parent={Thing::class}) public interface Intangible extends androidx.appactions.builtintypes.types.Thing {
+    method @androidx.appsearch.annotation.Document.BuilderProducer public static androidx.appactions.builtintypes.types.Intangible.Builder<?> Builder();
     method public androidx.appactions.builtintypes.types.Intangible.Builder<?> toBuilder();
     field public static final androidx.appactions.builtintypes.types.Intangible.Companion Companion;
   }
@@ -793,11 +793,11 @@
   }
 
   public static final class Intangible.Companion {
-    method public androidx.appactions.builtintypes.types.Intangible.Builder<?> Builder();
+    method @androidx.appsearch.annotation.Document.BuilderProducer public androidx.appactions.builtintypes.types.Intangible.Builder<?> Builder();
   }
 
-  @androidx.appsearch.annotation.Document(name="bit:ObjectCreationLimitReachedStatus") public interface ObjectCreationLimitReachedStatus extends androidx.appactions.builtintypes.types.ExecutionStatus {
-    method public static androidx.appactions.builtintypes.types.ObjectCreationLimitReachedStatus.Builder<?> Builder();
+  @androidx.appsearch.annotation.Document(name="bit:ObjectCreationLimitReachedStatus", parent={ExecutionStatus::class}) public interface ObjectCreationLimitReachedStatus extends androidx.appactions.builtintypes.types.ExecutionStatus {
+    method @androidx.appsearch.annotation.Document.BuilderProducer public static androidx.appactions.builtintypes.types.ObjectCreationLimitReachedStatus.Builder<?> Builder();
     method public androidx.appactions.builtintypes.types.ObjectCreationLimitReachedStatus.Builder<?> toBuilder();
     field public static final androidx.appactions.builtintypes.types.ObjectCreationLimitReachedStatus.Companion Companion;
   }
@@ -807,31 +807,31 @@
   }
 
   public static final class ObjectCreationLimitReachedStatus.Companion {
-    method public androidx.appactions.builtintypes.types.ObjectCreationLimitReachedStatus.Builder<?> Builder();
+    method @androidx.appsearch.annotation.Document.BuilderProducer public androidx.appactions.builtintypes.types.ObjectCreationLimitReachedStatus.Builder<?> Builder();
   }
 
-  @androidx.appsearch.annotation.Document(name="bit:Person") public interface Person extends androidx.appactions.builtintypes.types.Thing {
-    method public static androidx.appactions.builtintypes.types.Person.Builder<?> Builder();
+  @androidx.appsearch.annotation.Document(name="bit:Person", parent={Thing::class}) public interface Person extends androidx.appactions.builtintypes.types.Thing {
+    method @androidx.appsearch.annotation.Document.BuilderProducer public static androidx.appactions.builtintypes.types.Person.Builder<?> Builder();
     method @androidx.appsearch.annotation.Document.StringProperty public String? getEmail();
-    method @androidx.appsearch.annotation.Document.StringProperty public String? getTelephone();
+    method @androidx.appsearch.annotation.Document.StringProperty(name="telephone") public String? getTelephoneNumber();
     method public androidx.appactions.builtintypes.types.Person.Builder<?> toBuilder();
     property @androidx.appsearch.annotation.Document.StringProperty public abstract String? email;
-    property @androidx.appsearch.annotation.Document.StringProperty public abstract String? telephone;
+    property @androidx.appsearch.annotation.Document.StringProperty(name="telephone") public abstract String? telephoneNumber;
     field public static final androidx.appactions.builtintypes.types.Person.Companion Companion;
   }
 
   public static interface Person.Builder<Self extends androidx.appactions.builtintypes.types.Person.Builder<Self>> extends androidx.appactions.builtintypes.types.Thing.Builder<Self> {
     method public androidx.appactions.builtintypes.types.Person build();
     method public Self setEmail(String? text);
-    method public Self setTelephone(String? text);
+    method public Self setTelephoneNumber(String? text);
   }
 
   public static final class Person.Companion {
-    method public androidx.appactions.builtintypes.types.Person.Builder<?> Builder();
+    method @androidx.appsearch.annotation.Document.BuilderProducer public androidx.appactions.builtintypes.types.Person.Builder<?> Builder();
   }
 
-  @androidx.appsearch.annotation.Document(name="bit:Schedule") public interface Schedule extends androidx.appactions.builtintypes.types.Intangible {
-    method public static androidx.appactions.builtintypes.types.Schedule.Builder<?> Builder();
+  @androidx.appsearch.annotation.Document(name="bit:Schedule", parent={Intangible::class}) public interface Schedule extends androidx.appactions.builtintypes.types.Intangible {
+    method @androidx.appsearch.annotation.Document.BuilderProducer public static androidx.appactions.builtintypes.types.Schedule.Builder<?> Builder();
     method @androidx.appsearch.annotation.Document.DocumentProperty(name="byDay") public java.util.List<androidx.appactions.builtintypes.properties.ByDay> getByDays();
     method @androidx.appsearch.annotation.Document.LongProperty(name="byMonthDay") public java.util.List<java.lang.Long> getByMonthDays();
     method @androidx.appsearch.annotation.Document.LongProperty(name="byMonthWeek") public java.util.List<java.lang.Long> getByMonthWeeks();
@@ -904,11 +904,11 @@
   }
 
   public static final class Schedule.Companion {
-    method public androidx.appactions.builtintypes.types.Schedule.Builder<?> Builder();
+    method @androidx.appsearch.annotation.Document.BuilderProducer public androidx.appactions.builtintypes.types.Schedule.Builder<?> Builder();
   }
 
-  @androidx.appsearch.annotation.Document(name="bit:SuccessStatus") public interface SuccessStatus extends androidx.appactions.builtintypes.types.CommonExecutionStatus {
-    method public static androidx.appactions.builtintypes.types.SuccessStatus.Builder<?> Builder();
+  @androidx.appsearch.annotation.Document(name="bit:SuccessStatus", parent={CommonExecutionStatus::class}) public interface SuccessStatus extends androidx.appactions.builtintypes.types.CommonExecutionStatus {
+    method @androidx.appsearch.annotation.Document.BuilderProducer public static androidx.appactions.builtintypes.types.SuccessStatus.Builder<?> Builder();
     method public androidx.appactions.builtintypes.types.SuccessStatus.Builder<?> toBuilder();
     field public static final androidx.appactions.builtintypes.types.SuccessStatus.Companion Companion;
   }
@@ -918,11 +918,11 @@
   }
 
   public static final class SuccessStatus.Companion {
-    method public androidx.appactions.builtintypes.types.SuccessStatus.Builder<?> Builder();
+    method @androidx.appsearch.annotation.Document.BuilderProducer public androidx.appactions.builtintypes.types.SuccessStatus.Builder<?> Builder();
   }
 
   @androidx.appsearch.annotation.Document(name="bit:Thing") public interface Thing {
-    method public static androidx.appactions.builtintypes.types.Thing.Builder<?> Builder();
+    method @androidx.appsearch.annotation.Document.BuilderProducer public static androidx.appactions.builtintypes.types.Thing.Builder<?> Builder();
     method @androidx.appsearch.annotation.Document.DocumentProperty public androidx.appactions.builtintypes.properties.DisambiguatingDescription? getDisambiguatingDescription();
     method @androidx.appsearch.annotation.Document.Id public String? getIdentifier();
     method @androidx.appsearch.annotation.Document.DocumentProperty public androidx.appactions.builtintypes.properties.Name? getName();
@@ -946,11 +946,11 @@
   }
 
   public static final class Thing.Companion {
-    method public androidx.appactions.builtintypes.types.Thing.Builder<?> Builder();
+    method @androidx.appsearch.annotation.Document.BuilderProducer public androidx.appactions.builtintypes.types.Thing.Builder<?> Builder();
   }
 
-  @androidx.appsearch.annotation.Document(name="bit:Timer") public interface Timer extends androidx.appactions.builtintypes.types.Thing {
-    method public static androidx.appactions.builtintypes.types.Timer.Builder<?> Builder();
+  @androidx.appsearch.annotation.Document(name="bit:Timer", parent={Thing::class}) public interface Timer extends androidx.appactions.builtintypes.types.Thing {
+    method @androidx.appsearch.annotation.Document.BuilderProducer public static androidx.appactions.builtintypes.types.Timer.Builder<?> Builder();
     method public java.time.Duration? getDuration();
     method public androidx.appactions.builtintypes.types.Timer.Builder<?> toBuilder();
     property public abstract java.time.Duration? duration;
@@ -963,11 +963,11 @@
   }
 
   public static final class Timer.Companion {
-    method public androidx.appactions.builtintypes.types.Timer.Builder<?> Builder();
+    method @androidx.appsearch.annotation.Document.BuilderProducer public androidx.appactions.builtintypes.types.Timer.Builder<?> Builder();
   }
 
-  @androidx.appsearch.annotation.Document(name="bit:UnsupportedOperationStatus") public interface UnsupportedOperationStatus extends androidx.appactions.builtintypes.types.ExecutionStatus {
-    method public static androidx.appactions.builtintypes.types.UnsupportedOperationStatus.Builder<?> Builder();
+  @androidx.appsearch.annotation.Document(name="bit:UnsupportedOperationStatus", parent={ExecutionStatus::class}) public interface UnsupportedOperationStatus extends androidx.appactions.builtintypes.types.ExecutionStatus {
+    method @androidx.appsearch.annotation.Document.BuilderProducer public static androidx.appactions.builtintypes.types.UnsupportedOperationStatus.Builder<?> Builder();
     method public androidx.appactions.builtintypes.types.UnsupportedOperationStatus.Builder<?> toBuilder();
     field public static final androidx.appactions.builtintypes.types.UnsupportedOperationStatus.Companion Companion;
   }
@@ -977,7 +977,7 @@
   }
 
   public static final class UnsupportedOperationStatus.Companion {
-    method public androidx.appactions.builtintypes.types.UnsupportedOperationStatus.Builder<?> Builder();
+    method @androidx.appsearch.annotation.Document.BuilderProducer public androidx.appactions.builtintypes.types.UnsupportedOperationStatus.Builder<?> Builder();
   }
 
 }
diff --git a/appactions/builtintypes/builtintypes/api/restricted_current.txt b/appactions/builtintypes/builtintypes/api/restricted_current.txt
index fe310a0..719047a 100644
--- a/appactions/builtintypes/builtintypes/api/restricted_current.txt
+++ b/appactions/builtintypes/builtintypes/api/restricted_current.txt
@@ -408,7 +408,7 @@
     method public final androidx.appactions.builtintypes.properties.Name? getName();
     method public final String? getNamespace();
     method protected abstract String getSelfTypeName();
-    method public final String? getTelephone();
+    method public final String? getTelephoneNumber();
     method public final int hashCode();
     method public final Builder toBuilder();
     method protected abstract Builder toBuilderWithAdditionalPropertiesOnly();
@@ -420,7 +420,7 @@
     property public final androidx.appactions.builtintypes.properties.Name? name;
     property public final String? namespace;
     property protected abstract String selfTypeName;
-    property public final String? telephone;
+    property public final String? telephoneNumber;
   }
 
   public abstract static class AbstractPerson.Builder<Self extends androidx.appactions.builtintypes.types.AbstractPerson.Builder<Self, Built>, Built extends androidx.appactions.builtintypes.types.AbstractPerson<Built, Self>> implements androidx.appactions.builtintypes.types.Person.Builder<Self> {
@@ -436,7 +436,7 @@
     method public final Self setIdentifier(String? text);
     method public final Self setName(androidx.appactions.builtintypes.properties.Name? name);
     method public final Self setNamespace(String? namespace);
-    method public final Self setTelephone(String? text);
+    method public final Self setTelephoneNumber(String? text);
     method public final String toString();
     property protected abstract java.util.Map<java.lang.String,java.lang.Object> additionalProperties;
     property protected abstract String selfTypeName;
@@ -679,8 +679,8 @@
     property protected abstract String selfTypeName;
   }
 
-  @androidx.appsearch.annotation.Document(name="bit:Alarm") public interface Alarm extends androidx.appactions.builtintypes.types.Thing {
-    method public static androidx.appactions.builtintypes.types.Alarm.Builder<?> Builder();
+  @androidx.appsearch.annotation.Document(name="bit:Alarm", parent={Thing::class}) public interface Alarm extends androidx.appactions.builtintypes.types.Thing {
+    method @androidx.appsearch.annotation.Document.BuilderProducer public static androidx.appactions.builtintypes.types.Alarm.Builder<?> Builder();
     method @androidx.appsearch.annotation.Document.DocumentProperty public androidx.appactions.builtintypes.types.Schedule? getAlarmSchedule();
     method @androidx.appsearch.annotation.Document.BooleanProperty public Boolean? isAlarmEnabled();
     method public androidx.appactions.builtintypes.types.Alarm.Builder<?> toBuilder();
@@ -697,7 +697,7 @@
   }
 
   public static final class Alarm.Companion {
-    method public androidx.appactions.builtintypes.types.Alarm.Builder<?> Builder();
+    method @androidx.appsearch.annotation.Document.BuilderProducer public androidx.appactions.builtintypes.types.Alarm.Builder<?> Builder();
   }
 
   public static final class Alarm.DisambiguatingDescriptionValue extends androidx.appactions.builtintypes.properties.DisambiguatingDescription.CanonicalValue {
@@ -710,8 +710,8 @@
   public static final class Alarm.DisambiguatingDescriptionValue.Companion {
   }
 
-  @androidx.appsearch.annotation.Document(name="bit:CommonExecutionStatus") public interface CommonExecutionStatus extends androidx.appactions.builtintypes.types.ExecutionStatus {
-    method public static androidx.appactions.builtintypes.types.CommonExecutionStatus.Builder<?> Builder();
+  @androidx.appsearch.annotation.Document(name="bit:CommonExecutionStatus", parent={ExecutionStatus::class}) public interface CommonExecutionStatus extends androidx.appactions.builtintypes.types.ExecutionStatus {
+    method @androidx.appsearch.annotation.Document.BuilderProducer public static androidx.appactions.builtintypes.types.CommonExecutionStatus.Builder<?> Builder();
     method public androidx.appactions.builtintypes.types.CommonExecutionStatus.Builder<?> toBuilder();
     field public static final androidx.appactions.builtintypes.types.CommonExecutionStatus.Companion Companion;
   }
@@ -721,7 +721,7 @@
   }
 
   public static final class CommonExecutionStatus.Companion {
-    method public androidx.appactions.builtintypes.types.CommonExecutionStatus.Builder<?> Builder();
+    method @androidx.appsearch.annotation.Document.BuilderProducer public androidx.appactions.builtintypes.types.CommonExecutionStatus.Builder<?> Builder();
   }
 
   public final class DayOfWeek {
@@ -754,8 +754,8 @@
     method public default R wednesday();
   }
 
-  @androidx.appsearch.annotation.Document(name="bit:ExecutionStatus") public interface ExecutionStatus extends androidx.appactions.builtintypes.types.Intangible {
-    method public static androidx.appactions.builtintypes.types.ExecutionStatus.Builder<?> Builder();
+  @androidx.appsearch.annotation.Document(name="bit:ExecutionStatus", parent={Intangible::class}) public interface ExecutionStatus extends androidx.appactions.builtintypes.types.Intangible {
+    method @androidx.appsearch.annotation.Document.BuilderProducer public static androidx.appactions.builtintypes.types.ExecutionStatus.Builder<?> Builder();
     method public androidx.appactions.builtintypes.types.ExecutionStatus.Builder<?> toBuilder();
     field public static final androidx.appactions.builtintypes.types.ExecutionStatus.Companion Companion;
   }
@@ -765,11 +765,11 @@
   }
 
   public static final class ExecutionStatus.Companion {
-    method public androidx.appactions.builtintypes.types.ExecutionStatus.Builder<?> Builder();
+    method @androidx.appsearch.annotation.Document.BuilderProducer public androidx.appactions.builtintypes.types.ExecutionStatus.Builder<?> Builder();
   }
 
-  @androidx.appsearch.annotation.Document(name="bit:GenericErrorStatus") public interface GenericErrorStatus extends androidx.appactions.builtintypes.types.CommonExecutionStatus {
-    method public static androidx.appactions.builtintypes.types.GenericErrorStatus.Builder<?> Builder();
+  @androidx.appsearch.annotation.Document(name="bit:GenericErrorStatus", parent={CommonExecutionStatus::class}) public interface GenericErrorStatus extends androidx.appactions.builtintypes.types.CommonExecutionStatus {
+    method @androidx.appsearch.annotation.Document.BuilderProducer public static androidx.appactions.builtintypes.types.GenericErrorStatus.Builder<?> Builder();
     method public androidx.appactions.builtintypes.types.GenericErrorStatus.Builder<?> toBuilder();
     field public static final androidx.appactions.builtintypes.types.GenericErrorStatus.Companion Companion;
   }
@@ -779,11 +779,11 @@
   }
 
   public static final class GenericErrorStatus.Companion {
-    method public androidx.appactions.builtintypes.types.GenericErrorStatus.Builder<?> Builder();
+    method @androidx.appsearch.annotation.Document.BuilderProducer public androidx.appactions.builtintypes.types.GenericErrorStatus.Builder<?> Builder();
   }
 
-  @androidx.appsearch.annotation.Document(name="bit:Intangible") public interface Intangible extends androidx.appactions.builtintypes.types.Thing {
-    method public static androidx.appactions.builtintypes.types.Intangible.Builder<?> Builder();
+  @androidx.appsearch.annotation.Document(name="bit:Intangible", parent={Thing::class}) public interface Intangible extends androidx.appactions.builtintypes.types.Thing {
+    method @androidx.appsearch.annotation.Document.BuilderProducer public static androidx.appactions.builtintypes.types.Intangible.Builder<?> Builder();
     method public androidx.appactions.builtintypes.types.Intangible.Builder<?> toBuilder();
     field public static final androidx.appactions.builtintypes.types.Intangible.Companion Companion;
   }
@@ -793,11 +793,11 @@
   }
 
   public static final class Intangible.Companion {
-    method public androidx.appactions.builtintypes.types.Intangible.Builder<?> Builder();
+    method @androidx.appsearch.annotation.Document.BuilderProducer public androidx.appactions.builtintypes.types.Intangible.Builder<?> Builder();
   }
 
-  @androidx.appsearch.annotation.Document(name="bit:ObjectCreationLimitReachedStatus") public interface ObjectCreationLimitReachedStatus extends androidx.appactions.builtintypes.types.ExecutionStatus {
-    method public static androidx.appactions.builtintypes.types.ObjectCreationLimitReachedStatus.Builder<?> Builder();
+  @androidx.appsearch.annotation.Document(name="bit:ObjectCreationLimitReachedStatus", parent={ExecutionStatus::class}) public interface ObjectCreationLimitReachedStatus extends androidx.appactions.builtintypes.types.ExecutionStatus {
+    method @androidx.appsearch.annotation.Document.BuilderProducer public static androidx.appactions.builtintypes.types.ObjectCreationLimitReachedStatus.Builder<?> Builder();
     method public androidx.appactions.builtintypes.types.ObjectCreationLimitReachedStatus.Builder<?> toBuilder();
     field public static final androidx.appactions.builtintypes.types.ObjectCreationLimitReachedStatus.Companion Companion;
   }
@@ -807,31 +807,31 @@
   }
 
   public static final class ObjectCreationLimitReachedStatus.Companion {
-    method public androidx.appactions.builtintypes.types.ObjectCreationLimitReachedStatus.Builder<?> Builder();
+    method @androidx.appsearch.annotation.Document.BuilderProducer public androidx.appactions.builtintypes.types.ObjectCreationLimitReachedStatus.Builder<?> Builder();
   }
 
-  @androidx.appsearch.annotation.Document(name="bit:Person") public interface Person extends androidx.appactions.builtintypes.types.Thing {
-    method public static androidx.appactions.builtintypes.types.Person.Builder<?> Builder();
+  @androidx.appsearch.annotation.Document(name="bit:Person", parent={Thing::class}) public interface Person extends androidx.appactions.builtintypes.types.Thing {
+    method @androidx.appsearch.annotation.Document.BuilderProducer public static androidx.appactions.builtintypes.types.Person.Builder<?> Builder();
     method @androidx.appsearch.annotation.Document.StringProperty public String? getEmail();
-    method @androidx.appsearch.annotation.Document.StringProperty public String? getTelephone();
+    method @androidx.appsearch.annotation.Document.StringProperty(name="telephone") public String? getTelephoneNumber();
     method public androidx.appactions.builtintypes.types.Person.Builder<?> toBuilder();
     property @androidx.appsearch.annotation.Document.StringProperty public abstract String? email;
-    property @androidx.appsearch.annotation.Document.StringProperty public abstract String? telephone;
+    property @androidx.appsearch.annotation.Document.StringProperty(name="telephone") public abstract String? telephoneNumber;
     field public static final androidx.appactions.builtintypes.types.Person.Companion Companion;
   }
 
   public static interface Person.Builder<Self extends androidx.appactions.builtintypes.types.Person.Builder<Self>> extends androidx.appactions.builtintypes.types.Thing.Builder<Self> {
     method public androidx.appactions.builtintypes.types.Person build();
     method public Self setEmail(String? text);
-    method public Self setTelephone(String? text);
+    method public Self setTelephoneNumber(String? text);
   }
 
   public static final class Person.Companion {
-    method public androidx.appactions.builtintypes.types.Person.Builder<?> Builder();
+    method @androidx.appsearch.annotation.Document.BuilderProducer public androidx.appactions.builtintypes.types.Person.Builder<?> Builder();
   }
 
-  @androidx.appsearch.annotation.Document(name="bit:Schedule") public interface Schedule extends androidx.appactions.builtintypes.types.Intangible {
-    method public static androidx.appactions.builtintypes.types.Schedule.Builder<?> Builder();
+  @androidx.appsearch.annotation.Document(name="bit:Schedule", parent={Intangible::class}) public interface Schedule extends androidx.appactions.builtintypes.types.Intangible {
+    method @androidx.appsearch.annotation.Document.BuilderProducer public static androidx.appactions.builtintypes.types.Schedule.Builder<?> Builder();
     method @androidx.appsearch.annotation.Document.DocumentProperty(name="byDay") public java.util.List<androidx.appactions.builtintypes.properties.ByDay> getByDays();
     method @androidx.appsearch.annotation.Document.LongProperty(name="byMonthDay") public java.util.List<java.lang.Long> getByMonthDays();
     method @androidx.appsearch.annotation.Document.LongProperty(name="byMonthWeek") public java.util.List<java.lang.Long> getByMonthWeeks();
@@ -904,11 +904,11 @@
   }
 
   public static final class Schedule.Companion {
-    method public androidx.appactions.builtintypes.types.Schedule.Builder<?> Builder();
+    method @androidx.appsearch.annotation.Document.BuilderProducer public androidx.appactions.builtintypes.types.Schedule.Builder<?> Builder();
   }
 
-  @androidx.appsearch.annotation.Document(name="bit:SuccessStatus") public interface SuccessStatus extends androidx.appactions.builtintypes.types.CommonExecutionStatus {
-    method public static androidx.appactions.builtintypes.types.SuccessStatus.Builder<?> Builder();
+  @androidx.appsearch.annotation.Document(name="bit:SuccessStatus", parent={CommonExecutionStatus::class}) public interface SuccessStatus extends androidx.appactions.builtintypes.types.CommonExecutionStatus {
+    method @androidx.appsearch.annotation.Document.BuilderProducer public static androidx.appactions.builtintypes.types.SuccessStatus.Builder<?> Builder();
     method public androidx.appactions.builtintypes.types.SuccessStatus.Builder<?> toBuilder();
     field public static final androidx.appactions.builtintypes.types.SuccessStatus.Companion Companion;
   }
@@ -918,11 +918,11 @@
   }
 
   public static final class SuccessStatus.Companion {
-    method public androidx.appactions.builtintypes.types.SuccessStatus.Builder<?> Builder();
+    method @androidx.appsearch.annotation.Document.BuilderProducer public androidx.appactions.builtintypes.types.SuccessStatus.Builder<?> Builder();
   }
 
   @androidx.appsearch.annotation.Document(name="bit:Thing") public interface Thing {
-    method public static androidx.appactions.builtintypes.types.Thing.Builder<?> Builder();
+    method @androidx.appsearch.annotation.Document.BuilderProducer public static androidx.appactions.builtintypes.types.Thing.Builder<?> Builder();
     method @androidx.appsearch.annotation.Document.DocumentProperty public androidx.appactions.builtintypes.properties.DisambiguatingDescription? getDisambiguatingDescription();
     method @androidx.appsearch.annotation.Document.Id public String? getIdentifier();
     method @androidx.appsearch.annotation.Document.DocumentProperty public androidx.appactions.builtintypes.properties.Name? getName();
@@ -946,11 +946,11 @@
   }
 
   public static final class Thing.Companion {
-    method public androidx.appactions.builtintypes.types.Thing.Builder<?> Builder();
+    method @androidx.appsearch.annotation.Document.BuilderProducer public androidx.appactions.builtintypes.types.Thing.Builder<?> Builder();
   }
 
-  @androidx.appsearch.annotation.Document(name="bit:Timer") public interface Timer extends androidx.appactions.builtintypes.types.Thing {
-    method public static androidx.appactions.builtintypes.types.Timer.Builder<?> Builder();
+  @androidx.appsearch.annotation.Document(name="bit:Timer", parent={Thing::class}) public interface Timer extends androidx.appactions.builtintypes.types.Thing {
+    method @androidx.appsearch.annotation.Document.BuilderProducer public static androidx.appactions.builtintypes.types.Timer.Builder<?> Builder();
     method public java.time.Duration? getDuration();
     method public androidx.appactions.builtintypes.types.Timer.Builder<?> toBuilder();
     property public abstract java.time.Duration? duration;
@@ -963,11 +963,11 @@
   }
 
   public static final class Timer.Companion {
-    method public androidx.appactions.builtintypes.types.Timer.Builder<?> Builder();
+    method @androidx.appsearch.annotation.Document.BuilderProducer public androidx.appactions.builtintypes.types.Timer.Builder<?> Builder();
   }
 
-  @androidx.appsearch.annotation.Document(name="bit:UnsupportedOperationStatus") public interface UnsupportedOperationStatus extends androidx.appactions.builtintypes.types.ExecutionStatus {
-    method public static androidx.appactions.builtintypes.types.UnsupportedOperationStatus.Builder<?> Builder();
+  @androidx.appsearch.annotation.Document(name="bit:UnsupportedOperationStatus", parent={ExecutionStatus::class}) public interface UnsupportedOperationStatus extends androidx.appactions.builtintypes.types.ExecutionStatus {
+    method @androidx.appsearch.annotation.Document.BuilderProducer public static androidx.appactions.builtintypes.types.UnsupportedOperationStatus.Builder<?> Builder();
     method public androidx.appactions.builtintypes.types.UnsupportedOperationStatus.Builder<?> toBuilder();
     field public static final androidx.appactions.builtintypes.types.UnsupportedOperationStatus.Companion Companion;
   }
@@ -977,7 +977,7 @@
   }
 
   public static final class UnsupportedOperationStatus.Companion {
-    method public androidx.appactions.builtintypes.types.UnsupportedOperationStatus.Builder<?> Builder();
+    method @androidx.appsearch.annotation.Document.BuilderProducer public androidx.appactions.builtintypes.types.UnsupportedOperationStatus.Builder<?> Builder();
   }
 
 }
diff --git a/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/Alarm.kt b/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/Alarm.kt
index 28f7bd0..1944b8c 100644
--- a/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/Alarm.kt
+++ b/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/Alarm.kt
@@ -39,7 +39,10 @@
  * Should not be directly implemented. More properties may be added over time. Instead consider
  * using [Companion.Builder] or see [AbstractAlarm] if you need to extend this type.
  */
-@Document(name = "bit:Alarm")
+@Document(
+  name = "bit:Alarm",
+  parent = [Thing::class],
+)
 public interface Alarm : Thing {
   /**
    * Associates an Alarm with a Schedule.
@@ -51,7 +54,9 @@
   /**
    * Specifies if the alarm enabled or disabled.
    *
-   * See http://schema.googleapis.com/isAlarmEnabled for more context.
+   * Should be left unset in contexts where there is no notion of enabled/disabled alarms.
+   *
+   * See https://schema.googleapis.com/isAlarmEnabled for more context.
    */
   @get:Document.BooleanProperty @get:Suppress("AutoBoxing") public val isAlarmEnabled: Boolean?
 
@@ -60,7 +65,7 @@
 
   public companion object {
     /** Returns a default implementation of [Builder] with no properties set. */
-    @JvmStatic public fun Builder(): Builder<*> = AlarmImpl.Builder()
+    @JvmStatic @Document.BuilderProducer public fun Builder(): Builder<*> = AlarmImpl.Builder()
   }
 
   /**
diff --git a/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/CommonExecutionStatus.kt b/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/CommonExecutionStatus.kt
index a2343f4..eb282c3 100644
--- a/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/CommonExecutionStatus.kt
+++ b/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/CommonExecutionStatus.kt
@@ -42,14 +42,19 @@
  * Should not be directly implemented. More properties may be added over time. Instead consider
  * using [Companion.Builder] or see [AbstractCommonExecutionStatus] if you need to extend this type.
  */
-@Document(name = "bit:CommonExecutionStatus")
+@Document(
+  name = "bit:CommonExecutionStatus",
+  parent = [ExecutionStatus::class],
+)
 public interface CommonExecutionStatus : ExecutionStatus {
   /** Converts this [CommonExecutionStatus] to its builder with all the properties copied over. */
   public override fun toBuilder(): Builder<*>
 
   public companion object {
     /** Returns a default implementation of [Builder] with no properties set. */
-    @JvmStatic public fun Builder(): Builder<*> = CommonExecutionStatusImpl.Builder()
+    @JvmStatic
+    @Document.BuilderProducer
+    public fun Builder(): Builder<*> = CommonExecutionStatusImpl.Builder()
   }
 
   /**
diff --git a/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/ExecutionStatus.kt b/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/ExecutionStatus.kt
index fc8431a..fb9d1ac 100644
--- a/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/ExecutionStatus.kt
+++ b/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/ExecutionStatus.kt
@@ -42,14 +42,19 @@
  * Should not be directly implemented. More properties may be added over time. Instead consider
  * using [Companion.Builder] or see [AbstractExecutionStatus] if you need to extend this type.
  */
-@Document(name = "bit:ExecutionStatus")
+@Document(
+  name = "bit:ExecutionStatus",
+  parent = [Intangible::class],
+)
 public interface ExecutionStatus : Intangible {
   /** Converts this [ExecutionStatus] to its builder with all the properties copied over. */
   public override fun toBuilder(): Builder<*>
 
   public companion object {
     /** Returns a default implementation of [Builder] with no properties set. */
-    @JvmStatic public fun Builder(): Builder<*> = ExecutionStatusImpl.Builder()
+    @JvmStatic
+    @Document.BuilderProducer
+    public fun Builder(): Builder<*> = ExecutionStatusImpl.Builder()
   }
 
   /**
diff --git a/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/GenericErrorStatus.kt b/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/GenericErrorStatus.kt
index 02047e6..22b215c 100644
--- a/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/GenericErrorStatus.kt
+++ b/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/GenericErrorStatus.kt
@@ -38,14 +38,19 @@
  * Should not be directly implemented. More properties may be added over time. Instead consider
  * using [Companion.Builder] or see [AbstractGenericErrorStatus] if you need to extend this type.
  */
-@Document(name = "bit:GenericErrorStatus")
+@Document(
+  name = "bit:GenericErrorStatus",
+  parent = [CommonExecutionStatus::class],
+)
 public interface GenericErrorStatus : CommonExecutionStatus {
   /** Converts this [GenericErrorStatus] to its builder with all the properties copied over. */
   public override fun toBuilder(): Builder<*>
 
   public companion object {
     /** Returns a default implementation of [Builder] with no properties set. */
-    @JvmStatic public fun Builder(): Builder<*> = GenericErrorStatusImpl.Builder()
+    @JvmStatic
+    @Document.BuilderProducer
+    public fun Builder(): Builder<*> = GenericErrorStatusImpl.Builder()
   }
 
   /**
diff --git a/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/Intangible.kt b/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/Intangible.kt
index 280714d..8fa8ef8 100644
--- a/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/Intangible.kt
+++ b/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/Intangible.kt
@@ -39,14 +39,17 @@
  * Should not be directly implemented. More properties may be added over time. Instead consider
  * using [Companion.Builder] or see [AbstractIntangible] if you need to extend this type.
  */
-@Document(name = "bit:Intangible")
+@Document(
+  name = "bit:Intangible",
+  parent = [Thing::class],
+)
 public interface Intangible : Thing {
   /** Converts this [Intangible] to its builder with all the properties copied over. */
   public override fun toBuilder(): Builder<*>
 
   public companion object {
     /** Returns a default implementation of [Builder] with no properties set. */
-    @JvmStatic public fun Builder(): Builder<*> = IntangibleImpl.Builder()
+    @JvmStatic @Document.BuilderProducer public fun Builder(): Builder<*> = IntangibleImpl.Builder()
   }
 
   /**
diff --git a/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/ObjectCreationLimitReachedStatus.kt b/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/ObjectCreationLimitReachedStatus.kt
index c677643..5e22417 100644
--- a/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/ObjectCreationLimitReachedStatus.kt
+++ b/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/ObjectCreationLimitReachedStatus.kt
@@ -40,7 +40,10 @@
  * using [Companion.Builder] or see [AbstractObjectCreationLimitReachedStatus] if you need to extend
  * this type.
  */
-@Document(name = "bit:ObjectCreationLimitReachedStatus")
+@Document(
+  name = "bit:ObjectCreationLimitReachedStatus",
+  parent = [ExecutionStatus::class],
+)
 public interface ObjectCreationLimitReachedStatus : ExecutionStatus {
   /**
    * Converts this [ObjectCreationLimitReachedStatus] to its builder with all the properties copied
@@ -50,7 +53,9 @@
 
   public companion object {
     /** Returns a default implementation of [Builder] with no properties set. */
-    @JvmStatic public fun Builder(): Builder<*> = ObjectCreationLimitReachedStatusImpl.Builder()
+    @JvmStatic
+    @Document.BuilderProducer
+    public fun Builder(): Builder<*> = ObjectCreationLimitReachedStatusImpl.Builder()
   }
 
   /**
diff --git a/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/Person.kt b/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/Person.kt
index 39c3f36..694deab 100644
--- a/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/Person.kt
+++ b/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/Person.kt
@@ -38,7 +38,10 @@
  * Should not be directly implemented. More properties may be added over time. Instead consider
  * using [Companion.Builder] or see [AbstractPerson] if you need to extend this type.
  */
-@Document(name = "bit:Person")
+@Document(
+  name = "bit:Person",
+  parent = [Thing::class],
+)
 public interface Person : Thing {
   /**
    * Email address.
@@ -50,16 +53,16 @@
   /**
    * The telephone number.
    *
-   * See http://schema.org/telephone for more context.
+   * See https://schema.org/telephone for more context.
    */
-  @get:Document.StringProperty public val telephone: String?
+  @get:Document.StringProperty(name = "telephone") public val telephoneNumber: String?
 
   /** Converts this [Person] to its builder with all the properties copied over. */
   public override fun toBuilder(): Builder<*>
 
   public companion object {
     /** Returns a default implementation of [Builder] with no properties set. */
-    @JvmStatic public fun Builder(): Builder<*> = PersonImpl.Builder()
+    @JvmStatic @Document.BuilderProducer public fun Builder(): Builder<*> = PersonImpl.Builder()
   }
 
   /**
@@ -75,8 +78,8 @@
     /** Sets the `email`. */
     public fun setEmail(text: String?): Self
 
-    /** Sets the `telephone`. */
-    public fun setTelephone(text: String?): Self
+    /** Sets the `telephoneNumber`. */
+    public fun setTelephoneNumber(text: String?): Self
   }
 }
 
@@ -121,7 +124,7 @@
 internal constructor(
   public final override val namespace: String?,
   public final override val email: String?,
-  public final override val telephone: String?,
+  public final override val telephoneNumber: String?,
   public final override val disambiguatingDescription: DisambiguatingDescription?,
   public final override val identifier: String?,
   public final override val name: Name?,
@@ -146,7 +149,7 @@
   ) : this(
     person.namespace,
     person.email,
-    person.telephone,
+    person.telephoneNumber,
     person.disambiguatingDescription,
     person.identifier,
     person.name
@@ -159,7 +162,7 @@
     toBuilderWithAdditionalPropertiesOnly()
       .setNamespace(namespace)
       .setEmail(email)
-      .setTelephone(telephone)
+      .setTelephoneNumber(telephoneNumber)
       .setDisambiguatingDescription(disambiguatingDescription)
       .setIdentifier(identifier)
       .setName(name)
@@ -170,7 +173,7 @@
     other as Self
     if (namespace != other.namespace) return false
     if (email != other.email) return false
-    if (telephone != other.telephone) return false
+    if (telephoneNumber != other.telephoneNumber) return false
     if (disambiguatingDescription != other.disambiguatingDescription) return false
     if (identifier != other.identifier) return false
     if (name != other.name) return false
@@ -182,7 +185,7 @@
     Objects.hash(
       namespace,
       email,
-      telephone,
+      telephoneNumber,
       disambiguatingDescription,
       identifier,
       name,
@@ -197,8 +200,8 @@
     if (email != null) {
       attributes["email"] = email
     }
-    if (telephone != null) {
-      attributes["telephone"] = telephone
+    if (telephoneNumber != null) {
+      attributes["telephoneNumber"] = telephoneNumber
     }
     if (disambiguatingDescription != null) {
       attributes["disambiguatingDescription"] =
@@ -287,7 +290,7 @@
 
     private var email: String? = null
 
-    private var telephone: String? = null
+    private var telephoneNumber: String? = null
 
     private var disambiguatingDescription: DisambiguatingDescription? = null
 
@@ -307,7 +310,7 @@
 
     public final override fun build(): Built =
       buildFromPerson(
-        PersonImpl(namespace, email, telephone, disambiguatingDescription, identifier, name)
+        PersonImpl(namespace, email, telephoneNumber, disambiguatingDescription, identifier, name)
       )
 
     public final override fun setNamespace(namespace: String?): Self {
@@ -320,8 +323,8 @@
       return this as Self
     }
 
-    public final override fun setTelephone(text: String?): Self {
-      this.telephone = text
+    public final override fun setTelephoneNumber(text: String?): Self {
+      this.telephoneNumber = text
       return this as Self
     }
 
@@ -349,7 +352,7 @@
       other as Self
       if (namespace != other.namespace) return false
       if (email != other.email) return false
-      if (telephone != other.telephone) return false
+      if (telephoneNumber != other.telephoneNumber) return false
       if (disambiguatingDescription != other.disambiguatingDescription) return false
       if (identifier != other.identifier) return false
       if (name != other.name) return false
@@ -362,7 +365,7 @@
       Objects.hash(
         namespace,
         email,
-        telephone,
+        telephoneNumber,
         disambiguatingDescription,
         identifier,
         name,
@@ -378,8 +381,8 @@
       if (email != null) {
         attributes["email"] = email!!
       }
-      if (telephone != null) {
-        attributes["telephone"] = telephone!!
+      if (telephoneNumber != null) {
+        attributes["telephoneNumber"] = telephoneNumber!!
       }
       if (disambiguatingDescription != null) {
         attributes["disambiguatingDescription"] =
@@ -409,11 +412,11 @@
   public constructor(
     namespace: String?,
     email: String?,
-    telephone: String?,
+    telephoneNumber: String?,
     disambiguatingDescription: DisambiguatingDescription?,
     identifier: String?,
     name: Name?,
-  ) : super(namespace, email, telephone, disambiguatingDescription, identifier, name)
+  ) : super(namespace, email, telephoneNumber, disambiguatingDescription, identifier, name)
 
   public constructor(person: Person) : super(person)
 
diff --git a/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/Schedule.kt b/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/Schedule.kt
index 5daa277..3cbe575 100644
--- a/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/Schedule.kt
+++ b/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/Schedule.kt
@@ -62,7 +62,10 @@
  * Should not be directly implemented. More properties may be added over time. Instead consider
  * using [Companion.Builder] or see [AbstractSchedule] if you need to extend this type.
  */
-@Document(name = "bit:Schedule")
+@Document(
+  name = "bit:Schedule",
+  parent = [Intangible::class],
+)
 public interface Schedule : Intangible {
   /**
    * Defines the day(s) of the week on which a recurring Event takes place.
@@ -174,7 +177,7 @@
 
   public companion object {
     /** Returns a default implementation of [Builder] with no properties set. */
-    @JvmStatic public fun Builder(): Builder<*> = ScheduleImpl.Builder()
+    @JvmStatic @Document.BuilderProducer public fun Builder(): Builder<*> = ScheduleImpl.Builder()
   }
 
   /**
diff --git a/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/SuccessStatus.kt b/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/SuccessStatus.kt
index 8518775..55f320e 100644
--- a/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/SuccessStatus.kt
+++ b/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/SuccessStatus.kt
@@ -38,14 +38,19 @@
  * Should not be directly implemented. More properties may be added over time. Instead consider
  * using [Companion.Builder] or see [AbstractSuccessStatus] if you need to extend this type.
  */
-@Document(name = "bit:SuccessStatus")
+@Document(
+  name = "bit:SuccessStatus",
+  parent = [CommonExecutionStatus::class],
+)
 public interface SuccessStatus : CommonExecutionStatus {
   /** Converts this [SuccessStatus] to its builder with all the properties copied over. */
   public override fun toBuilder(): Builder<*>
 
   public companion object {
     /** Returns a default implementation of [Builder] with no properties set. */
-    @JvmStatic public fun Builder(): Builder<*> = SuccessStatusImpl.Builder()
+    @JvmStatic
+    @Document.BuilderProducer
+    public fun Builder(): Builder<*> = SuccessStatusImpl.Builder()
   }
 
   /**
diff --git a/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/Thing.kt b/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/Thing.kt
index d359e77..65384bc 100644
--- a/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/Thing.kt
+++ b/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/Thing.kt
@@ -72,7 +72,7 @@
 
   public companion object {
     /** Returns a default implementation of [Builder] with no properties set. */
-    @JvmStatic public fun Builder(): Builder<*> = ThingImpl.Builder()
+    @JvmStatic @Document.BuilderProducer public fun Builder(): Builder<*> = ThingImpl.Builder()
   }
 
   /**
diff --git a/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/Timer.kt b/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/Timer.kt
index ecc155b..3966022 100644
--- a/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/Timer.kt
+++ b/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/Timer.kt
@@ -39,7 +39,10 @@
  * Should not be directly implemented. More properties may be added over time. Instead consider
  * using [Companion.Builder] or see [AbstractTimer] if you need to extend this type.
  */
-@Document(name = "bit:Timer")
+@Document(
+  name = "bit:Timer",
+  parent = [Thing::class],
+)
 public interface Timer : Thing {
   /**
    * The duration of the item (movie, audio recording, event, etc.).
@@ -53,7 +56,7 @@
 
   public companion object {
     /** Returns a default implementation of [Builder] with no properties set. */
-    @JvmStatic public fun Builder(): Builder<*> = TimerImpl.Builder()
+    @JvmStatic @Document.BuilderProducer public fun Builder(): Builder<*> = TimerImpl.Builder()
   }
 
   /**
diff --git a/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/UnsupportedOperationStatus.kt b/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/UnsupportedOperationStatus.kt
index 62d57ca..2d907cd 100644
--- a/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/UnsupportedOperationStatus.kt
+++ b/appactions/builtintypes/builtintypes/src/main/java/androidx/appactions/builtintypes/types/UnsupportedOperationStatus.kt
@@ -39,7 +39,10 @@
  * using [Companion.Builder] or see [AbstractUnsupportedOperationStatus] if you need to extend this
  * type.
  */
-@Document(name = "bit:UnsupportedOperationStatus")
+@Document(
+  name = "bit:UnsupportedOperationStatus",
+  parent = [ExecutionStatus::class],
+)
 public interface UnsupportedOperationStatus : ExecutionStatus {
   /**
    * Converts this [UnsupportedOperationStatus] to its builder with all the properties copied over.
@@ -48,7 +51,9 @@
 
   public companion object {
     /** Returns a default implementation of [Builder] with no properties set. */
-    @JvmStatic public fun Builder(): Builder<*> = UnsupportedOperationStatusImpl.Builder()
+    @JvmStatic
+    @Document.BuilderProducer
+    public fun Builder(): Builder<*> = UnsupportedOperationStatusImpl.Builder()
   }
 
   /**
diff --git a/appcompat/appcompat/build.gradle b/appcompat/appcompat/build.gradle
index 8a29a14..c6bedc2f 100644
--- a/appcompat/appcompat/build.gradle
+++ b/appcompat/appcompat/build.gradle
@@ -107,8 +107,4 @@
     description = "Provides backwards-compatible implementations of UI-related Android SDK " +
             "functionality, including dark mode and Material theming."
     failOnDeprecationWarnings = false
-    deviceTests {
-        // Temporarily disabled due to b/286161632
-        enabled = false
-    }
 }
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/SystemLocalesMaintainedOnAppLanguageChangeTestCase.kt b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/SystemLocalesMaintainedOnAppLanguageChangeTestCase.kt
index f439860..77bede1 100644
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/SystemLocalesMaintainedOnAppLanguageChangeTestCase.kt
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/SystemLocalesMaintainedOnAppLanguageChangeTestCase.kt
@@ -29,6 +29,7 @@
 import junit.framework.Assert.assertEquals
 import org.junit.After
 import org.junit.Before
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -49,12 +50,14 @@
         )
     }
 
+    @Ignore
     @Test
     fun testGetSystemLocales_noAppLocalesSet_systemLocalesSameAsExpectedSystemLocales() {
         val context = InstrumentationRegistry.getInstrumentation().context
         assertEquals(expectedSystemLocales, LocaleManagerCompat.getSystemLocales(context))
     }
 
+    @Ignore
     @Test
     fun testGetSystemLocales_afterAppLocalesSet_systemLocalesSameAsExpectedSystemLocales() {
         LocalesUtils.setLocalesAndWaitForRecreate(rule.activity, LocalesUtils.CUSTOM_LOCALE_LIST)
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatTextViewFontScalingTest.kt b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatTextViewFontScalingTest.kt
index 4b3c8ecd..a87d598 100644
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatTextViewFontScalingTest.kt
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/widget/AppCompatTextViewFontScalingTest.kt
@@ -17,6 +17,7 @@
 package androidx.appcompat.widget
 
 import android.app.Activity
+import android.os.Build
 import android.os.Bundle
 import android.util.TypedValue
 import android.widget.TextView
@@ -43,7 +44,14 @@
 
     @After
     fun teardown() {
-        resetSystemFontScale(scenarioRule.scenario)
+        // Have to manually check the version here because if we try to rely on the assumeTrue() in
+        // resetSystemFontScale(), it is called twice (again in setSystemFontScale()) and the test
+        // fails with a "TestCouldNotBeSkippedException: Test could not be skipped due to other
+        // failures" because it thinks the second assumeTrue() was a separate error.
+        // tl;dr avoids a bug in jUnit when multiple assumeTrue()s happen in @Test and @After
+        if (Build.VERSION.SDK_INT >= 29) {
+            resetSystemFontScale(scenarioRule.scenario)
+        }
     }
 
     @Test
@@ -66,27 +74,6 @@
 
     @Test
     @Throws(Throwable::class)
-    fun testNonLinearFontScaling_testSetLineHeightSpFirstAndSetTextSizeSpAfter() {
-        setSystemFontScale(2f, scenarioRule.scenario)
-        scenarioRule.scenario.onActivity { activity ->
-            assertThat(activity.resources.configuration.fontScale).isWithin(0.02f).of(2f)
-
-            val textView = AppCompatTextView(activity)
-            val textSizeSp = 20f
-            val lineHeightSp = 40f
-
-            textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSizeSp)
-            textView.setLineHeight(TypedValue.COMPLEX_UNIT_SP, lineHeightSp)
-
-            val newTextSizeSp = 10f
-            textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, newTextSizeSp)
-
-            verifyLineHeightIsIntendedProportions(lineHeightSp, newTextSizeSp, activity, textView)
-        }
-    }
-
-    @Test
-    @Throws(Throwable::class)
     fun testNonLinearFontScaling_overwriteXml_testSetLineHeightSpAndSetTextSizeSp() {
         setSystemFontScale(2f, scenarioRule.scenario)
         scenarioRule.scenario.onActivity { activity ->
@@ -120,23 +107,6 @@
 
     @Test
     @Throws(Throwable::class)
-    fun testNonLinearFontScaling_overwriteXml_testLineHeightAttrSpAndSetTextSizeSpAfter() {
-        setSystemFontScale(2f, scenarioRule.scenario)
-        scenarioRule.scenario.onActivity { activity ->
-            assertThat(activity.resources.configuration.fontScale).isWithin(0.02f).of(2f)
-
-            val textView = findTextView(activity, R.id.textview_lineheight2x)
-            val newTextSizeSp = 10f
-            val lineHeightSp = 40f
-
-            textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, newTextSizeSp)
-
-            verifyLineHeightIsIntendedProportions(lineHeightSp, newTextSizeSp, activity, textView)
-        }
-    }
-
-    @Test
-    @Throws(Throwable::class)
     fun testNonLinearFontScaling_dimenXml_testLineHeightAttrSpAndTextSizeAttrSp() {
         setSystemFontScale(2f, scenarioRule.scenario)
         scenarioRule.scenario.onActivity { activity ->
@@ -224,4 +194,4 @@
                 .of(expectedLineHeightPx)
         }
     }
-}
\ No newline at end of file
+}
diff --git a/appsearch/appsearch-builtin-types/api/current.txt b/appsearch/appsearch-builtin-types/api/current.txt
index 1e295d6..ceffd8c 100644
--- a/appsearch/appsearch-builtin-types/api/current.txt
+++ b/appsearch/appsearch-builtin-types/api/current.txt
@@ -15,6 +15,7 @@
   @androidx.appsearch.annotation.Document(name="builtin:Alarm") public class Alarm extends androidx.appsearch.builtintypes.Thing {
     method public String? getBlackoutPeriodEndDate();
     method public String? getBlackoutPeriodStartDate();
+    method public int getComputingDevice();
     method public int[]? getDaysOfWeek();
     method @IntRange(from=0, to=23) public int getHour();
     method @IntRange(from=0, to=59) public int getMinute();
@@ -23,6 +24,9 @@
     method public String? getRingtone();
     method public boolean isEnabled();
     method public boolean shouldVibrate();
+    field public static final int COMPUTING_DEVICE_SMART_PHONE = 1; // 0x1
+    field public static final int COMPUTING_DEVICE_SMART_WATCH = 2; // 0x2
+    field public static final int COMPUTING_DEVICE_UNKNOWN = 0; // 0x0
   }
 
   public static final class Alarm.Builder {
@@ -35,6 +39,7 @@
     method public androidx.appsearch.builtintypes.Alarm.Builder clearPotentialActions();
     method public androidx.appsearch.builtintypes.Alarm.Builder setBlackoutPeriodEndDate(String?);
     method public androidx.appsearch.builtintypes.Alarm.Builder setBlackoutPeriodStartDate(String?);
+    method public androidx.appsearch.builtintypes.Alarm.Builder setComputingDevice(int);
     method public androidx.appsearch.builtintypes.Alarm.Builder setCreationTimestampMillis(long);
     method public androidx.appsearch.builtintypes.Alarm.Builder setDaysOfWeek(@IntRange(from=java.util.Calendar.SUNDAY, to=java.util.Calendar.SATURDAY) int...);
     method public androidx.appsearch.builtintypes.Alarm.Builder setDescription(String?);
diff --git a/appsearch/appsearch-builtin-types/api/restricted_current.txt b/appsearch/appsearch-builtin-types/api/restricted_current.txt
index e402ed8c..4f35050 100644
--- a/appsearch/appsearch-builtin-types/api/restricted_current.txt
+++ b/appsearch/appsearch-builtin-types/api/restricted_current.txt
@@ -17,6 +17,7 @@
   @androidx.appsearch.annotation.Document(name="builtin:Alarm") public class Alarm extends androidx.appsearch.builtintypes.Thing {
     method public String? getBlackoutPeriodEndDate();
     method public String? getBlackoutPeriodStartDate();
+    method public int getComputingDevice();
     method public int[]? getDaysOfWeek();
     method @IntRange(from=0, to=23) public int getHour();
     method @IntRange(from=0, to=59) public int getMinute();
@@ -25,6 +26,9 @@
     method public String? getRingtone();
     method public boolean isEnabled();
     method public boolean shouldVibrate();
+    field public static final int COMPUTING_DEVICE_SMART_PHONE = 1; // 0x1
+    field public static final int COMPUTING_DEVICE_SMART_WATCH = 2; // 0x2
+    field public static final int COMPUTING_DEVICE_UNKNOWN = 0; // 0x0
   }
 
   public static final class Alarm.Builder {
@@ -37,6 +41,7 @@
     method public androidx.appsearch.builtintypes.Alarm.Builder clearPotentialActions();
     method public androidx.appsearch.builtintypes.Alarm.Builder setBlackoutPeriodEndDate(String?);
     method public androidx.appsearch.builtintypes.Alarm.Builder setBlackoutPeriodStartDate(String?);
+    method public androidx.appsearch.builtintypes.Alarm.Builder setComputingDevice(int);
     method public androidx.appsearch.builtintypes.Alarm.Builder setCreationTimestampMillis(long);
     method public androidx.appsearch.builtintypes.Alarm.Builder setDaysOfWeek(@IntRange(from=java.util.Calendar.SUNDAY, to=java.util.Calendar.SATURDAY) int...);
     method public androidx.appsearch.builtintypes.Alarm.Builder setDescription(String?);
diff --git a/appsearch/appsearch-builtin-types/src/androidTest/java/androidx/appsearch/builtintypes/AlarmTest.java b/appsearch/appsearch-builtin-types/src/androidTest/java/androidx/appsearch/builtintypes/AlarmTest.java
index 4747d51..5becd28 100644
--- a/appsearch/appsearch-builtin-types/src/androidTest/java/androidx/appsearch/builtintypes/AlarmTest.java
+++ b/appsearch/appsearch-builtin-types/src/androidTest/java/androidx/appsearch/builtintypes/AlarmTest.java
@@ -57,6 +57,7 @@
                 .setShouldVibrate(true)
                 .setPreviousInstance(alarmInstance1)
                 .setNextInstance(alarmInstance2)
+                .setComputingDevice(Alarm.COMPUTING_DEVICE_SMART_WATCH)
                 .build();
 
         assertThat(alarm.getNamespace()).isEqualTo("namespace");
@@ -82,6 +83,7 @@
         assertThat(alarm.shouldVibrate()).isTrue();
         assertThat(alarm.getPreviousInstance()).isEqualTo(alarmInstance1);
         assertThat(alarm.getNextInstance()).isEqualTo(alarmInstance2);
+        assertThat(alarm.getComputingDevice()).isEqualTo(Alarm.COMPUTING_DEVICE_SMART_WATCH);
     }
 
     @Test
@@ -113,6 +115,7 @@
                 .setShouldVibrate(true)
                 .setPreviousInstance(alarmInstance1)
                 .setNextInstance(alarmInstance2)
+                .setComputingDevice(Alarm.COMPUTING_DEVICE_SMART_WATCH)
                 .build();
 
         Alarm alarm2 = new Alarm.Builder(alarm1).build();
@@ -140,6 +143,7 @@
         assertThat(alarm1.shouldVibrate()).isEqualTo(alarm2.shouldVibrate());
         assertThat(alarm1.getPreviousInstance()).isEqualTo(alarm2.getPreviousInstance());
         assertThat(alarm1.getNextInstance()).isEqualTo(alarm2.getNextInstance());
+        assertThat(alarm1.getComputingDevice()).isEqualTo(alarm2.getComputingDevice());
     }
 
     @Test
@@ -173,6 +177,7 @@
                 .setShouldVibrate(true)
                 .setPreviousInstance(alarmInstance1)
                 .setNextInstance(alarmInstance2)
+                .setComputingDevice(Alarm.COMPUTING_DEVICE_SMART_WATCH)
                 .build();
 
         GenericDocument genericDocument = GenericDocument.fromDocumentClass(alarm);
@@ -207,6 +212,8 @@
                 .isEqualTo(GenericDocument.fromDocumentClass(alarmInstance1));
         assertThat(genericDocument.getPropertyDocument("nextInstance"))
                 .isEqualTo(GenericDocument.fromDocumentClass(alarmInstance2));
+        assertThat(genericDocument.getPropertyLong("computingDevice"))
+                .isEqualTo(Alarm.COMPUTING_DEVICE_SMART_WATCH);
     }
 
     @Test
diff --git a/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/Alarm.java b/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/Alarm.java
index 4c4a2bb..56336cf 100644
--- a/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/Alarm.java
+++ b/appsearch/appsearch-builtin-types/src/main/java/androidx/appsearch/builtintypes/Alarm.java
@@ -16,13 +16,17 @@
 
 package androidx.appsearch.builtintypes;
 
+import androidx.annotation.IntDef;
 import androidx.annotation.IntRange;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
 import androidx.appsearch.annotation.Document;
 import androidx.appsearch.utils.DateTimeFormatValidator;
 import androidx.core.util.Preconditions;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Calendar;
 import java.util.List;
 
@@ -31,6 +35,18 @@
  */
 @Document(name = "builtin:Alarm")
 public class Alarm extends Thing {
+    /** The device that this {@link Alarm} belongs to. */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @IntDef({COMPUTING_DEVICE_UNKNOWN, COMPUTING_DEVICE_SMART_PHONE, COMPUTING_DEVICE_SMART_WATCH})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ComputingDevice {}
+    /** The {@link Alarm} belongs to an unknown device. */
+    public static final int COMPUTING_DEVICE_UNKNOWN = 0;
+    /** The {@link Alarm} belongs to a smart phone device. */
+    public static final int COMPUTING_DEVICE_SMART_PHONE = 1;
+    /** The {@link Alarm} belongs to a smart watch device. */
+    public static final int COMPUTING_DEVICE_SMART_WATCH = 2;
+
     @Document.BooleanProperty
     private final boolean mEnabled;
 
@@ -61,6 +77,9 @@
     @Document.DocumentProperty
     private final AlarmInstance mNextInstance;
 
+    @Document.LongProperty
+    private final int mComputingDevice;
+
     Alarm(@NonNull String namespace, @NonNull String id, int documentScore,
             long creationTimestampMillis, long documentTtlMillis, @Nullable String name,
             @Nullable List<String> alternateNames, @Nullable String description,
@@ -69,7 +88,8 @@
             boolean enabled, @Nullable int[] daysOfWeek, int hour, int minute,
             @Nullable String blackoutPeriodStartDate, @Nullable String blackoutPeriodEndDate,
             @Nullable String ringtone, boolean shouldVibrate,
-            @Nullable AlarmInstance previousInstance, @Nullable AlarmInstance nextInstance) {
+            @Nullable AlarmInstance previousInstance, @Nullable AlarmInstance nextInstance,
+            int computingDevice) {
         super(namespace, id, documentScore, creationTimestampMillis, documentTtlMillis, name,
                 alternateNames, description, image, url, potentialActions);
         mEnabled = enabled;
@@ -82,6 +102,7 @@
         mShouldVibrate = shouldVibrate;
         mPreviousInstance = previousInstance;
         mNextInstance = nextInstance;
+        mComputingDevice = computingDevice;
     }
 
     /** Returns whether or not the {@link Alarm} is active. */
@@ -196,6 +217,12 @@
         return mNextInstance;
     }
 
+    /** Returns the {@link ComputingDevice} this alarm belongs to. */
+    @ComputingDevice
+    public int getComputingDevice() {
+        return mComputingDevice;
+    }
+
     /** Builder for {@link Alarm}. */
     public static final class Builder extends BuilderImpl<Builder> {
         /**
@@ -229,6 +256,7 @@
         protected boolean mShouldVibrate;
         protected AlarmInstance mPreviousInstance;
         protected AlarmInstance mNextInstance;
+        protected int mComputingDevice;
 
         BuilderImpl(@NonNull String namespace, @NonNull String id) {
             super(namespace, id);
@@ -246,6 +274,7 @@
             mShouldVibrate = alarm.shouldVibrate();
             mPreviousInstance = alarm.getPreviousInstance();
             mNextInstance = alarm.getNextInstance();
+            mComputingDevice = alarm.getComputingDevice();
         }
 
         /** Sets whether or not the {@link Alarm} is active. */
@@ -391,6 +420,13 @@
             return (T) this;
         }
 
+        /** Sets the {@link ComputingDevice} this alarm belongs to. */
+        @NonNull
+        public T setComputingDevice(@ComputingDevice int computingDevice) {
+            mComputingDevice = computingDevice;
+            return (T) this;
+        }
+
         /** Builds the {@link Alarm}. */
         @NonNull
         @Override
@@ -400,7 +436,7 @@
                     mPotentialActions,
                     mEnabled, mDaysOfWeek, mHour, mMinute, mBlackoutPeriodStartDate,
                     mBlackoutPeriodEndDate, mRingtone, mShouldVibrate, mPreviousInstance,
-                    mNextInstance);
+                    mNextInstance, mComputingDevice);
         }
     }
 }
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java
index 9851266..c46429c 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java
@@ -719,6 +719,177 @@
     }
 
     @Test
+    public void testGlobalQuery_withJoin_packageFilter() throws Exception {
+        // Create a new mAppSearchImpl with a mock Visibility Checker
+        mAppSearchImpl.close();
+        File tempFolder = mTemporaryFolder.newFolder();
+        // We need to share across packages
+        VisibilityChecker mockVisibilityChecker =
+                (callerAccess, packageName, prefixedSchema, visibilityStore) -> true;
+        mAppSearchImpl = AppSearchImpl.create(
+                tempFolder,
+                new UnlimitedLimitConfig(),
+                new DefaultIcingOptionsConfig(),
+                /*initStatsBuilder=*/ null,
+                ALWAYS_OPTIMIZE,
+                mockVisibilityChecker);
+
+        // Insert package1 schema
+        List<AppSearchSchema> personSchema =
+                ImmutableList.of(new AppSearchSchema.Builder("personSchema").build());
+        InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
+                "package1",
+                "database1",
+                personSchema,
+                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*forceOverride=*/ false,
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
+        assertThat(internalSetSchemaResponse.isSuccess()).isTrue();
+
+        AppSearchSchema.StringPropertyConfig personField =
+                new AppSearchSchema.StringPropertyConfig.Builder("personId")
+                        .setJoinableValueType(AppSearchSchema.StringPropertyConfig
+                                .JOINABLE_VALUE_TYPE_QUALIFIED_ID).build();
+        // Insert package2 schema
+        List<AppSearchSchema> callSchema =
+                ImmutableList.of(new AppSearchSchema.Builder("callSchema")
+                        .addProperty(personField).build());
+        internalSetSchemaResponse = mAppSearchImpl.setSchema(
+                "package2",
+                "database2",
+                callSchema,
+                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*forceOverride=*/ true,
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
+        assertThat(internalSetSchemaResponse.isSuccess()).isTrue();
+
+        List<AppSearchSchema> textSchema =
+                ImmutableList.of(new AppSearchSchema.Builder("textSchema")
+                        .addProperty(personField).build());
+        internalSetSchemaResponse = mAppSearchImpl.setSchema(
+                "package3",
+                "database3",
+                textSchema,
+                /*visibilityDocuments=*/ Collections.emptyList(),
+                /*forceOverride=*/ true,
+                /*version=*/ 0,
+                /* setSchemaStatsBuilder= */ null);
+        assertThat(internalSetSchemaResponse.isSuccess()).isTrue();
+
+        // Insert package1 document
+        GenericDocument person = new GenericDocument.Builder<>("namespace", "id",
+                "personSchema")
+                .build();
+        mAppSearchImpl.putDocument(
+                "package1",
+                "database1",
+                person,
+                /*sendChangeNotifications=*/ false,
+                /*logger=*/ null);
+
+        // Insert package2 document
+        GenericDocument call =
+                new GenericDocument.Builder<>("namespace", "id", "callSchema")
+                        .setPropertyString("personId", "package1$database1/namespace#id").build();
+        mAppSearchImpl.putDocument(
+                "package2",
+                "database2",
+                call,
+                /*sendChangeNotifications=*/ false,
+                /*logger=*/ null);
+
+        // Insert package3 document
+        GenericDocument text =
+                new GenericDocument.Builder<>("namespace", "id", "textSchema")
+                        .setPropertyString("personId", "package1$database1/namespace#id").build();
+        mAppSearchImpl.putDocument(
+                "package3",
+                "database3",
+                text,
+                /*sendChangeNotifications=*/ false,
+                /*logger=*/ null);
+
+        // Filter on parent spec only
+        SearchSpec nested = new SearchSpec.Builder()
+                .setRankingStrategy(SearchSpec.RANKING_STRATEGY_CREATION_TIMESTAMP)
+                .setOrder(SearchSpec.ORDER_ASCENDING)
+                .build();
+        JoinSpec join = new JoinSpec.Builder("personId").setNestedSearch("", nested).build();
+
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
+                .addFilterPackageNames("package1")
+                .setJoinSpec(join)
+                .build();
+        SearchResultPage searchResultPage = mAppSearchImpl.globalQuery("", searchSpec,
+                new CallerAccess("package1"),
+                /*logger=*/ null);
+        assertThat(searchResultPage.getResults()).hasSize(1);
+        assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(person);
+        SearchResult result = searchResultPage.getResults().get(0);
+        assertThat(result.getJoinedResults()).hasSize(2);
+
+        // Filter on neither
+        searchSpec = new SearchSpec.Builder()
+                .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
+                .setRankingStrategy(SearchSpec.RANKING_STRATEGY_CREATION_TIMESTAMP)
+                .setOrder(SearchSpec.ORDER_ASCENDING)
+                .setJoinSpec(join)
+                .build();
+        searchResultPage = mAppSearchImpl.globalQuery("", searchSpec,
+                new CallerAccess("package1"),
+                /*logger=*/ null);
+        assertThat(searchResultPage.getResults()).hasSize(3);
+        assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(person);
+        assertThat(searchResultPage.getResults().get(1).getGenericDocument()).isEqualTo(call);
+        assertThat(searchResultPage.getResults().get(2).getGenericDocument()).isEqualTo(text);
+        result = searchResultPage.getResults().get(0);
+        assertThat(result.getJoinedResults()).hasSize(2);
+
+        // Filter on child spec only
+        nested = new SearchSpec.Builder()
+                .addFilterPackageNames("package2")
+                .build();
+        join = new JoinSpec.Builder("personId")
+                .setNestedSearch("", nested)
+                .build();
+
+        searchSpec = new SearchSpec.Builder()
+                .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
+                .setRankingStrategy(SearchSpec.RANKING_STRATEGY_CREATION_TIMESTAMP)
+                .setOrder(SearchSpec.ORDER_ASCENDING)
+                .setJoinSpec(join)
+                .build();
+        searchResultPage = mAppSearchImpl.globalQuery("", searchSpec,
+                new CallerAccess("package1"),
+                /*logger=*/ null);
+        assertThat(searchResultPage.getResults()).hasSize(3);
+        assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(person);
+        assertThat(searchResultPage.getResults().get(1).getGenericDocument()).isEqualTo(call);
+        assertThat(searchResultPage.getResults().get(2).getGenericDocument()).isEqualTo(text);
+        result = searchResultPage.getResults().get(0);
+        assertThat(result.getJoinedResults()).hasSize(1);
+        assertThat(result.getJoinedResults().get(0).getGenericDocument()).isEqualTo(call);
+
+        // Filter on both
+        searchSpec = new SearchSpec.Builder()
+                .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
+                .addFilterPackageNames("package1")
+                .setJoinSpec(join)
+                .build();
+        searchResultPage = mAppSearchImpl.globalQuery("", searchSpec,
+                new CallerAccess("package1"),
+                /*logger=*/ null);
+        assertThat(searchResultPage.getResults()).hasSize(1);
+        assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(person);
+        result = searchResultPage.getResults().get(0);
+        assertThat(result.getJoinedResults()).hasSize(1);
+        assertThat(result.getJoinedResults().get(0).getGenericDocument()).isEqualTo(call);
+    }
+
+    @Test
     public void testQueryInvalidPackages_withJoin() throws Exception {
         // Make sure that local queries with joinspecs including package filters don't access
         // other packages.
@@ -747,8 +918,8 @@
         // Insert package1 schema
         List<AppSearchSchema> personAndCallSchema =
                 ImmutableList.of(new AppSearchSchema.Builder("personSchema").build(),
-                new AppSearchSchema.Builder("callSchema")
-                .addProperty(personField).build());
+                        new AppSearchSchema.Builder("callSchema")
+                                .addProperty(personField).build());
         InternalSetSchemaResponse internalSetSchemaResponse = mAppSearchImpl.setSchema(
                 "package1",
                 "database1",
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SchemaToProtoConverterTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SchemaToProtoConverterTest.java
index 5c98d3e..92a87de 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SchemaToProtoConverterTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SchemaToProtoConverterTest.java
@@ -128,6 +128,8 @@
                         .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
                         .setJoinableValueType(AppSearchSchema.StringPropertyConfig
                                 .JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+                        // TODO(b/274157614): Export this to framework when we can access hidden
+                        //  APIs.
                         // @exportToFramework:startStrip()
                         // TODO(b/274157614) start exporting this when it is unhidden in framework
                         .setDeletionPropagation(true)
@@ -137,7 +139,9 @@
 
         JoinableConfig joinableConfig = JoinableConfig.newBuilder()
                 .setValueType(JoinableConfig.ValueType.Code.QUALIFIED_ID)
+                // @exportToFramework:startStrip()
                 .setPropagateDelete(true)
+                // @exportToFramework:endStrip()
                 .build();
 
         SchemaTypeConfigProto expectedAlbumProto = SchemaTypeConfigProto.newBuilder()
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverterTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverterTest.java
index 9a19a6a..de56b69 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverterTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverterTest.java
@@ -42,6 +42,7 @@
 import com.google.android.icing.proto.ScoringSpecProto;
 import com.google.android.icing.proto.SearchSpecProto;
 import com.google.android.icing.proto.TypePropertyWeights;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 
@@ -390,6 +391,170 @@
     }
 
     @Test
+    public void testToResultSpecProto_grouping_withJoinSpec_packageFilter() throws Exception {
+        String personPrefix = PrefixUtil.createPrefix("contacts", "database");
+        String actionPrefix = PrefixUtil.createPrefix("aiai", "database");
+
+        SchemaTypeConfigProto configProto = SchemaTypeConfigProto.getDefaultInstance();
+        Map<String, Map<String, SchemaTypeConfigProto>> schemaMap = ImmutableMap.of(
+                personPrefix, ImmutableMap.of(personPrefix + "typeA", configProto),
+                actionPrefix, ImmutableMap.of(actionPrefix + "typeA", configProto));
+        Map<String, Set<String>> namespaceMap = ImmutableMap.of(
+                personPrefix, ImmutableSet.of(personPrefix + "namespaceA"),
+                actionPrefix, ImmutableSet.of(actionPrefix + "namespaceA"));
+
+        SearchSpec nestedSearchSpec = new SearchSpec.Builder()
+                .setResultGrouping(SearchSpec.GROUPING_TYPE_PER_PACKAGE, 10)
+                .addFilterPackageNames("aiai")
+                .build();
+
+        // Create a JoinSpec object and set it in the converter
+        JoinSpec joinSpec = new JoinSpec.Builder("childPropertyExpression")
+                .setNestedSearch("nestedQuery", nestedSearchSpec)
+                .build();
+
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setJoinSpec(joinSpec)
+                .setResultGrouping(SearchSpec.GROUPING_TYPE_PER_PACKAGE, 10)
+                .addFilterPackageNames("contacts")
+                .build();
+
+        SearchSpecToProtoConverter converter = new SearchSpecToProtoConverter(
+                /*queryExpression=*/"query",
+                searchSpec,
+                /*prefixes=*/ImmutableSet.of(personPrefix, actionPrefix),
+                namespaceMap,
+                schemaMap,
+                mDefaultIcingOptionsConfig);
+
+        ResultSpecProto resultSpecProto = converter.toResultSpecProto(
+                namespaceMap,
+                schemaMap);
+
+        assertThat(resultSpecProto.getResultGroupingsCount()).isEqualTo(1);
+        assertThat(resultSpecProto.getResultGroupings(0).getEntryGroupings(0).getNamespace())
+                .isEqualTo("contacts$database/namespaceA");
+        ResultSpecProto nestedResultSpecProto =
+                converter.toSearchSpecProto().getJoinSpec().getNestedSpec().getResultSpec();
+        assertThat(nestedResultSpecProto.getResultGroupingsCount()).isEqualTo(1);
+        assertThat(nestedResultSpecProto.getResultGroupings(0).getEntryGroupings(0).getNamespace())
+                .isEqualTo("aiai$database/namespaceA");
+    }
+
+    @Test
+    public void testToResultSpecProto_projection_withJoinSpec_packageFilter() throws Exception {
+        String personPrefix = PrefixUtil.createPrefix("contacts", "database");
+        String actionPrefix = PrefixUtil.createPrefix("aiai", "database");
+
+        SchemaTypeConfigProto configProto = SchemaTypeConfigProto.getDefaultInstance();
+        Map<String, Map<String, SchemaTypeConfigProto>> schemaMap = ImmutableMap.of(
+                personPrefix, ImmutableMap.of(personPrefix + "Person", configProto),
+                actionPrefix, ImmutableMap.of(actionPrefix + "ContactAction", configProto));
+        Map<String, Set<String>> namespaceMap = ImmutableMap.of(
+                personPrefix, ImmutableSet.of(personPrefix + "namespaceA"),
+                actionPrefix, ImmutableSet.of(actionPrefix + "namespaceA"));
+
+        SearchSpec nestedSearchSpec = new SearchSpec.Builder()
+                .setResultGrouping(SearchSpec.GROUPING_TYPE_PER_PACKAGE, 10)
+                .addProjection("ContactAction", ImmutableList.of("type"))
+                .build();
+
+        // Create a JoinSpec object and set it in the converter
+        JoinSpec joinSpec = new JoinSpec.Builder("childPropertyExpression")
+                .setNestedSearch("nestedQuery", nestedSearchSpec)
+                .build();
+
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setJoinSpec(joinSpec)
+                .setResultGrouping(SearchSpec.GROUPING_TYPE_PER_PACKAGE, 10)
+                .addProjection("Person", ImmutableList.of("name"))
+                .build();
+
+        SearchSpecToProtoConverter converter = new SearchSpecToProtoConverter(
+                /*queryExpression=*/"query",
+                searchSpec,
+                /*prefixes=*/ImmutableSet.of(personPrefix, actionPrefix),
+                namespaceMap,
+                schemaMap,
+                mDefaultIcingOptionsConfig);
+
+        ResultSpecProto resultSpecProto = converter.toResultSpecProto(
+                namespaceMap,
+                schemaMap);
+
+        assertThat(resultSpecProto.getTypePropertyMasksCount()).isEqualTo(1);
+        assertThat(resultSpecProto.getTypePropertyMasks(0).getSchemaType()).isEqualTo(
+                "contacts$database/Person");
+        assertThat(resultSpecProto.getTypePropertyMasks(0).getPaths(0)).isEqualTo("name");
+
+        ResultSpecProto nestedResultSpecProto =
+                converter.toSearchSpecProto().getJoinSpec().getNestedSpec().getResultSpec();
+        assertThat(nestedResultSpecProto.getTypePropertyMasksCount()).isEqualTo(1);
+        assertThat(nestedResultSpecProto.getTypePropertyMasks(0).getSchemaType()).isEqualTo(
+                "aiai$database/ContactAction");
+        assertThat(nestedResultSpecProto.getTypePropertyMasks(0).getPaths(0)).isEqualTo("type");
+    }
+
+    @Test
+    public void testToResultSpecProto_weight_withJoinSpec_packageFilter() throws Exception {
+        String personPrefix = PrefixUtil.createPrefix("contacts", "database");
+        String actionPrefix = PrefixUtil.createPrefix("aiai", "database");
+
+        SchemaTypeConfigProto configProto = SchemaTypeConfigProto.getDefaultInstance();
+        Map<String, Map<String, SchemaTypeConfigProto>> schemaMap = ImmutableMap.of(
+                personPrefix, ImmutableMap.of(personPrefix + "Person", configProto),
+                actionPrefix, ImmutableMap.of(actionPrefix + "ContactAction", configProto));
+        Map<String, Set<String>> namespaceMap = ImmutableMap.of(
+                personPrefix, ImmutableSet.of(personPrefix + "namespaceA"),
+                actionPrefix, ImmutableSet.of(actionPrefix + "namespaceA"));
+
+        SearchSpec nestedSearchSpec = new SearchSpec.Builder()
+                .setResultGrouping(SearchSpec.GROUPING_TYPE_PER_PACKAGE, 10)
+                .setPropertyWeights("ContactAction", ImmutableMap.of("type", 3.0))
+                .setRankingStrategy(RANKING_STRATEGY_RELEVANCE_SCORE)
+                .build();
+
+        // Create a JoinSpec object and set it in the converter
+        JoinSpec joinSpec = new JoinSpec.Builder("childPropertyExpression")
+                .setNestedSearch("nestedQuery", nestedSearchSpec)
+                .build();
+
+        SearchSpec searchSpec = new SearchSpec.Builder()
+                .setJoinSpec(joinSpec)
+                .setPropertyWeights("Person", ImmutableMap.of("name", 2.0))
+                .setRankingStrategy(RANKING_STRATEGY_RELEVANCE_SCORE)
+                .build();
+
+        SearchSpecToProtoConverter converter = new SearchSpecToProtoConverter(
+                /*queryExpression=*/"query",
+                searchSpec,
+                /*prefixes=*/ImmutableSet.of(personPrefix, actionPrefix),
+                namespaceMap,
+                schemaMap,
+                mDefaultIcingOptionsConfig);
+
+        ScoringSpecProto scoringSpecProto = converter.toScoringSpecProto();
+
+        assertThat(scoringSpecProto.getTypePropertyWeightsCount()).isEqualTo(1);
+        assertThat(scoringSpecProto.getTypePropertyWeights(0).getSchemaType()).isEqualTo(
+                "contacts$database/Person");
+        assertThat(scoringSpecProto.getTypePropertyWeights(0)
+                .getPropertyWeights(0).getWeight()).isEqualTo(2);
+        assertThat(scoringSpecProto.getTypePropertyWeights(0)
+                .getPropertyWeights(0).getPath()).isEqualTo("name");
+
+        ScoringSpecProto nestedScoringSpecProto =
+                converter.toSearchSpecProto().getJoinSpec().getNestedSpec().getScoringSpec();
+        assertThat(nestedScoringSpecProto.getTypePropertyWeightsCount()).isEqualTo(1);
+        assertThat(nestedScoringSpecProto.getTypePropertyWeights(0).getSchemaType()).isEqualTo(
+                "aiai$database/ContactAction");
+        assertThat(nestedScoringSpecProto.getTypePropertyWeights(0)
+                .getPropertyWeights(0).getWeight()).isEqualTo(3);
+        assertThat(nestedScoringSpecProto.getTypePropertyWeights(0)
+                .getPropertyWeights(0).getPath()).isEqualTo("type");
+    }
+
+    @Test
     public void testToResultSpecProto_groupByPackage() {
         SearchSpec searchSpec = new SearchSpec.Builder()
                 .setResultGrouping(GROUPING_TYPE_PER_PACKAGE, 5)
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchImpl.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchImpl.java
index 55882b7..c819c45 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchImpl.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchImpl.java
@@ -1342,22 +1342,6 @@
                 return new SearchResultPage(Bundle.EMPTY);
             }
 
-            if (searchSpec.getJoinSpec() != null) {
-                List<String> joinFilterPackages =
-                        searchSpec.getJoinSpec().getNestedSearchSpec().getFilterPackageNames();
-
-                // Ensure the nested SearchSpec only filters on the package performing the query.
-                if (joinFilterPackages.isEmpty() || (joinFilterPackages.size() > 1
-                        && joinFilterPackages.contains(packageName))) {
-                    searchSpec.getJoinSpec().getNestedSearchSpec().getBundle()
-                            .putStringArrayList("packageName",
-                                    new ArrayList<>(Collections.singleton(packageName)));
-                } else if (!joinFilterPackages.contains(packageName)) {
-                    // Filter packages only contains other packages, remove the JoinSpec
-                    searchSpec.getBundle().remove("joinSpec");
-                }
-            }
-
             String prefix = createPrefix(packageName, databaseName);
             SearchSpecToProtoConverter searchSpecToProtoConverter =
                     new SearchSpecToProtoConverter(queryExpression, searchSpec,
@@ -1425,8 +1409,26 @@
             throwIfClosedLocked();
 
             long aclLatencyStartMillis = SystemClock.elapsedRealtime();
+
+            // The two scenarios where we want to limit package filters are if the outer
+            // SearchSpec has package filters and there is no JoinSpec, or if both outer and
+            // nested SearchSpecs have package filters. If outer SearchSpec has no package
+            // filters or the nested SearchSpec has no package filters, then we pass the key set of
+            // mNamespaceMapLocked to the SearchSpecToProtoConverter, signifying that there is a
+            // SearchSpec that wants to query every visible package.
+            Set<String> packageFilters = new ArraySet<>();
+            if (!searchSpec.getFilterPackageNames().isEmpty()) {
+                if (searchSpec.getJoinSpec() == null) {
+                    packageFilters.addAll(searchSpec.getFilterPackageNames());
+                } else if (!searchSpec.getJoinSpec().getNestedSearchSpec()
+                        .getFilterPackageNames().isEmpty()) {
+                    packageFilters.addAll(searchSpec.getFilterPackageNames());
+                    packageFilters.addAll(
+                            searchSpec.getJoinSpec().getNestedSearchSpec().getFilterPackageNames());
+                }
+            }
+
             // Convert package filters to prefix filters
-            Set<String> packageFilters = new ArraySet<>(searchSpec.getFilterPackageNames());
             Set<String> prefixFilters = new ArraySet<>();
             if (packageFilters.isEmpty()) {
                 // Client didn't restrict their search over packages. Try to query over all
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverter.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverter.java
index a8ba0d3..b7d648b 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverter.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/converter/SearchSpecToProtoConverter.java
@@ -65,7 +65,15 @@
     private static final String TAG = "AppSearchSearchSpecConv";
     private final String mQueryExpression;
     private final SearchSpec mSearchSpec;
-    private final Set<String> mPrefixes;
+    /**
+     * The union of allowed prefixes for the top-level SearchSpec and any nested SearchSpecs.
+     */
+    private final Set<String> mAllAllowedPrefixes;
+    /**
+     * The intersection of mAllAllowedPrefixes and prefixes requested in the SearchSpec currently
+     * being handled.
+     */
+    private final Set<String> mCurrentSearchSpecPrefixFilters;
     /**
      * The intersected prefixed namespaces that are existing in AppSearch and also accessible to the
      * client.
@@ -107,7 +115,9 @@
      *
      * @param queryExpression                Query String to search.
      * @param searchSpec    The spec we need to convert from.
-     * @param prefixes      Set of database prefix which the caller want to access.
+     * @param allAllowedPrefixes Superset of database prefixes which the {@link SearchSpec} and all
+     *                           nested SearchSpecs are allowed to access. An empty set means no
+     *                           database prefixes are allowed, so nothing will be searched.
      * @param namespaceMap  The cached Map of {@code <Prefix, Set<PrefixedNamespace>>} stores
      *                      all prefixed namespace filters which are stored in AppSearch.
      * @param schemaMap     The cached Map of {@code <Prefix, Map<PrefixedSchemaType, schemaProto>>}
@@ -116,25 +126,51 @@
     public SearchSpecToProtoConverter(
             @NonNull String queryExpression,
             @NonNull SearchSpec searchSpec,
-            @NonNull Set<String> prefixes,
+            @NonNull Set<String> allAllowedPrefixes,
             @NonNull Map<String, Set<String>> namespaceMap,
             @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap,
             @NonNull IcingOptionsConfig icingOptionsConfig) {
         mQueryExpression = Preconditions.checkNotNull(queryExpression);
         mSearchSpec = Preconditions.checkNotNull(searchSpec);
-        mPrefixes = Preconditions.checkNotNull(prefixes);
+        mAllAllowedPrefixes = Preconditions.checkNotNull(allAllowedPrefixes);
         mNamespaceMap = Preconditions.checkNotNull(namespaceMap);
         mSchemaMap = Preconditions.checkNotNull(schemaMap);
         mIcingOptionsConfig = Preconditions.checkNotNull(icingOptionsConfig);
+
+        // This field holds the prefix filters for the SearchSpec currently being handled, which
+        // could be an outer or inner SearchSpec. If this constructor is called from outside of
+        // SearchSpecToProtoConverter, it will be handling an outer SearchSpec. If this SearchSpec
+        // contains a JoinSpec, the nested SearchSpec will be handled in the creation of
+        // mNestedConverter. This is useful as the two SearchSpecs could have different package
+        // filters.
+        List<String> packageFilters = searchSpec.getFilterPackageNames();
+        if (packageFilters.isEmpty()) {
+            mCurrentSearchSpecPrefixFilters = mAllAllowedPrefixes;
+        } else {
+            mCurrentSearchSpecPrefixFilters = new ArraySet<>();
+            for (String prefix : mAllAllowedPrefixes) {
+                String packageName = getPackageName(prefix);
+                if (packageFilters.contains(packageName)) {
+                    // This performs an intersection of allowedPrefixes with prefixes requested
+                    // in the SearchSpec currently being handled. The same operation is done
+                    // on the nested SearchSpecs when mNestedConverter is created.
+                    mCurrentSearchSpecPrefixFilters.add(prefix);
+                }
+            }
+        }
+
         mTargetPrefixedNamespaceFilters =
                 SearchSpecToProtoConverterUtil.generateTargetNamespaceFilters(
-                        prefixes, namespaceMap, searchSpec.getFilterNamespaces());
+                        mCurrentSearchSpecPrefixFilters, namespaceMap,
+                        searchSpec.getFilterNamespaces());
+
         // If the target namespace filter is empty, the user has nothing to search for. We can skip
         // generate the target schema filter.
         if (!mTargetPrefixedNamespaceFilters.isEmpty()) {
             mTargetPrefixedSchemaFilters =
                     SearchSpecToProtoConverterUtil.generateTargetSchemaFilters(
-                            prefixes, schemaMap, searchSpec.getFilterSchemas());
+                            mCurrentSearchSpecPrefixFilters, schemaMap,
+                            searchSpec.getFilterSchemas());
         } else {
             mTargetPrefixedSchemaFilters = new ArraySet<>();
         }
@@ -147,7 +183,7 @@
         mNestedConverter = new SearchSpecToProtoConverter(
                 joinSpec.getNestedQuery(),
                 joinSpec.getNestedSearchSpec(),
-                mPrefixes,
+                mAllAllowedPrefixes,
                 namespaceMap,
                 schemaMap,
                 mIcingOptionsConfig);
@@ -361,41 +397,41 @@
                 ResultSpecProto.ResultGroupingType.NONE;
         switch (groupingType) {
             case SearchSpec.GROUPING_TYPE_PER_PACKAGE :
-                addPerPackageResultGroupings(mPrefixes, mSearchSpec.getResultGroupingLimit(),
-                        namespaceMap, resultSpecBuilder);
+                addPerPackageResultGroupings(mCurrentSearchSpecPrefixFilters,
+                        mSearchSpec.getResultGroupingLimit(), namespaceMap, resultSpecBuilder);
                 resultGroupingType = ResultSpecProto.ResultGroupingType.NAMESPACE;
                 break;
             case SearchSpec.GROUPING_TYPE_PER_NAMESPACE:
-                addPerNamespaceResultGroupings(mPrefixes, mSearchSpec.getResultGroupingLimit(),
-                        namespaceMap, resultSpecBuilder);
+                addPerNamespaceResultGroupings(mCurrentSearchSpecPrefixFilters,
+                        mSearchSpec.getResultGroupingLimit(), namespaceMap, resultSpecBuilder);
                 resultGroupingType = ResultSpecProto.ResultGroupingType.NAMESPACE;
                 break;
             case SearchSpec.GROUPING_TYPE_PER_SCHEMA:
-                addPerSchemaResultGrouping(mPrefixes, mSearchSpec.getResultGroupingLimit(),
-                        schemaMap, resultSpecBuilder);
+                addPerSchemaResultGrouping(mCurrentSearchSpecPrefixFilters,
+                        mSearchSpec.getResultGroupingLimit(), schemaMap, resultSpecBuilder);
                 resultGroupingType = ResultSpecProto.ResultGroupingType.SCHEMA_TYPE;
                 break;
             case SearchSpec.GROUPING_TYPE_PER_PACKAGE | SearchSpec.GROUPING_TYPE_PER_NAMESPACE:
-                addPerPackagePerNamespaceResultGroupings(mPrefixes,
+                addPerPackagePerNamespaceResultGroupings(mCurrentSearchSpecPrefixFilters,
                         mSearchSpec.getResultGroupingLimit(),
                         namespaceMap, resultSpecBuilder);
                 resultGroupingType = ResultSpecProto.ResultGroupingType.NAMESPACE;
                 break;
             case SearchSpec.GROUPING_TYPE_PER_PACKAGE | SearchSpec.GROUPING_TYPE_PER_SCHEMA:
-                addPerPackagePerSchemaResultGroupings(mPrefixes,
+                addPerPackagePerSchemaResultGroupings(mCurrentSearchSpecPrefixFilters,
                         mSearchSpec.getResultGroupingLimit(),
                         schemaMap, resultSpecBuilder);
                 resultGroupingType = ResultSpecProto.ResultGroupingType.SCHEMA_TYPE;
                 break;
             case SearchSpec.GROUPING_TYPE_PER_NAMESPACE | SearchSpec.GROUPING_TYPE_PER_SCHEMA:
-                addPerNamespaceAndSchemaResultGrouping(mPrefixes,
+                addPerNamespaceAndSchemaResultGrouping(mCurrentSearchSpecPrefixFilters,
                         mSearchSpec.getResultGroupingLimit(),
                         namespaceMap, schemaMap, resultSpecBuilder);
                 resultGroupingType = ResultSpecProto.ResultGroupingType.NAMESPACE_AND_SCHEMA_TYPE;
                 break;
             case SearchSpec.GROUPING_TYPE_PER_PACKAGE | SearchSpec.GROUPING_TYPE_PER_NAMESPACE
                 | SearchSpec.GROUPING_TYPE_PER_SCHEMA:
-                addPerPackagePerNamespacePerSchemaResultGrouping(mPrefixes,
+                addPerPackagePerNamespacePerSchemaResultGrouping(mCurrentSearchSpecPrefixFilters,
                         mSearchSpec.getResultGroupingLimit(),
                         namespaceMap, schemaMap, resultSpecBuilder);
                 resultGroupingType = ResultSpecProto.ResultGroupingType.NAMESPACE_AND_SCHEMA_TYPE;
@@ -415,7 +451,7 @@
             boolean isWildcard =
                     unprefixedType.equals(SearchSpec.PROJECTION_SCHEMA_TYPE_WILDCARD);
             // Qualify the given schema types
-            for (String prefix : mPrefixes) {
+            for (String prefix : mCurrentSearchSpecPrefixFilters) {
                 String prefixedType = isWildcard ? unprefixedType : prefix + unprefixedType;
                 if (isWildcard || mTargetPrefixedSchemaFilters.contains(prefixedType)) {
                     resultSpecBuilder.addTypePropertyMasks(typePropertyMaskBuilders.get(i)
@@ -923,7 +959,7 @@
 
         for (Map.Entry<String, Map<String, Double>> typePropertyWeight :
                 typePropertyWeightsMap.entrySet()) {
-            for (String prefix : mPrefixes) {
+            for (String prefix : mCurrentSearchSpecPrefixFilters) {
                 String prefixedSchemaType = prefix + typePropertyWeight.getKey();
                 if (mTargetPrefixedSchemaFilters.contains(prefixedSchemaType)) {
                     TypePropertyWeights.Builder typePropertyWeightsBuilder =
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/FeaturesImpl.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/FeaturesImpl.java
index e0a3931..ed3a8f6 100644
--- a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/FeaturesImpl.java
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/FeaturesImpl.java
@@ -63,8 +63,8 @@
 
             // Beyond Android U features
             case Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA:
-                // TODO(b/258715421) : Update to reflect support in Android U+ once this feature is
-                // synced over into service-appsearch.
+                // TODO(b/258715421) : Update to reflect support in Android U+ once this feature has
+                // an extservices sdk that includes it.
                 // fall through
             case Features.SCHEMA_SET_DELETION_PROPAGATION:
                 // TODO(b/268521214) : Update when feature is ready in service-appsearch.
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SearchSpecToPlatformConverter.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SearchSpecToPlatformConverter.java
index a861ec8..4cb82c1 100644
--- a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SearchSpecToPlatformConverter.java
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/converter/SearchSpecToPlatformConverter.java
@@ -77,8 +77,8 @@
                 .setSnippetCountPerProperty(jetpackSearchSpec.getSnippetCountPerProperty())
                 .setMaxSnippetSize(jetpackSearchSpec.getMaxSnippetSize());
         if (jetpackSearchSpec.getResultGroupingTypeFlags() != 0) {
-            // TODO(b/258715421): Add Build.VERSION.SDK_INT condition once
-            // SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA is supported on Android U.
+            // TODO(b/258715421): Add Build.VERSION.SDK_INT condition once there is an extservices
+            // sdk that includes SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA.
             if (true) {
                 if ((jetpackSearchSpec.getResultGroupingTypeFlags()
                         & SearchSpec.GROUPING_TYPE_PER_SCHEMA) != 0) {
diff --git a/appsearch/appsearch/api/current.txt b/appsearch/appsearch/api/current.txt
index 4a4cac2..77b9381 100644
--- a/appsearch/appsearch/api/current.txt
+++ b/appsearch/appsearch/api/current.txt
@@ -31,6 +31,12 @@
   @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public static @interface Document.DoubleProperty {
     method public abstract String name() default "";
     method public abstract boolean required() default false;
+    method public abstract Class<?> serializer() default androidx.appsearch.annotation.Document.DoubleProperty.DefaultSerializer.class;
+  }
+
+  public static final class Document.DoubleProperty.DefaultSerializer {
+    method public static double deserialize(double);
+    method public static double serialize(double);
   }
 
   @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public static @interface Document.Id {
@@ -40,6 +46,12 @@
     method public abstract int indexingType() default androidx.appsearch.app.AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_NONE;
     method public abstract String name() default "";
     method public abstract boolean required() default false;
+    method public abstract Class<?> serializer() default androidx.appsearch.annotation.Document.LongProperty.DefaultSerializer.class;
+  }
+
+  public static final class Document.LongProperty.DefaultSerializer {
+    method public static long deserialize(long);
+    method public static long serialize(long);
   }
 
   @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public static @interface Document.Namespace {
@@ -53,9 +65,15 @@
     method public abstract int joinableValueType() default androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE;
     method public abstract String name() default "";
     method public abstract boolean required() default false;
+    method public abstract Class<?> serializer() default androidx.appsearch.annotation.Document.StringProperty.DefaultSerializer.class;
     method public abstract int tokenizerType() default androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN;
   }
 
+  public static final class Document.StringProperty.DefaultSerializer {
+    method public static String deserialize(String);
+    method public static String serialize(String);
+  }
+
   @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public static @interface Document.TtlMillis {
   }
 
diff --git a/appsearch/appsearch/api/restricted_current.txt b/appsearch/appsearch/api/restricted_current.txt
index 4a4cac2..77b9381 100644
--- a/appsearch/appsearch/api/restricted_current.txt
+++ b/appsearch/appsearch/api/restricted_current.txt
@@ -31,6 +31,12 @@
   @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public static @interface Document.DoubleProperty {
     method public abstract String name() default "";
     method public abstract boolean required() default false;
+    method public abstract Class<?> serializer() default androidx.appsearch.annotation.Document.DoubleProperty.DefaultSerializer.class;
+  }
+
+  public static final class Document.DoubleProperty.DefaultSerializer {
+    method public static double deserialize(double);
+    method public static double serialize(double);
   }
 
   @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public static @interface Document.Id {
@@ -40,6 +46,12 @@
     method public abstract int indexingType() default androidx.appsearch.app.AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_NONE;
     method public abstract String name() default "";
     method public abstract boolean required() default false;
+    method public abstract Class<?> serializer() default androidx.appsearch.annotation.Document.LongProperty.DefaultSerializer.class;
+  }
+
+  public static final class Document.LongProperty.DefaultSerializer {
+    method public static long deserialize(long);
+    method public static long serialize(long);
   }
 
   @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public static @interface Document.Namespace {
@@ -53,9 +65,15 @@
     method public abstract int joinableValueType() default androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE;
     method public abstract String name() default "";
     method public abstract boolean required() default false;
+    method public abstract Class<?> serializer() default androidx.appsearch.annotation.Document.StringProperty.DefaultSerializer.class;
     method public abstract int tokenizerType() default androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN;
   }
 
+  public static final class Document.StringProperty.DefaultSerializer {
+    method public static String deserialize(String);
+    method public static String serialize(String);
+  }
+
   @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public static @interface Document.TtlMillis {
   }
 
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSchemaInternalTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSchemaInternalTest.java
new file mode 100644
index 0000000..58be2eb
--- /dev/null
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSchemaInternalTest.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.appsearch.app;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import androidx.appsearch.testutil.AppSearchEmail;
+
+import org.junit.Test;
+
+import java.util.List;
+
+/** Tests for private APIs of {@link AppSearchSchema}. */
+public class AppSearchSchemaInternalTest {
+    // TODO(b/291122592): move to CTS once the APIs it uses are public
+    @Test
+    public void testParentTypes() {
+        AppSearchSchema schema =
+                new AppSearchSchema.Builder("EmailMessage")
+                        .addParentType("Email")
+                        .addParentType("Message")
+                        .build();
+        assertThat(schema.getParentTypes()).containsExactly("Email", "Message");
+    }
+
+    // TODO(b/291122592): move to CTS once the APIs it uses are public
+    @Test
+    public void testDuplicateParentTypes() {
+        AppSearchSchema schema =
+                new AppSearchSchema.Builder("EmailMessage")
+                        .addParentType("Email")
+                        .addParentType("Message")
+                        .addParentType("Email")
+                        .build();
+        assertThat(schema.getParentTypes()).containsExactly("Email", "Message");
+    }
+
+    // TODO(b/291122592): move to CTS once the APIs it uses are public
+    @Test
+    public void testDocumentPropertyConfig_indexableNestedPropertyStrings() {
+        AppSearchSchema.DocumentPropertyConfig documentPropertyConfig =
+                new AppSearchSchema.DocumentPropertyConfig.Builder("property", "Schema")
+                        .addIndexableNestedProperties("prop1", "prop2", "prop1.prop2")
+                        .build();
+        assertThat(documentPropertyConfig.getIndexableNestedProperties())
+                .containsExactly("prop1", "prop2", "prop1.prop2");
+    }
+
+    // TODO(b/291122592): move to CTS once the APIs it uses are public
+    @Test
+    public void testDocumentPropertyConfig_indexableNestedPropertyPropertyPaths() {
+        AppSearchSchema.DocumentPropertyConfig documentPropertyConfig =
+                new AppSearchSchema.DocumentPropertyConfig.Builder("property", "Schema")
+                        .addIndexableNestedPropertyPaths(
+                                new PropertyPath("prop1"), new PropertyPath("prop1.prop2"))
+                        .build();
+        assertThat(documentPropertyConfig.getIndexableNestedProperties())
+                .containsExactly("prop1", "prop1.prop2");
+    }
+
+    // TODO(b/291122592): move to CTS once the APIs it uses are public
+    @Test
+    public void testDocumentPropertyConfig_indexableNestedPropertyProperty_duplicatePaths() {
+        AppSearchSchema.DocumentPropertyConfig documentPropertyConfig =
+                new AppSearchSchema.DocumentPropertyConfig.Builder("property", "Schema")
+                        .addIndexableNestedPropertyPaths(
+                                new PropertyPath("prop1"), new PropertyPath("prop1.prop2"))
+                        .addIndexableNestedProperties("prop1")
+                        .build();
+        assertThat(documentPropertyConfig.getIndexableNestedProperties())
+                .containsExactly("prop1", "prop1.prop2");
+    }
+
+    // TODO(b/291122592): move to CTS once the APIs it uses are public
+    @Test
+    public void testDocumentPropertyConfig_reusingBuilderDoesNotAffectPreviouslyBuiltConfigs() {
+        AppSearchSchema.DocumentPropertyConfig.Builder builder =
+                new AppSearchSchema.DocumentPropertyConfig.Builder("property", "Schema")
+                        .addIndexableNestedProperties("prop1");
+        AppSearchSchema.DocumentPropertyConfig config1 = builder.build();
+        assertThat(config1.getIndexableNestedProperties()).containsExactly("prop1");
+
+        builder.addIndexableNestedProperties("prop2");
+        AppSearchSchema.DocumentPropertyConfig config2 = builder.build();
+        assertThat(config2.getIndexableNestedProperties()).containsExactly("prop1", "prop2");
+        assertThat(config1.getIndexableNestedProperties()).containsExactly("prop1");
+
+        builder.addIndexableNestedPropertyPaths(new PropertyPath("prop3"));
+        AppSearchSchema.DocumentPropertyConfig config3 = builder.build();
+        assertThat(config3.getIndexableNestedProperties())
+                .containsExactly("prop1", "prop2", "prop3");
+        assertThat(config2.getIndexableNestedProperties()).containsExactly("prop1", "prop2");
+        assertThat(config1.getIndexableNestedProperties()).containsExactly("prop1");
+    }
+
+    // TODO(b/291122592): move to CTS once the APIs it uses are public
+    @Test
+    public void testPropertyConfig() {
+        AppSearchSchema schema =
+                new AppSearchSchema.Builder("Test")
+                        .addProperty(
+                                new AppSearchSchema.StringPropertyConfig.Builder("string")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setIndexingType(
+                                                AppSearchSchema.StringPropertyConfig
+                                                        .INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(
+                                                AppSearchSchema.StringPropertyConfig
+                                                        .TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.LongPropertyConfig.Builder("long")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                AppSearchSchema.LongPropertyConfig
+                                                        .INDEXING_TYPE_NONE)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.LongPropertyConfig.Builder("indexableLong")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                AppSearchSchema.LongPropertyConfig
+                                                        .INDEXING_TYPE_RANGE)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.DoublePropertyConfig.Builder("double")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.BooleanPropertyConfig.Builder("boolean")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.BytesPropertyConfig.Builder("bytes")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.DocumentPropertyConfig.Builder(
+                                                "document1", AppSearchEmail.SCHEMA_TYPE)
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                                        .setShouldIndexNestedProperties(true)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.DocumentPropertyConfig.Builder(
+                                                "document2", AppSearchEmail.SCHEMA_TYPE)
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                                        .setShouldIndexNestedProperties(false)
+                                        .addIndexableNestedProperties("path1", "path2", "path3")
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.StringPropertyConfig.Builder("qualifiedId1")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setJoinableValueType(
+                                                AppSearchSchema.StringPropertyConfig
+                                                        .JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.StringPropertyConfig.Builder("qualifiedId2")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setJoinableValueType(
+                                                AppSearchSchema.StringPropertyConfig
+                                                        .JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+                                        .build())
+                        .build();
+
+        assertThat(schema.getSchemaType()).isEqualTo("Test");
+        List<AppSearchSchema.PropertyConfig> properties = schema.getProperties();
+        assertThat(properties).hasSize(10);
+
+        assertThat(properties.get(0).getName()).isEqualTo("string");
+        assertThat(properties.get(0).getCardinality())
+                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED);
+        assertThat(((AppSearchSchema.StringPropertyConfig) properties.get(0)).getIndexingType())
+                .isEqualTo(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS);
+        assertThat(((AppSearchSchema.StringPropertyConfig) properties.get(0)).getTokenizerType())
+                .isEqualTo(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN);
+
+        assertThat(properties.get(1).getName()).isEqualTo("long");
+        assertThat(properties.get(1).getCardinality())
+                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL);
+        assertThat(((AppSearchSchema.LongPropertyConfig) properties.get(1)).getIndexingType())
+                .isEqualTo(AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_NONE);
+
+        assertThat(properties.get(2).getName()).isEqualTo("indexableLong");
+        assertThat(properties.get(2).getCardinality())
+                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL);
+        assertThat(((AppSearchSchema.LongPropertyConfig) properties.get(2)).getIndexingType())
+                .isEqualTo(AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_RANGE);
+
+        assertThat(properties.get(3).getName()).isEqualTo("double");
+        assertThat(properties.get(3).getCardinality())
+                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED);
+        assertThat(properties.get(3)).isInstanceOf(AppSearchSchema.DoublePropertyConfig.class);
+
+        assertThat(properties.get(4).getName()).isEqualTo("boolean");
+        assertThat(properties.get(4).getCardinality())
+                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED);
+        assertThat(properties.get(4)).isInstanceOf(AppSearchSchema.BooleanPropertyConfig.class);
+
+        assertThat(properties.get(5).getName()).isEqualTo("bytes");
+        assertThat(properties.get(5).getCardinality())
+                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL);
+        assertThat(properties.get(5)).isInstanceOf(AppSearchSchema.BytesPropertyConfig.class);
+
+        assertThat(properties.get(6).getName()).isEqualTo("document1");
+        assertThat(properties.get(6).getCardinality())
+                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED);
+        assertThat(((AppSearchSchema.DocumentPropertyConfig) properties.get(6)).getSchemaType())
+                .isEqualTo(AppSearchEmail.SCHEMA_TYPE);
+        assertThat(
+                        ((AppSearchSchema.DocumentPropertyConfig) properties.get(6))
+                                .shouldIndexNestedProperties())
+                .isEqualTo(true);
+
+        assertThat(properties.get(7).getName()).isEqualTo("document2");
+        assertThat(properties.get(7).getCardinality())
+                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED);
+        assertThat(((AppSearchSchema.DocumentPropertyConfig) properties.get(7)).getSchemaType())
+                .isEqualTo(AppSearchEmail.SCHEMA_TYPE);
+        assertThat(
+                        ((AppSearchSchema.DocumentPropertyConfig) properties.get(7))
+                                .shouldIndexNestedProperties())
+                .isEqualTo(false);
+        assertThat(
+                        ((AppSearchSchema.DocumentPropertyConfig) properties.get(7))
+                                .getIndexableNestedProperties())
+                .containsExactly("path1", "path2", "path3");
+
+        assertThat(properties.get(8).getName()).isEqualTo("qualifiedId1");
+        assertThat(properties.get(8).getCardinality())
+                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL);
+        assertThat(
+                        ((AppSearchSchema.StringPropertyConfig) properties.get(8))
+                                .getJoinableValueType())
+                .isEqualTo(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID);
+
+        assertThat(properties.get(9).getName()).isEqualTo("qualifiedId2");
+        assertThat(properties.get(9).getCardinality())
+                .isEqualTo(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED);
+        assertThat(
+                        ((AppSearchSchema.StringPropertyConfig) properties.get(9))
+                                .getJoinableValueType())
+                .isEqualTo(AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID);
+    }
+
+    // TODO(b/291122592): move to CTS once the APIs it uses are public
+    @Test
+    public void testInvalidDocumentPropertyConfig_indexableNestedProperties() {
+        // Adding indexableNestedProperties with shouldIndexNestedProperties=true should fail.
+        AppSearchSchema.DocumentPropertyConfig.Builder builder =
+                new AppSearchSchema.DocumentPropertyConfig.Builder("prop1", "Schema1")
+                        .setShouldIndexNestedProperties(true)
+                        .addIndexableNestedProperties("prop1");
+        IllegalArgumentException e =
+                assertThrows(IllegalArgumentException.class, () -> builder.build());
+        assertThat(e)
+                .hasMessageThat()
+                .contains(
+                        "DocumentIndexingConfig#shouldIndexNestedProperties is required to be false"
+                            + " when one or more indexableNestedProperties are provided.");
+
+        builder.addIndexableNestedPropertyPaths(new PropertyPath("prop1.prop2"));
+        e = assertThrows(IllegalArgumentException.class, () -> builder.build());
+        assertThat(e)
+                .hasMessageThat()
+                .contains(
+                        "DocumentIndexingConfig#shouldIndexNestedProperties is required to be false"
+                            + " when one or more indexableNestedProperties are provided.");
+    }
+}
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionInternalTestBase.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionInternalTestBase.java
index c289e9e..87f4f43 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionInternalTestBase.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionInternalTestBase.java
@@ -16,8 +16,10 @@
 
 package androidx.appsearch.app;
 
+import static androidx.appsearch.app.AppSearchResult.RESULT_INVALID_ARGUMENT;
 import static androidx.appsearch.testutil.AppSearchTestUtils.checkIsBatchResultSuccess;
 import static androidx.appsearch.testutil.AppSearchTestUtils.convertSearchResultsToDocuments;
+import static androidx.appsearch.testutil.AppSearchTestUtils.doGet;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -28,6 +30,7 @@
 import androidx.annotation.NonNull;
 import androidx.appsearch.app.AppSearchSchema.PropertyConfig;
 import androidx.appsearch.app.AppSearchSchema.StringPropertyConfig;
+import androidx.appsearch.exceptions.AppSearchException;
 import androidx.appsearch.testutil.AppSearchEmail;
 
 import com.google.common.collect.ImmutableList;
@@ -39,6 +42,7 @@
 
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 
 public abstract class AppSearchSessionInternalTestBase {
@@ -69,53 +73,66 @@
     }
 
     private void cleanup() throws Exception {
-        mDb1.setSchemaAsync(
-                new SetSchemaRequest.Builder().setForceOverride(true).build()).get();
+        mDb1.setSchemaAsync(new SetSchemaRequest.Builder().setForceOverride(true).build()).get();
     }
 
     // TODO(b/228240987) delete this test when we support property restrict for multiple terms
     @Test
     public void testSearchSuggestion_propertyFilter() throws Exception {
         // Schema registration
-        AppSearchSchema schemaType1 = new AppSearchSchema.Builder("Type1").addProperty(
-                        new StringPropertyConfig.Builder("propertyone")
-                                .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                                .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                                .build()).addProperty(
-                        new StringPropertyConfig.Builder("propertytwo")
-                                .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                                .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                                .build())
-                .build();
-        AppSearchSchema schemaType2 = new AppSearchSchema.Builder("Type2").addProperty(
-                        new StringPropertyConfig.Builder("propertythree")
-                                .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                                .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                                .build()).addProperty(
-                        new StringPropertyConfig.Builder("propertyfour")
-                                .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                                .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                                .build())
-                .build();
-        mDb1.setSchemaAsync(new SetSchemaRequest.Builder()
-                .addSchemas(schemaType1, schemaType2).build()).get();
+        AppSearchSchema schemaType1 =
+                new AppSearchSchema.Builder("Type1")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("propertyone")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("propertytwo")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .build())
+                        .build();
+        AppSearchSchema schemaType2 =
+                new AppSearchSchema.Builder("Type2")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("propertythree")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("propertyfour")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .build())
+                        .build();
+        mDb1.setSchemaAsync(
+                        new SetSchemaRequest.Builder().addSchemas(schemaType1, schemaType2).build())
+                .get();
 
         // Index documents
-        GenericDocument doc1 = new GenericDocument.Builder<>("namespace", "id1", "Type1")
-                .setPropertyString("propertyone", "termone")
-                .setPropertyString("propertytwo", "termtwo")
-                .build();
-        GenericDocument doc2 = new GenericDocument.Builder<>("namespace", "id2", "Type2")
-                .setPropertyString("propertythree", "termthree")
-                .setPropertyString("propertyfour", "termfour")
-                .build();
+        GenericDocument doc1 =
+                new GenericDocument.Builder<>("namespace", "id1", "Type1")
+                        .setPropertyString("propertyone", "termone")
+                        .setPropertyString("propertytwo", "termtwo")
+                        .build();
+        GenericDocument doc2 =
+                new GenericDocument.Builder<>("namespace", "id2", "Type2")
+                        .setPropertyString("propertythree", "termthree")
+                        .setPropertyString("propertyfour", "termfour")
+                        .build();
 
-        checkIsBatchResultSuccess(mDb1.putAsync(
-                new PutDocumentsRequest.Builder().addGenericDocuments(doc1, doc2).build()));
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                        new PutDocumentsRequest.Builder().addGenericDocuments(doc1, doc2).build()));
 
         SearchSuggestionResult resultOne =
                 new SearchSuggestionResult.Builder().setSuggestedResult("termone").build();
@@ -127,403 +144,104 @@
                 new SearchSuggestionResult.Builder().setSuggestedResult("termfour").build();
 
         // Only search for type1/propertyone
-        List<SearchSuggestionResult> suggestions = mDb1.searchSuggestionAsync(
-                /*suggestionQueryExpression=*/"t",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10)
-                        .addFilterSchemas("Type1")
-                        .addFilterProperties("Type1", ImmutableList.of("propertyone"))
-                        .build()).get();
+        List<SearchSuggestionResult> suggestions =
+                mDb1.searchSuggestionAsync(
+                                /* suggestionQueryExpression= */ "t",
+                                new SearchSuggestionSpec.Builder(/* totalResultCount= */ 10)
+                                        .addFilterSchemas("Type1")
+                                        .addFilterProperties(
+                                                "Type1", ImmutableList.of("propertyone"))
+                                        .build())
+                        .get();
         assertThat(suggestions).containsExactly(resultOne);
 
         // Only search for type1/propertyone and type1/propertytwo
-        suggestions = mDb1.searchSuggestionAsync(
-                /*suggestionQueryExpression=*/"t",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10)
-                        .addFilterSchemas("Type1")
-                        .addFilterProperties("Type1",
-                                ImmutableList.of("propertyone", "propertytwo"))
-                        .build()).get();
+        suggestions =
+                mDb1.searchSuggestionAsync(
+                                /* suggestionQueryExpression= */ "t",
+                                new SearchSuggestionSpec.Builder(/* totalResultCount= */ 10)
+                                        .addFilterSchemas("Type1")
+                                        .addFilterProperties(
+                                                "Type1",
+                                                ImmutableList.of("propertyone", "propertytwo"))
+                                        .build())
+                        .get();
         assertThat(suggestions).containsExactly(resultOne, resultTwo);
 
         // Only search for type1/propertyone and type2/propertythree
-        suggestions = mDb1.searchSuggestionAsync(
-                /*suggestionQueryExpression=*/"t",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10)
-                        .addFilterSchemas("Type1", "Type2")
-                        .addFilterProperties("Type1", ImmutableList.of("propertyone"))
-                        .addFilterProperties("Type2", ImmutableList.of("propertythree"))
-                        .build()).get();
+        suggestions =
+                mDb1.searchSuggestionAsync(
+                                /* suggestionQueryExpression= */ "t",
+                                new SearchSuggestionSpec.Builder(/* totalResultCount= */ 10)
+                                        .addFilterSchemas("Type1", "Type2")
+                                        .addFilterProperties(
+                                                "Type1", ImmutableList.of("propertyone"))
+                                        .addFilterProperties(
+                                                "Type2", ImmutableList.of("propertythree"))
+                                        .build())
+                        .get();
         assertThat(suggestions).containsExactly(resultOne, resultThree);
 
         // Only search for type1/propertyone and type2/propertyfour, in addFilterPropertyPaths
-        suggestions = mDb1.searchSuggestionAsync(
-                /*suggestionQueryExpression=*/"t",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10)
-                        .addFilterSchemas("Type1", "Type2")
-                        .addFilterProperties("Type1", ImmutableList.of("propertyone"))
-                        .addFilterPropertyPaths("Type2",
-                                ImmutableList.of(new PropertyPath("propertyfour")))
-                        .build()).get();
+        suggestions =
+                mDb1.searchSuggestionAsync(
+                                /* suggestionQueryExpression= */ "t",
+                                new SearchSuggestionSpec.Builder(/* totalResultCount= */ 10)
+                                        .addFilterSchemas("Type1", "Type2")
+                                        .addFilterProperties(
+                                                "Type1", ImmutableList.of("propertyone"))
+                                        .addFilterPropertyPaths(
+                                                "Type2",
+                                                ImmutableList.of(new PropertyPath("propertyfour")))
+                                        .build())
+                        .get();
         assertThat(suggestions).containsExactly(resultOne, resultFour);
 
         // Only search for type1/propertyone and everything in type2
-        suggestions = mDb1.searchSuggestionAsync(
-                /*suggestionQueryExpression=*/"t",
-                new SearchSuggestionSpec.Builder(/*totalResultCount=*/10)
-                        .addFilterProperties("Type1", ImmutableList.of("propertyone"))
-                        .build()).get();
+        suggestions =
+                mDb1.searchSuggestionAsync(
+                                /* suggestionQueryExpression= */ "t",
+                                new SearchSuggestionSpec.Builder(/* totalResultCount= */ 10)
+                                        .addFilterProperties(
+                                                "Type1", ImmutableList.of("propertyone"))
+                                        .build())
+                        .get();
         assertThat(suggestions).containsExactly(resultOne, resultThree, resultFour);
     }
 
-    // TODO(b/258715421): move this test to cts test once we un-hide schema type grouping API.
-    @Test
-    public void testQuery_ResultGroupingLimits_SchemaGroupingSupported() throws Exception {
-        assumeTrue(mDb1.getFeatures()
-                .isFeatureSupported(Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA));
-        // Schema registration
-        AppSearchSchema genericSchema = new AppSearchSchema.Builder("Generic")
-                .addProperty(new StringPropertyConfig.Builder("foo")
-                .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                .build()
-            ).build();
-        mDb1.setSchemaAsync(new SetSchemaRequest.Builder()
-                .addSchemas(AppSearchEmail.SCHEMA)
-                .addSchemas(genericSchema)
-                .build())
-            .get();
-
-        // Index four documents.
-        AppSearchEmail inEmail1 =
-                new AppSearchEmail.Builder("namespace1", "id1")
-                .setFrom("[email protected]")
-                .setTo("[email protected]", "[email protected]")
-                .setSubject("testPut example")
-                .setBody("This is the body of the testPut email")
-                .build();
-        checkIsBatchResultSuccess(mDb1.putAsync(
-                new PutDocumentsRequest.Builder().addGenericDocuments(inEmail1).build()));
-        AppSearchEmail inEmail2 =
-                new AppSearchEmail.Builder("namespace1", "id2")
-                .setFrom("[email protected]")
-                .setTo("[email protected]", "[email protected]")
-                .setSubject("testPut example")
-                .setBody("This is the body of the testPut email")
-                .build();
-        checkIsBatchResultSuccess(mDb1.putAsync(
-                new PutDocumentsRequest.Builder().addGenericDocuments(inEmail2).build()));
-        AppSearchEmail inEmail3 =
-                new AppSearchEmail.Builder("namespace2", "id3")
-                .setFrom("[email protected]")
-                .setTo("[email protected]", "[email protected]")
-                .setSubject("testPut example")
-                .setBody("This is the body of the testPut email")
-                .build();
-        checkIsBatchResultSuccess(mDb1.putAsync(
-                new PutDocumentsRequest.Builder().addGenericDocuments(inEmail3).build()));
-        AppSearchEmail inEmail4 =
-                new AppSearchEmail.Builder("namespace2", "id4")
-                .setFrom("[email protected]")
-                .setTo("[email protected]", "[email protected]")
-                .setSubject("testPut example")
-                .setBody("This is the body of the testPut email")
-                .build();
-        checkIsBatchResultSuccess(mDb1.putAsync(
-                new PutDocumentsRequest.Builder().addGenericDocuments(inEmail4).build()));
-        AppSearchEmail inEmail5 =
-                new AppSearchEmail.Builder("namespace2", "id5")
-                .setFrom("[email protected]")
-                .setTo("[email protected]", "[email protected]")
-                .setSubject("testPut example")
-                .setBody("This is the body of the testPut email")
-                .build();
-        checkIsBatchResultSuccess(mDb1.putAsync(
-                new PutDocumentsRequest.Builder().addGenericDocuments(inEmail5).build()));
-        GenericDocument inDoc1 =
-                new GenericDocument.Builder<>("namespace3", "id6", "Generic")
-                .setPropertyString("foo", "body").build();
-        checkIsBatchResultSuccess(mDb1.putAsync(
-                new PutDocumentsRequest.Builder().addGenericDocuments(inDoc1).build()));
-        GenericDocument inDoc2 =
-                new GenericDocument.Builder<>("namespace3", "id7", "Generic")
-                .setPropertyString("foo", "body").build();
-        checkIsBatchResultSuccess(mDb1.putAsync(
-                new PutDocumentsRequest.Builder().addGenericDocuments(inDoc2).build()));
-        GenericDocument inDoc3 =
-                new GenericDocument.Builder<>("namespace4", "id8", "Generic")
-                .setPropertyString("foo", "body").build();
-        checkIsBatchResultSuccess(mDb1.putAsync(
-                new PutDocumentsRequest.Builder().addGenericDocuments(inDoc3).build()));
-
-        // Query with per package result grouping. Only the last document 'doc3' should be
-        // returned.
-        SearchResults searchResults = mDb1.search("body", new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .setResultGrouping(SearchSpec.GROUPING_TYPE_PER_PACKAGE, /*resultLimit=*/ 1)
-                .build());
-        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).containsExactly(inDoc3);
-
-        // Query with per namespace result grouping. Only the last document in each namespace should
-        // be returned ('doc3', 'doc2', 'email5' and 'email2').
-        searchResults = mDb1.search("body", new SearchSpec.Builder()
-            .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-            .setResultGrouping(
-                SearchSpec.GROUPING_TYPE_PER_NAMESPACE, /*resultLimit=*/ 1)
-            .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail2);
-
-        // Query with per namespace result grouping. Two of the last documents in each namespace
-        // should be returned ('doc3', 'doc2', 'doc1', 'email5', 'email4', 'email2', 'email1')
-        searchResults = mDb1.search("body", new SearchSpec.Builder()
-            .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-            .setResultGrouping(
-                SearchSpec.GROUPING_TYPE_PER_NAMESPACE, /*resultLimit=*/ 2)
-            .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).containsExactly(inDoc3, inDoc2, inDoc1, inEmail5, inEmail4, inEmail2,
-                inEmail1);
-
-        // Query with per schema result grouping. Only the last document of each schema type should
-        // be returned ('doc3', 'email5')
-        searchResults = mDb1.search("body", new SearchSpec.Builder()
-            .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-            .setResultGrouping(
-                SearchSpec.GROUPING_TYPE_PER_SCHEMA, /*resultLimit=*/ 1)
-            .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).containsExactly(inDoc3, inEmail5);
-
-        // Query with per schema result grouping. Only the last two documents of each schema type
-        // should be returned ('doc3', 'doc2', 'email5', 'email4')
-        searchResults = mDb1.search("body", new SearchSpec.Builder()
-            .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-            .setResultGrouping(
-                SearchSpec.GROUPING_TYPE_PER_SCHEMA, /*resultLimit=*/ 2)
-            .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail4);
-
-        // Query with per package and per namespace result grouping. Only the last document in each
-        // namespace should be returned ('doc3', 'doc2', 'email5' and 'email2').
-        searchResults = mDb1.search("body", new SearchSpec.Builder()
-            .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-            .setResultGrouping(
-                SearchSpec.GROUPING_TYPE_PER_NAMESPACE
-                    | SearchSpec.GROUPING_TYPE_PER_PACKAGE, /*resultLimit=*/ 1)
-            .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail2);
-
-        // Query with per package and per namespace result grouping. Only the last two documents
-        // in each namespace should be returned ('doc3', 'doc2', 'doc1', 'email5', 'email4',
-        // 'email2', 'email1')
-        searchResults = mDb1.search("body", new SearchSpec.Builder()
-            .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-            .setResultGrouping(
-                SearchSpec.GROUPING_TYPE_PER_NAMESPACE
-                    | SearchSpec.GROUPING_TYPE_PER_PACKAGE, /*resultLimit=*/ 2)
-            .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).containsExactly(inDoc3, inDoc2, inDoc1, inEmail5, inEmail4, inEmail2,
-                inEmail1);
-
-        // Query with per package and per schema type result grouping. Only the last document in
-        // each schema type should be returned. ('doc3', 'email5')
-        searchResults = mDb1.search("body", new SearchSpec.Builder()
-            .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-            .setResultGrouping(
-                SearchSpec.GROUPING_TYPE_PER_SCHEMA
-                    | SearchSpec.GROUPING_TYPE_PER_PACKAGE, /*resultLimit=*/ 1)
-            .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).containsExactly(inDoc3, inEmail5);
-
-        // Query with per package and per schema type result grouping. Only the last two document in
-        // each schema type should be returned. ('doc3', 'doc2', 'email5', 'email4')
-        searchResults = mDb1.search("body", new SearchSpec.Builder()
-            .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-            .setResultGrouping(
-                SearchSpec.GROUPING_TYPE_PER_SCHEMA
-                    | SearchSpec.GROUPING_TYPE_PER_PACKAGE, /*resultLimit=*/ 2)
-            .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail4);
-
-        // Query with per namespace and per schema type result grouping. Only the last document in
-        // each namespace should be returned. ('doc3', 'doc2', 'email5' and 'email2').
-        searchResults = mDb1.search("body", new SearchSpec.Builder()
-            .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-            .setResultGrouping(
-                SearchSpec.GROUPING_TYPE_PER_NAMESPACE
-                    | SearchSpec.GROUPING_TYPE_PER_SCHEMA, /*resultLimit=*/ 1)
-            .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail2);
-
-        // Query with per namespace and per schema type result grouping. Only the last two documents
-        // in each namespace should be returned. ('doc3', 'doc2', 'doc1', 'email5', 'email4',
-        // 'email2', 'email1')
-        searchResults = mDb1.search("body", new SearchSpec.Builder()
-            .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-            .setResultGrouping(
-                SearchSpec.GROUPING_TYPE_PER_NAMESPACE
-                    | SearchSpec.GROUPING_TYPE_PER_SCHEMA, /*resultLimit=*/ 2)
-            .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).containsExactly(inDoc3, inDoc2, inDoc1, inEmail5, inEmail4, inEmail2,
-                inEmail1);
-
-        // Query with per namespace, per package and per schema type result grouping. Only the last
-        // document in each namespace should be returned. ('doc3', 'doc2', 'email5' and 'email2')
-        searchResults = mDb1.search("body", new SearchSpec.Builder()
-            .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-            .setResultGrouping(
-                SearchSpec.GROUPING_TYPE_PER_NAMESPACE | SearchSpec.GROUPING_TYPE_PER_SCHEMA
-                    | SearchSpec.GROUPING_TYPE_PER_PACKAGE, /*resultLimit=*/ 1)
-            .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail2);
-
-        // Query with per namespace, per package and per schema type result grouping. Only the last
-        // two documents in each namespace should be returned.('doc3', 'doc2', 'doc1', 'email5',
-        // 'email4', 'email2', 'email1')
-        searchResults = mDb1.search("body", new SearchSpec.Builder()
-            .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-            .setResultGrouping(
-                SearchSpec.GROUPING_TYPE_PER_NAMESPACE | SearchSpec.GROUPING_TYPE_PER_SCHEMA
-                    | SearchSpec.GROUPING_TYPE_PER_PACKAGE, /*resultLimit=*/ 2)
-            .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).containsExactly(inDoc3, inDoc2, inDoc1, inEmail5, inEmail4, inEmail2,
-                inEmail1);
-    }
-
-    // TODO(b/258715421): move this test to cts test once we un-hide schema type grouping API.
-    @Test
-    public void testQuery_ResultGroupingLimits_SchemaGroupingNotSupported() throws Exception {
-        assumeFalse(mDb1.getFeatures()
-                .isFeatureSupported(Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA));
-        // Schema registration
-        mDb1.setSchemaAsync(new SetSchemaRequest.Builder()
-            .addSchemas(AppSearchEmail.SCHEMA).build()).get();
-
-        // Index four documents.
-        AppSearchEmail inEmail1 =
-                new AppSearchEmail.Builder("namespace1", "id1")
-                .setFrom("[email protected]")
-                .setTo("[email protected]", "[email protected]")
-                .setSubject("testPut example")
-                .setBody("This is the body of the testPut email")
-                .build();
-        checkIsBatchResultSuccess(mDb1.putAsync(
-                new PutDocumentsRequest.Builder().addGenericDocuments(inEmail1).build()));
-        AppSearchEmail inEmail2 =
-                new AppSearchEmail.Builder("namespace1", "id2")
-                .setFrom("[email protected]")
-                .setTo("[email protected]", "[email protected]")
-                .setSubject("testPut example")
-                .setBody("This is the body of the testPut email")
-                .build();
-        checkIsBatchResultSuccess(mDb1.putAsync(
-                new PutDocumentsRequest.Builder().addGenericDocuments(inEmail2).build()));
-        AppSearchEmail inEmail3 =
-                new AppSearchEmail.Builder("namespace2", "id3")
-                .setFrom("[email protected]")
-                .setTo("[email protected]", "[email protected]")
-                .setSubject("testPut example")
-                .setBody("This is the body of the testPut email")
-                .build();
-        checkIsBatchResultSuccess(mDb1.putAsync(
-                new PutDocumentsRequest.Builder().addGenericDocuments(inEmail3).build()));
-        AppSearchEmail inEmail4 =
-                new AppSearchEmail.Builder("namespace2", "id4")
-                .setFrom("[email protected]")
-                .setTo("[email protected]", "[email protected]")
-                .setSubject("testPut example")
-                .setBody("This is the body of the testPut email")
-                .build();
-        checkIsBatchResultSuccess(mDb1.putAsync(
-                new PutDocumentsRequest.Builder().addGenericDocuments(inEmail4).build()));
-
-        // SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA is not supported.
-        // UnsupportedOperationException will be thrown.
-        SearchSpec searchSpec1 = new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .setResultGrouping(SearchSpec.GROUPING_TYPE_PER_SCHEMA, /*resultLimit=*/ 1)
-                .build();
-        UnsupportedOperationException exception = assertThrows(UnsupportedOperationException.class,
-                () -> mDb1.search("body", searchSpec1));
-        assertThat(exception).hasMessageThat().contains(
-                Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA + " is not available on this"
-                + " AppSearch implementation.");
-
-        // SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA is not supported.
-        // UnsupportedOperationException will be thrown.
-        SearchSpec searchSpec2 = new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .setResultGrouping(SearchSpec.GROUPING_TYPE_PER_PACKAGE
-                | SearchSpec.GROUPING_TYPE_PER_SCHEMA, /*resultLimit=*/ 1)
-                .build();
-        exception = assertThrows(UnsupportedOperationException.class,
-            () -> mDb1.search("body", searchSpec2));
-        assertThat(exception).hasMessageThat().contains(
-                Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA + " is not available on this"
-                + " AppSearch implementation.");
-
-        // SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA is not supported.
-        // UnsupportedOperationException will be thrown.
-        SearchSpec searchSpec3 = new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .setResultGrouping(SearchSpec.GROUPING_TYPE_PER_NAMESPACE
-                | SearchSpec.GROUPING_TYPE_PER_SCHEMA, /*resultLimit=*/ 1)
-                .build();
-        exception = assertThrows(UnsupportedOperationException.class,
-                () -> mDb1.search("body", searchSpec3));
-        assertThat(exception).hasMessageThat().contains(
-                Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA + " is not available on this"
-                + " AppSearch implementation.");
-
-        // SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA is not supported.
-        // UnsupportedOperationException will be thrown.
-        SearchSpec searchSpec4 = new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .setResultGrouping(SearchSpec.GROUPING_TYPE_PER_NAMESPACE
-                | SearchSpec.GROUPING_TYPE_PER_SCHEMA
-                | SearchSpec.GROUPING_TYPE_PER_PACKAGE, /*resultLimit=*/ 1)
-                .build();
-        exception = assertThrows(UnsupportedOperationException.class,
-                () -> mDb1.search("body", searchSpec4));
-        assertThat(exception).hasMessageThat().contains(
-                Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA + " is not available on this"
-                + " AppSearch implementation.");
-    }
-
     // TODO(b/268521214): Move test to cts once deletion propagation is available in framework.
     @Test
     public void testGetSchema_joinableValueType() throws Exception {
         assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.JOIN_SPEC_AND_QUALIFIED_ID));
-        assumeTrue(mDb1.getFeatures().isFeatureSupported(
-                Features.SCHEMA_SET_DELETION_PROPAGATION));
-        AppSearchSchema inSchema = new AppSearchSchema.Builder("Test")
-                .addProperty(new StringPropertyConfig.Builder("normalStr")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .build()
-                ).addProperty(new StringPropertyConfig.Builder("optionalQualifiedIdStr")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setJoinableValueType(StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID)
-                        .build()
-                ).addProperty(new StringPropertyConfig.Builder("requiredQualifiedIdStr")
-                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                        .setJoinableValueType(StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID)
-                        .setDeletionPropagation(true)
-                        .build()
-                ).build();
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_SET_DELETION_PROPAGATION));
+        AppSearchSchema inSchema =
+                new AppSearchSchema.Builder("Test")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("normalStr")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("optionalQualifiedIdStr")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setJoinableValueType(
+                                                StringPropertyConfig
+                                                        .JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("requiredQualifiedIdStr")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setJoinableValueType(
+                                                StringPropertyConfig
+                                                        .JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+                                        // TODO(b/274157614): Export this to framework when we
+                                        //  can access hidden APIs.
+                                        // @exportToFramework:startStrip()
+                                        .setDeletionPropagation(true)
+                                        // @exportToFramework:endStrip()
+                                        .build())
+                        .build();
 
-        SetSchemaRequest request = new SetSchemaRequest.Builder()
-                .addSchemas(inSchema).build();
+        SetSchemaRequest request = new SetSchemaRequest.Builder().addSchemas(inSchema).build();
 
         mDb1.setSchemaAsync(request).get();
 
@@ -536,20 +254,1620 @@
     @Test
     public void testGetSchema_deletionPropagation_unsupported() {
         assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.JOIN_SPEC_AND_QUALIFIED_ID));
-        assumeFalse(mDb1.getFeatures().isFeatureSupported(
-                Features.SCHEMA_SET_DELETION_PROPAGATION));
-        AppSearchSchema schema = new AppSearchSchema.Builder("Test")
-                .addProperty(new StringPropertyConfig.Builder("qualifiedIdDeletionPropagation")
-                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                        .setJoinableValueType(StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID)
-                        .setDeletionPropagation(true)
-                        .build()
-                ).build();
-        SetSchemaRequest request = new SetSchemaRequest.Builder()
-                .addSchemas(schema).build();
-        Exception e = assertThrows(UnsupportedOperationException.class, () ->
-                mDb1.setSchemaAsync(request).get());
-        assertThat(e.getMessage()).isEqualTo("Setting deletion propagation is not supported "
-                + "on this AppSearch implementation.");
+        assumeFalse(
+                mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_SET_DELETION_PROPAGATION));
+        AppSearchSchema schema =
+                new AppSearchSchema.Builder("Test")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("qualifiedIdDeletionPropagation")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setJoinableValueType(
+                                                StringPropertyConfig
+                                                        .JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+                                        .setDeletionPropagation(true)
+                                        .build())
+                        .build();
+        SetSchemaRequest request = new SetSchemaRequest.Builder().addSchemas(schema).build();
+        Exception e =
+                assertThrows(
+                        UnsupportedOperationException.class,
+                        () -> mDb1.setSchemaAsync(request).get());
+        assertThat(e.getMessage())
+                .isEqualTo(
+                        "Setting deletion propagation is not supported "
+                                + "on this AppSearch implementation.");
+    }
+
+    // TODO(b/291122592): move to CTS once the APIs it uses are public
+    @Test
+    public void testGetSchema_parentTypes() throws Exception {
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE));
+        AppSearchSchema emailSchema = new AppSearchSchema.Builder("Email").build();
+        AppSearchSchema messageSchema = new AppSearchSchema.Builder("Message").build();
+        AppSearchSchema emailMessageSchema =
+                new AppSearchSchema.Builder("EmailMessage")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("sender")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("email")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("content")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .build())
+                        .addParentType("Email")
+                        .addParentType("Message")
+                        .build();
+
+        SetSchemaRequest request =
+                new SetSchemaRequest.Builder()
+                        .addSchemas(emailMessageSchema)
+                        .addSchemas(emailSchema)
+                        .addSchemas(messageSchema)
+                        .build();
+
+        mDb1.setSchemaAsync(request).get();
+
+        Set<AppSearchSchema> actual = mDb1.getSchemaAsync().get().getSchemas();
+        assertThat(actual).hasSize(3);
+        assertThat(actual).isEqualTo(request.getSchemas());
+    }
+
+    // TODO(b/291122592): move to CTS once the APIs it uses are public
+    @Test
+    public void testGetSchema_parentTypes_notSupported() throws Exception {
+        assumeFalse(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE));
+        AppSearchSchema emailSchema = new AppSearchSchema.Builder("Email").build();
+        AppSearchSchema messageSchema = new AppSearchSchema.Builder("Message").build();
+        AppSearchSchema emailMessageSchema =
+                new AppSearchSchema.Builder("EmailMessage")
+                        .addParentType("Email")
+                        .addParentType("Message")
+                        .build();
+
+        SetSchemaRequest request =
+                new SetSchemaRequest.Builder()
+                        .addSchemas(emailMessageSchema)
+                        .addSchemas(emailSchema)
+                        .addSchemas(messageSchema)
+                        .build();
+
+        UnsupportedOperationException e =
+                assertThrows(
+                        UnsupportedOperationException.class,
+                        () -> mDb1.setSchemaAsync(request).get());
+        assertThat(e)
+                .hasMessageThat()
+                .contains(
+                        Features.SCHEMA_ADD_PARENT_TYPE
+                                + " is not available on this AppSearch implementation.");
+    }
+
+    // TODO(b/291122592): move to CTS once the APIs it uses are public
+    @Test
+    public void testSetSchema_dataTypeIncompatibleWithParentTypes() throws Exception {
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE));
+        AppSearchSchema messageSchema =
+                new AppSearchSchema.Builder("Message")
+                        .addProperty(
+                                new AppSearchSchema.LongPropertyConfig.Builder("sender")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .build())
+                        .build();
+        AppSearchSchema emailSchema =
+                new AppSearchSchema.Builder("Email")
+                        .addParentType("Message")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("sender")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .build())
+                        .build();
+
+        SetSchemaRequest request =
+                new SetSchemaRequest.Builder()
+                        .addSchemas(messageSchema)
+                        .addSchemas(emailSchema)
+                        .build();
+
+        ExecutionException executionException =
+                assertThrows(ExecutionException.class, () -> mDb1.setSchemaAsync(request).get());
+        assertThat(executionException).hasCauseThat().isInstanceOf(AppSearchException.class);
+        AppSearchException exception = (AppSearchException) executionException.getCause();
+        assertThat(exception.getResultCode()).isEqualTo(RESULT_INVALID_ARGUMENT);
+        assertThat(exception)
+                .hasMessageThat()
+                .containsMatch(
+                        "Property sender from child type .*\\$/Email is not compatible"
+                                + " to the parent type .*\\$/Message.");
+    }
+
+    // TODO(b/291122592): move to CTS once the APIs it uses are public
+    @Test
+    public void testSetSchema_documentTypeIncompatibleWithParentTypes() throws Exception {
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE));
+        AppSearchSchema personSchema = new AppSearchSchema.Builder("Person").build();
+        AppSearchSchema artistSchema =
+                new AppSearchSchema.Builder("Artist").addParentType("Person").build();
+        AppSearchSchema messageSchema =
+                new AppSearchSchema.Builder("Message")
+                        .addProperty(
+                                new AppSearchSchema.DocumentPropertyConfig.Builder(
+                                                "sender", "Artist")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .build())
+                        .build();
+        AppSearchSchema emailSchema =
+                new AppSearchSchema.Builder("Email")
+                        .addParentType("Message")
+                        // "sender" is defined as an Artist in the parent type Message, which
+                        // requires "sender"'s type here to be a subtype of Artist. Thus, this is
+                        // incompatible because Person is not a subtype of Artist.
+                        .addProperty(
+                                new AppSearchSchema.DocumentPropertyConfig.Builder(
+                                                "sender", "Person")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .build())
+                        .build();
+
+        SetSchemaRequest request =
+                new SetSchemaRequest.Builder()
+                        .addSchemas(personSchema)
+                        .addSchemas(artistSchema)
+                        .addSchemas(messageSchema)
+                        .addSchemas(emailSchema)
+                        .build();
+
+        ExecutionException executionException =
+                assertThrows(ExecutionException.class, () -> mDb1.setSchemaAsync(request).get());
+        assertThat(executionException).hasCauseThat().isInstanceOf(AppSearchException.class);
+        AppSearchException exception = (AppSearchException) executionException.getCause();
+        assertThat(exception.getResultCode()).isEqualTo(RESULT_INVALID_ARGUMENT);
+        assertThat(exception)
+                .hasMessageThat()
+                .containsMatch(
+                        "Property sender from child type .*\\$/Email is not compatible"
+                                + " to the parent type .*\\$/Message.");
+    }
+
+    // TODO(b/291122592): move to CTS once the APIs it uses are public
+    @Test
+    public void testSetSchema_compatibleWithParentTypes() throws Exception {
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE));
+        AppSearchSchema personSchema = new AppSearchSchema.Builder("Person").build();
+        AppSearchSchema artistSchema =
+                new AppSearchSchema.Builder("Artist").addParentType("Person").build();
+        AppSearchSchema messageSchema =
+                new AppSearchSchema.Builder("Message")
+                        .addProperty(
+                                new AppSearchSchema.DocumentPropertyConfig.Builder(
+                                                "sender", "Person")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("note")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .build();
+        AppSearchSchema emailSchema =
+                new AppSearchSchema.Builder("Email")
+                        .addParentType("Message")
+                        .addProperty(
+                                // Artist is a subtype of Person, so compatible
+                                new AppSearchSchema.DocumentPropertyConfig.Builder(
+                                                "sender", "Artist")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("note")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        // A different indexing or tokenizer type is ok.
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(
+                                                StringPropertyConfig.TOKENIZER_TYPE_VERBATIM)
+                                        .build())
+                        .build();
+
+        SetSchemaRequest request =
+                new SetSchemaRequest.Builder()
+                        .addSchemas(personSchema)
+                        .addSchemas(artistSchema)
+                        .addSchemas(messageSchema)
+                        .addSchemas(emailSchema)
+                        .build();
+
+        mDb1.setSchemaAsync(request).get();
+
+        Set<AppSearchSchema> actual = mDb1.getSchemaAsync().get().getSchemas();
+        assertThat(actual).hasSize(4);
+        assertThat(actual).isEqualTo(request.getSchemas());
+    }
+
+    // TODO(b/291122592): move to CTS once the APIs it uses are public
+    @Test
+    public void testQuery_typeFilterWithPolymorphism() throws Exception {
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE));
+
+        // Schema registration
+        AppSearchSchema personSchema =
+                new AppSearchSchema.Builder("Person")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("name")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .build())
+                        .build();
+        AppSearchSchema artistSchema =
+                new AppSearchSchema.Builder("Artist")
+                        .addParentType("Person")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("name")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .build())
+                        .build();
+        mDb1.setSchemaAsync(
+                        new SetSchemaRequest.Builder()
+                                .addSchemas(personSchema)
+                                .addSchemas(artistSchema)
+                                .addSchemas(AppSearchEmail.SCHEMA)
+                                .build())
+                .get();
+
+        // Index some documents
+        GenericDocument personDoc =
+                new GenericDocument.Builder<>("namespace", "id1", "Person")
+                        .setPropertyString("name", "Foo")
+                        .build();
+        GenericDocument artistDoc =
+                new GenericDocument.Builder<>("namespace", "id2", "Artist")
+                        .setPropertyString("name", "Foo")
+                        .build();
+        AppSearchEmail emailDoc =
+                new AppSearchEmail.Builder("namespace", "id3")
+                        .setFrom("[email protected]")
+                        .setTo("[email protected]", "[email protected]")
+                        .setSubject("testPut example")
+                        .setBody("Foo")
+                        .build();
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                        new PutDocumentsRequest.Builder()
+                                .addGenericDocuments(personDoc, artistDoc, emailDoc)
+                                .build()));
+
+        // Query for the documents
+        SearchResults searchResults =
+                mDb1.search(
+                        "Foo",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .build());
+        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents).hasSize(3);
+        assertThat(documents).containsExactly(personDoc, artistDoc, emailDoc);
+
+        // Query with a filter for the "Person" type should also include the "Artist" type.
+        searchResults =
+                mDb1.search(
+                        "Foo",
+                        new SearchSpec.Builder()
+                                .addFilterSchemas("Person")
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .build());
+        documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents).hasSize(2);
+        assertThat(documents).containsExactly(personDoc, artistDoc);
+
+        // Query with a filters for the "Artist" type should not include the "Person" type.
+        searchResults =
+                mDb1.search(
+                        "Foo",
+                        new SearchSpec.Builder()
+                                .addFilterSchemas("Artist")
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .build());
+        documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents).hasSize(1);
+        assertThat(documents).containsExactly(artistDoc);
+    }
+
+    // TODO(b/291122592): move to CTS once the APIs it uses are public
+    @Test
+    public void testQuery_projectionWithPolymorphism() throws Exception {
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE));
+
+        // Schema registration
+        AppSearchSchema personSchema =
+                new AppSearchSchema.Builder("Person")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("name")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("emailAddress")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .build())
+                        .build();
+        AppSearchSchema artistSchema =
+                new AppSearchSchema.Builder("Artist")
+                        .addParentType("Person")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("name")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("emailAddress")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("company")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .build())
+                        .build();
+        mDb1.setSchemaAsync(
+                        new SetSchemaRequest.Builder()
+                                .addSchemas(personSchema)
+                                .addSchemas(artistSchema)
+                                .build())
+                .get();
+
+        // Index two documents
+        GenericDocument personDoc =
+                new GenericDocument.Builder<>("namespace", "id1", "Person")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyString("name", "Foo Person")
+                        .setPropertyString("emailAddress", "[email protected]")
+                        .build();
+        GenericDocument artistDoc =
+                new GenericDocument.Builder<>("namespace", "id2", "Artist")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyString("name", "Foo Artist")
+                        .setPropertyString("emailAddress", "[email protected]")
+                        .setPropertyString("company", "Company")
+                        .build();
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                        new PutDocumentsRequest.Builder()
+                                .addGenericDocuments(personDoc, artistDoc)
+                                .build()));
+
+        // Query with type property paths {"Person", ["name"]}, {"Artist", ["emailAddress"]}
+        // This will be expanded to paths {"Person", ["name"]}, {"Artist", ["name", "emailAddress"]}
+        // via polymorphism.
+        SearchResults searchResults =
+                mDb1.search(
+                        "Foo",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .addProjection("Person", ImmutableList.of("name"))
+                                .addProjection("Artist", ImmutableList.of("emailAddress"))
+                                .build());
+        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
+
+        // The person document should have been returned with only the "name" property. The artist
+        // document should have been returned with all of its properties.
+        GenericDocument expectedPerson =
+                new GenericDocument.Builder<>("namespace", "id1", "Person")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyString("name", "Foo Person")
+                        .build();
+        GenericDocument expectedArtist =
+                new GenericDocument.Builder<>("namespace", "id2", "Artist")
+                        .setCreationTimestampMillis(1000)
+                        .setPropertyString("name", "Foo Artist")
+                        .setPropertyString("emailAddress", "[email protected]")
+                        .build();
+        assertThat(documents).containsExactly(expectedPerson, expectedArtist);
+    }
+
+    // TODO(b/291122592): move to CTS once the APIs it uses are public
+    @Test
+    public void testQuery_indexBasedOnParentTypePolymorphism() throws Exception {
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE));
+
+        // Schema registration
+        AppSearchSchema personSchema =
+                new AppSearchSchema.Builder("Person")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("name")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .build())
+                        .build();
+        AppSearchSchema artistSchema =
+                new AppSearchSchema.Builder("Artist")
+                        .addParentType("Person")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("name")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("company")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .build())
+                        .build();
+        AppSearchSchema messageSchema =
+                new AppSearchSchema.Builder("Message")
+                        .addProperty(
+                                new AppSearchSchema.DocumentPropertyConfig.Builder(
+                                                "sender", "Person")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setShouldIndexNestedProperties(true)
+                                        .build())
+                        .build();
+        mDb1.setSchemaAsync(
+                        new SetSchemaRequest.Builder()
+                                .addSchemas(personSchema)
+                                .addSchemas(artistSchema)
+                                .addSchemas(messageSchema)
+                                .build())
+                .get();
+
+        // Index some an artistDoc and a messageDoc
+        GenericDocument artistDoc =
+                new GenericDocument.Builder<>("namespace", "id1", "Artist")
+                        .setPropertyString("name", "Foo")
+                        .setPropertyString("company", "Bar")
+                        .build();
+        GenericDocument messageDoc =
+                new GenericDocument.Builder<>("namespace", "id2", "Message")
+                        // sender is defined as a Person, which accepts an Artist because Artist <:
+                        // Person.
+                        // However, indexing will be based on what's defined in Person, so the
+                        // "company"
+                        // property in artistDoc cannot be used to search this messageDoc.
+                        .setPropertyDocument("sender", artistDoc)
+                        .build();
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                        new PutDocumentsRequest.Builder()
+                                .addGenericDocuments(artistDoc, messageDoc)
+                                .build()));
+
+        // Query for the documents
+        SearchResults searchResults =
+                mDb1.search(
+                        "Foo",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .build());
+        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents).hasSize(2);
+        assertThat(documents).containsExactly(artistDoc, messageDoc);
+
+        // The "company" property in artistDoc cannot be used to search messageDoc.
+        searchResults =
+                mDb1.search(
+                        "Bar",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .build());
+        documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents).hasSize(1);
+        assertThat(documents).containsExactly(artistDoc);
+    }
+
+    // TODO(b/291122592): move to CTS once the APIs it uses are public
+    @Test
+    public void testSetSchema_indexableNestedPropsList() throws Exception {
+        assumeTrue(
+                mDb1.getFeatures()
+                        .isFeatureSupported(Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES));
+
+        AppSearchSchema personSchema =
+                new AppSearchSchema.Builder("Person")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("name")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.DocumentPropertyConfig.Builder(
+                                                "worksFor", "Organization")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setShouldIndexNestedProperties(false)
+                                        .addIndexableNestedProperties("name")
+                                        .build())
+                        .build();
+        AppSearchSchema organizationSchema =
+                new AppSearchSchema.Builder("Organization")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("name")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("notes")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .build();
+
+        mDb1.setSchemaAsync(
+                        new SetSchemaRequest.Builder()
+                                .addSchemas(personSchema, organizationSchema)
+                                .build())
+                .get();
+
+        // Test that properties in Person's indexable_nested_properties_list are indexed and
+        // searchable
+        GenericDocument org1 =
+                new GenericDocument.Builder<>("namespace", "org1", "Organization")
+                        .setPropertyString("name", "Org1")
+                        .setPropertyString("notes", "Some notes")
+                        .build();
+        GenericDocument person1 =
+                new GenericDocument.Builder<>("namespace", "person1", "Person")
+                        .setPropertyString("name", "Jane")
+                        .setPropertyDocument("worksFor", org1)
+                        .build();
+
+        AppSearchBatchResult<String, Void> putResult =
+                checkIsBatchResultSuccess(
+                        mDb1.putAsync(
+                                new PutDocumentsRequest.Builder()
+                                        .addGenericDocuments(person1, org1)
+                                        .build()));
+        assertThat(putResult.getSuccesses()).containsExactly("person1", null, "org1", null);
+        assertThat(putResult.getFailures()).isEmpty();
+
+        GetByDocumentIdRequest getByDocumentIdRequest =
+                new GetByDocumentIdRequest.Builder("namespace").addIds("person1", "org1").build();
+        List<GenericDocument> outDocuments = doGet(mDb1, getByDocumentIdRequest);
+        assertThat(outDocuments).hasSize(2);
+        assertThat(outDocuments).containsExactly(person1, org1);
+
+        // Both org1 and person should be returned for query "Org1"
+        // For org1 this matches the 'name' property and for person1 this matches the
+        // 'worksFor.name' property.
+        SearchResults searchResults =
+                mDb1.search(
+                        "Org1",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .build());
+        outDocuments = convertSearchResultsToDocuments(searchResults);
+        assertThat(outDocuments).hasSize(2);
+        assertThat(outDocuments).containsExactly(person1, org1);
+
+        // Only org1 should be returned for query "notes", since 'worksFor.notes' is not indexed
+        // for the Person-type.
+        searchResults =
+                mDb1.search(
+                        "notes",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .build());
+        outDocuments = convertSearchResultsToDocuments(searchResults);
+        assertThat(outDocuments).hasSize(1);
+        assertThat(outDocuments).containsExactly(org1);
+    }
+
+    // TODO(b/291122592): move to CTS once the APIs it uses are public
+    @Test
+    public void testSetSchema_indexableNestedPropsList_notSupported() throws Exception {
+        assumeFalse(
+                mDb1.getFeatures()
+                        .isFeatureSupported(Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES));
+
+        AppSearchSchema personSchema =
+                new AppSearchSchema.Builder("Person")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("name")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.DocumentPropertyConfig.Builder(
+                                                "worksFor", "Organization")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setShouldIndexNestedProperties(false)
+                                        .addIndexableNestedProperties("name")
+                                        .build())
+                        .build();
+        AppSearchSchema organizationSchema =
+                new AppSearchSchema.Builder("Organization")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("name")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("notes")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .build();
+
+        SetSchemaRequest setSchemaRequest =
+                new SetSchemaRequest.Builder().addSchemas(personSchema, organizationSchema).build();
+        UnsupportedOperationException e =
+                assertThrows(
+                        UnsupportedOperationException.class,
+                        () -> mDb1.setSchemaAsync(setSchemaRequest).get());
+        assertThat(e)
+                .hasMessageThat()
+                .contains(
+                        "DocumentPropertyConfig.addIndexableNestedProperties is not supported on"
+                            + " this AppSearch implementation.");
+    }
+
+    // TODO(b/291122592): move to CTS once the APIs it uses are public
+    @Test
+    public void testSetSchema_indexableNestedPropsList_nonIndexableProp() throws Exception {
+        assumeTrue(
+                mDb1.getFeatures()
+                        .isFeatureSupported(Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES));
+
+        AppSearchSchema personSchema =
+                new AppSearchSchema.Builder("Person")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("name")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.DocumentPropertyConfig.Builder(
+                                                "worksFor", "Organization")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setShouldIndexNestedProperties(false)
+                                        .addIndexableNestedProperties("name")
+                                        .build())
+                        .build();
+        AppSearchSchema organizationSchema =
+                new AppSearchSchema.Builder("Organization")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("name")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("notes")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_NONE)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_NONE)
+                                        .build())
+                        .build();
+
+        mDb1.setSchemaAsync(
+                        new SetSchemaRequest.Builder()
+                                .addSchemas(personSchema, organizationSchema)
+                                .build())
+                .get();
+
+        // Test that Person's nested properties are indexed correctly.
+        GenericDocument org1 =
+                new GenericDocument.Builder<>("namespace", "org1", "Organization")
+                        .setPropertyString("name", "Org1")
+                        .setPropertyString("notes", "Some notes")
+                        .build();
+        GenericDocument person1 =
+                new GenericDocument.Builder<>("namespace", "person1", "Person")
+                        .setPropertyString("name", "Jane")
+                        .setPropertyDocument("worksFor", org1)
+                        .build();
+
+        AppSearchBatchResult<String, Void> putResult =
+                checkIsBatchResultSuccess(
+                        mDb1.putAsync(
+                                new PutDocumentsRequest.Builder()
+                                        .addGenericDocuments(person1, org1)
+                                        .build()));
+        assertThat(putResult.getSuccesses()).containsExactly("person1", null, "org1", null);
+        assertThat(putResult.getFailures()).isEmpty();
+
+        GetByDocumentIdRequest getByDocumentIdRequest =
+                new GetByDocumentIdRequest.Builder("namespace").addIds("person1", "org1").build();
+        List<GenericDocument> outDocuments = doGet(mDb1, getByDocumentIdRequest);
+        assertThat(outDocuments).hasSize(2);
+        assertThat(outDocuments).containsExactly(person1, org1);
+
+        // Both org1 and person should be returned for query "Org1"
+        // For org1 this matches the 'name' property and for person1 this matches the
+        // 'worksFor.name' property.
+        SearchResults searchResults =
+                mDb1.search(
+                        "Org1",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .build());
+        outDocuments = convertSearchResultsToDocuments(searchResults);
+        assertThat(outDocuments).hasSize(2);
+        assertThat(outDocuments).containsExactly(person1, org1);
+
+        // No documents should match for "notes", since both 'Organization:notes'
+        // and 'Person:worksFor.notes' are non-indexable.
+        searchResults =
+                mDb1.search(
+                        "notes",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .build());
+        outDocuments = convertSearchResultsToDocuments(searchResults);
+        assertThat(outDocuments).hasSize(0);
+    }
+
+    // TODO(b/291122592): move to CTS once the APIs it uses are public
+    @Test
+    public void testSetSchema_indexableNestedPropsList_multipleNestedLevels() throws Exception {
+        assumeTrue(
+                mDb1.getFeatures()
+                        .isFeatureSupported(Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES));
+
+        AppSearchSchema emailSchema =
+                new AppSearchSchema.Builder("Email")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("subject")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.DocumentPropertyConfig.Builder(
+                                                "sender", "Person")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
+                                        .setShouldIndexNestedProperties(false)
+                                        .addIndexableNestedProperties(
+                                                "name", "worksFor.name", "worksFor.notes")
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.DocumentPropertyConfig.Builder(
+                                                "recipient", "Person")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
+                                        .setShouldIndexNestedProperties(true)
+                                        .build())
+                        .build();
+        AppSearchSchema personSchema =
+                new AppSearchSchema.Builder("Person")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("name")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("age")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.DocumentPropertyConfig.Builder(
+                                                "worksFor", "Organization")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setShouldIndexNestedProperties(false)
+                                        .addIndexableNestedProperties("name", "id")
+                                        .build())
+                        .build();
+        AppSearchSchema organizationSchema =
+                new AppSearchSchema.Builder("Organization")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("name")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("notes")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("id")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .build();
+
+        mDb1.setSchemaAsync(
+                        new SetSchemaRequest.Builder()
+                                .addSchemas(emailSchema, personSchema, organizationSchema)
+                                .build())
+                .get();
+
+        // Test that Email and Person's nested properties are indexed correctly.
+        GenericDocument org1 =
+                new GenericDocument.Builder<>("namespace", "org1", "Organization")
+                        .setPropertyString("name", "Org1")
+                        .setPropertyString("notes", "Some notes")
+                        .setPropertyString("id", "1234")
+                        .build();
+        GenericDocument person1 =
+                new GenericDocument.Builder<>("namespace", "person1", "Person")
+                        .setPropertyString("name", "Jane")
+                        .setPropertyString("age", "20")
+                        .setPropertyDocument("worksFor", org1)
+                        .build();
+        GenericDocument person2 =
+                new GenericDocument.Builder<>("namespace", "person2", "Person")
+                        .setPropertyString("name", "John")
+                        .setPropertyString("age", "30")
+                        .setPropertyDocument("worksFor", org1)
+                        .build();
+        GenericDocument email1 =
+                new GenericDocument.Builder<>("namespace", "email1", "Email")
+                        .setPropertyString("subject", "Greetings!")
+                        .setPropertyDocument("sender", person1)
+                        .setPropertyDocument("recipient", person2)
+                        .build();
+        AppSearchBatchResult<String, Void> putResult =
+                checkIsBatchResultSuccess(
+                        mDb1.putAsync(
+                                new PutDocumentsRequest.Builder()
+                                        .addGenericDocuments(person1, org1, person2, email1)
+                                        .build()));
+        assertThat(putResult.getSuccesses())
+                .containsExactly("person1", null, "org1", null, "person2", null, "email1", null);
+        assertThat(putResult.getFailures()).isEmpty();
+
+        GetByDocumentIdRequest getByDocumentIdRequest =
+                new GetByDocumentIdRequest.Builder("namespace")
+                        .addIds("person1", "org1", "person2", "email1")
+                        .build();
+        List<GenericDocument> outDocuments = doGet(mDb1, getByDocumentIdRequest);
+        assertThat(outDocuments).hasSize(4);
+        assertThat(outDocuments).containsExactly(person1, org1, person2, email1);
+
+        // Indexed properties:
+        // Email: 'subject', 'sender.name', 'sender.worksFor.name', 'sender.worksFor.notes',
+        //        'recipient.name', 'recipient.age', 'recipient.worksFor.name',
+        //        'recipient.worksFor.id'
+        //        (Email:recipient sets index_nested_props=true, so it follows the same indexing
+        //         configs as the next schema-type level (person))
+        // Person: 'name', 'age', 'worksFor.name', 'worksFor.id'
+        // Organization: 'name', 'notes', 'id'
+        //
+        // All documents should be returned for query 'Org1' because all schemaTypes index the
+        // 'Organization:name' property.
+        SearchResults searchResults =
+                mDb1.search(
+                        "Org1",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .build());
+        outDocuments = convertSearchResultsToDocuments(searchResults);
+        assertThat(outDocuments).hasSize(4);
+        assertThat(outDocuments).containsExactly(person1, org1, person2, email1);
+
+        // org1 and email1 should be returned for query 'notes'
+        searchResults =
+                mDb1.search(
+                        "notes",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .build());
+        outDocuments = convertSearchResultsToDocuments(searchResults);
+        assertThat(outDocuments).hasSize(2);
+        assertThat(outDocuments).containsExactly(org1, email1);
+
+        // all docs should be returned for query "1234"
+        searchResults =
+                mDb1.search(
+                        "1234",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .build());
+        outDocuments = convertSearchResultsToDocuments(searchResults);
+        assertThat(outDocuments).hasSize(4);
+        assertThat(outDocuments).containsExactly(person1, org1, person2, email1);
+
+        // email1 should be returned for query "30", but not for "20" since sender.age is not
+        // indexed, but recipient.age is.
+        // For query "30", person2 should also be returned
+        // For query "20, person1 should be returned.
+        searchResults =
+                mDb1.search(
+                        "30",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .build());
+        outDocuments = convertSearchResultsToDocuments(searchResults);
+        assertThat(outDocuments).hasSize(2);
+        assertThat(outDocuments).containsExactly(person2, email1);
+
+        searchResults =
+                mDb1.search(
+                        "20",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .build());
+        outDocuments = convertSearchResultsToDocuments(searchResults);
+        assertThat(outDocuments).hasSize(1);
+        assertThat(outDocuments).containsExactly(person1);
+    }
+
+    // TODO(b/291122592): move to CTS once the APIs it uses are public
+    @Test
+    public void testSetSchema_indexableNestedPropsList_circularRefs() throws Exception {
+        assumeTrue(
+                mDb1.getFeatures()
+                        .isFeatureSupported(Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES));
+        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SET_SCHEMA_CIRCULAR_REFERENCES));
+
+        // Create schema with valid cycle: Person -> Organization -> Person...
+        AppSearchSchema personSchema =
+                new AppSearchSchema.Builder("Person")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("name")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("address")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.DocumentPropertyConfig.Builder(
+                                                "worksFor", "Organization")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setShouldIndexNestedProperties(false)
+                                        .addIndexableNestedProperties(
+                                                "name", "notes", "funder.name")
+                                        .build())
+                        .build();
+        AppSearchSchema organizationSchema =
+                new AppSearchSchema.Builder("Organization")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("name")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new StringPropertyConfig.Builder("notes")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .addProperty(
+                                new AppSearchSchema.DocumentPropertyConfig.Builder(
+                                                "funder", "Person")
+                                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
+                                        .setShouldIndexNestedProperties(false)
+                                        .addIndexableNestedProperties(
+                                                "name",
+                                                "worksFor.name",
+                                                "worksFor.funder.address",
+                                                "worksFor.funder.worksFor.notes")
+                                        .build())
+                        .build();
+        mDb1.setSchemaAsync(
+                        new SetSchemaRequest.Builder()
+                                .addSchemas(personSchema, organizationSchema)
+                                .build())
+                .get();
+
+        // Test that documents following the circular schema are indexed correctly, and that its
+        // sections are searchable
+        GenericDocument person1 =
+                new GenericDocument.Builder<>("namespace", "person1", "Person")
+                        .setPropertyString("name", "Person1")
+                        .setPropertyString("address", "someAddress")
+                        .build();
+        GenericDocument org1 =
+                new GenericDocument.Builder<>("namespace", "org1", "Organization")
+                        .setPropertyString("name", "Org1")
+                        .setPropertyString("notes", "someNote")
+                        .setPropertyDocument("funder", person1)
+                        .build();
+        GenericDocument person2 =
+                new GenericDocument.Builder<>("namespace", "person2", "Person")
+                        .setPropertyString("name", "Person2")
+                        .setPropertyString("address", "anotherAddress")
+                        .setPropertyDocument("worksFor", org1)
+                        .build();
+        GenericDocument org2 =
+                new GenericDocument.Builder<>("namespace", "org2", "Organization")
+                        .setPropertyString("name", "Org2")
+                        .setPropertyString("notes", "anotherNote")
+                        .setPropertyDocument("funder", person2)
+                        .build();
+
+        AppSearchBatchResult<String, Void> putResult =
+                checkIsBatchResultSuccess(
+                        mDb1.putAsync(
+                                new PutDocumentsRequest.Builder()
+                                        .addGenericDocuments(person1, org1, person2, org2)
+                                        .build()));
+        assertThat(putResult.getSuccesses())
+                .containsExactly("person1", null, "org1", null, "person2", null, "org2", null);
+        assertThat(putResult.getFailures()).isEmpty();
+
+        GetByDocumentIdRequest getByDocumentIdRequest =
+                new GetByDocumentIdRequest.Builder("namespace")
+                        .addIds("person1", "person2", "org1", "org2")
+                        .build();
+        List<GenericDocument> outDocuments = doGet(mDb1, getByDocumentIdRequest);
+        assertThat(outDocuments).hasSize(4);
+        assertThat(outDocuments).containsExactly(person1, person2, org1, org2);
+
+        // Indexed properties:
+        // Person: 'name', 'address', 'worksFor.name', 'worksFor.notes', 'worksFor.funder.name'
+        // Organization: 'name', 'notes', 'funder.name', 'funder.worksFor.name',
+        //               'funder.worksFor.funder.address', 'funder.worksFor.funder.worksFor.notes'
+        //
+        // "Person1" should match person1 (name), org1 (funder.name) and person2
+        // (worksFor.funder.name)
+        SearchResults searchResults =
+                mDb1.search(
+                        "Person1",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .build());
+        outDocuments = convertSearchResultsToDocuments(searchResults);
+        assertThat(outDocuments).hasSize(3);
+        assertThat(outDocuments).containsExactly(person1, org1, person2);
+
+        // "someAddress" should match person1 (address) and org2 (funder.worksFor.funder.address)
+        searchResults =
+                mDb1.search(
+                        "someAddress",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .build());
+        outDocuments = convertSearchResultsToDocuments(searchResults);
+        assertThat(outDocuments).hasSize(2);
+        assertThat(outDocuments).containsExactly(person1, org2);
+
+        // "Org1" should match org1 (name), person2 (worksFor.name) and org2 (funder.worksFor.name)
+        searchResults =
+                mDb1.search(
+                        "Org1",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .build());
+        outDocuments = convertSearchResultsToDocuments(searchResults);
+        assertThat(outDocuments).hasSize(3);
+        assertThat(outDocuments).containsExactly(org1, person2, org2);
+
+        // "someNote" should match org1 (notes) and person2 (worksFor.notes)
+        searchResults =
+                mDb1.search(
+                        "someNote",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .build());
+        outDocuments = convertSearchResultsToDocuments(searchResults);
+        assertThat(outDocuments).hasSize(2);
+        assertThat(outDocuments).containsExactly(org1, person2);
+
+        // "Person2" should match person2 (name), org2 (funder.name)
+        searchResults =
+                mDb1.search(
+                        "Person2",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .build());
+        outDocuments = convertSearchResultsToDocuments(searchResults);
+        assertThat(outDocuments).hasSize(2);
+        assertThat(outDocuments).containsExactly(person2, org2);
+
+        // "anotherAddress" should match only person2 (address)
+        searchResults =
+                mDb1.search(
+                        "anotherAddress",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .build());
+        outDocuments = convertSearchResultsToDocuments(searchResults);
+        assertThat(outDocuments).hasSize(1);
+        assertThat(outDocuments).containsExactly(person2);
+
+        // "Org2" and "anotherNote" should both match only org2 (name, notes)
+        searchResults =
+                mDb1.search(
+                        "Org2",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .build());
+        outDocuments = convertSearchResultsToDocuments(searchResults);
+        assertThat(outDocuments).hasSize(1);
+        assertThat(outDocuments).containsExactly(org2);
+
+        searchResults =
+                mDb1.search(
+                        "anotherNote",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .build());
+        outDocuments = convertSearchResultsToDocuments(searchResults);
+        assertThat(outDocuments).hasSize(1);
+        assertThat(outDocuments).containsExactly(org2);
+    }
+
+    // TODO(b/291122592): move to CTS once the APIs it uses are public
+    @Test
+    public void testQuery_ResultGroupingLimits_SchemaGroupingSupported() throws Exception {
+        assumeTrue(
+                mDb1.getFeatures()
+                        .isFeatureSupported(Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA));
+        // Schema registration
+        AppSearchSchema genericSchema =
+                new AppSearchSchema.Builder("Generic")
+                        .addProperty(
+                                new StringPropertyConfig.Builder("foo")
+                                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                        .setIndexingType(
+                                                StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+                                        .build())
+                        .build();
+        mDb1.setSchemaAsync(
+                        new SetSchemaRequest.Builder()
+                                .addSchemas(AppSearchEmail.SCHEMA)
+                                .addSchemas(genericSchema)
+                                .build())
+                .get();
+
+        // Index four documents.
+        AppSearchEmail inEmail1 =
+                new AppSearchEmail.Builder("namespace1", "id1")
+                        .setFrom("[email protected]")
+                        .setTo("[email protected]", "[email protected]")
+                        .setSubject("testPut example")
+                        .setBody("This is the body of the testPut email")
+                        .build();
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                        new PutDocumentsRequest.Builder().addGenericDocuments(inEmail1).build()));
+        AppSearchEmail inEmail2 =
+                new AppSearchEmail.Builder("namespace1", "id2")
+                        .setFrom("[email protected]")
+                        .setTo("[email protected]", "[email protected]")
+                        .setSubject("testPut example")
+                        .setBody("This is the body of the testPut email")
+                        .build();
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                        new PutDocumentsRequest.Builder().addGenericDocuments(inEmail2).build()));
+        AppSearchEmail inEmail3 =
+                new AppSearchEmail.Builder("namespace2", "id3")
+                        .setFrom("[email protected]")
+                        .setTo("[email protected]", "[email protected]")
+                        .setSubject("testPut example")
+                        .setBody("This is the body of the testPut email")
+                        .build();
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                        new PutDocumentsRequest.Builder().addGenericDocuments(inEmail3).build()));
+        AppSearchEmail inEmail4 =
+                new AppSearchEmail.Builder("namespace2", "id4")
+                        .setFrom("[email protected]")
+                        .setTo("[email protected]", "[email protected]")
+                        .setSubject("testPut example")
+                        .setBody("This is the body of the testPut email")
+                        .build();
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                        new PutDocumentsRequest.Builder().addGenericDocuments(inEmail4).build()));
+        AppSearchEmail inEmail5 =
+                new AppSearchEmail.Builder("namespace2", "id5")
+                        .setFrom("[email protected]")
+                        .setTo("[email protected]", "[email protected]")
+                        .setSubject("testPut example")
+                        .setBody("This is the body of the testPut email")
+                        .build();
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                        new PutDocumentsRequest.Builder().addGenericDocuments(inEmail5).build()));
+        GenericDocument inDoc1 =
+                new GenericDocument.Builder<>("namespace3", "id6", "Generic")
+                        .setPropertyString("foo", "body")
+                        .build();
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                        new PutDocumentsRequest.Builder().addGenericDocuments(inDoc1).build()));
+        GenericDocument inDoc2 =
+                new GenericDocument.Builder<>("namespace3", "id7", "Generic")
+                        .setPropertyString("foo", "body")
+                        .build();
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                        new PutDocumentsRequest.Builder().addGenericDocuments(inDoc2).build()));
+        GenericDocument inDoc3 =
+                new GenericDocument.Builder<>("namespace4", "id8", "Generic")
+                        .setPropertyString("foo", "body")
+                        .build();
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                        new PutDocumentsRequest.Builder().addGenericDocuments(inDoc3).build()));
+
+        // Query with per package result grouping. Only the last document 'doc3' should be
+        // returned.
+        SearchResults searchResults =
+                mDb1.search(
+                        "body",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .setResultGrouping(
+                                        SearchSpec.GROUPING_TYPE_PER_PACKAGE, /* resultLimit= */ 1)
+                                .build());
+        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents).containsExactly(inDoc3);
+
+        // Query with per namespace result grouping. Only the last document in each namespace should
+        // be returned ('doc3', 'doc2', 'email5' and 'email2').
+        searchResults =
+                mDb1.search(
+                        "body",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .setResultGrouping(
+                                        SearchSpec.GROUPING_TYPE_PER_NAMESPACE,
+                                        /* resultLimit= */ 1)
+                                .build());
+        documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail2);
+
+        // Query with per namespace result grouping. Two of the last documents in each namespace
+        // should be returned ('doc3', 'doc2', 'doc1', 'email5', 'email4', 'email2', 'email1')
+        searchResults =
+                mDb1.search(
+                        "body",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .setResultGrouping(
+                                        SearchSpec.GROUPING_TYPE_PER_NAMESPACE,
+                                        /* resultLimit= */ 2)
+                                .build());
+        documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents)
+                .containsExactly(inDoc3, inDoc2, inDoc1, inEmail5, inEmail4, inEmail2, inEmail1);
+
+        // Query with per schema result grouping. Only the last document of each schema type should
+        // be returned ('doc3', 'email5')
+        searchResults =
+                mDb1.search(
+                        "body",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .setResultGrouping(
+                                        SearchSpec.GROUPING_TYPE_PER_SCHEMA, /* resultLimit= */ 1)
+                                .build());
+        documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents).containsExactly(inDoc3, inEmail5);
+
+        // Query with per schema result grouping. Only the last two documents of each schema type
+        // should be returned ('doc3', 'doc2', 'email5', 'email4')
+        searchResults =
+                mDb1.search(
+                        "body",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .setResultGrouping(
+                                        SearchSpec.GROUPING_TYPE_PER_SCHEMA, /* resultLimit= */ 2)
+                                .build());
+        documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail4);
+
+        // Query with per package and per namespace result grouping. Only the last document in each
+        // namespace should be returned ('doc3', 'doc2', 'email5' and 'email2').
+        searchResults =
+                mDb1.search(
+                        "body",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .setResultGrouping(
+                                        SearchSpec.GROUPING_TYPE_PER_NAMESPACE
+                                                | SearchSpec.GROUPING_TYPE_PER_PACKAGE,
+                                        /* resultLimit= */ 1)
+                                .build());
+        documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail2);
+
+        // Query with per package and per namespace result grouping. Only the last two documents
+        // in each namespace should be returned ('doc3', 'doc2', 'doc1', 'email5', 'email4',
+        // 'email2', 'email1')
+        searchResults =
+                mDb1.search(
+                        "body",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .setResultGrouping(
+                                        SearchSpec.GROUPING_TYPE_PER_NAMESPACE
+                                                | SearchSpec.GROUPING_TYPE_PER_PACKAGE,
+                                        /* resultLimit= */ 2)
+                                .build());
+        documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents)
+                .containsExactly(inDoc3, inDoc2, inDoc1, inEmail5, inEmail4, inEmail2, inEmail1);
+
+        // Query with per package and per schema type result grouping. Only the last document in
+        // each schema type should be returned. ('doc3', 'email5')
+        searchResults =
+                mDb1.search(
+                        "body",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .setResultGrouping(
+                                        SearchSpec.GROUPING_TYPE_PER_SCHEMA
+                                                | SearchSpec.GROUPING_TYPE_PER_PACKAGE,
+                                        /* resultLimit= */ 1)
+                                .build());
+        documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents).containsExactly(inDoc3, inEmail5);
+
+        // Query with per package and per schema type result grouping. Only the last two document in
+        // each schema type should be returned. ('doc3', 'doc2', 'email5', 'email4')
+        searchResults =
+                mDb1.search(
+                        "body",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .setResultGrouping(
+                                        SearchSpec.GROUPING_TYPE_PER_SCHEMA
+                                                | SearchSpec.GROUPING_TYPE_PER_PACKAGE,
+                                        /* resultLimit= */ 2)
+                                .build());
+        documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail4);
+
+        // Query with per namespace and per schema type result grouping. Only the last document in
+        // each namespace should be returned. ('doc3', 'doc2', 'email5' and 'email2').
+        searchResults =
+                mDb1.search(
+                        "body",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .setResultGrouping(
+                                        SearchSpec.GROUPING_TYPE_PER_NAMESPACE
+                                                | SearchSpec.GROUPING_TYPE_PER_SCHEMA,
+                                        /* resultLimit= */ 1)
+                                .build());
+        documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail2);
+
+        // Query with per namespace and per schema type result grouping. Only the last two documents
+        // in each namespace should be returned. ('doc3', 'doc2', 'doc1', 'email5', 'email4',
+        // 'email2', 'email1')
+        searchResults =
+                mDb1.search(
+                        "body",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .setResultGrouping(
+                                        SearchSpec.GROUPING_TYPE_PER_NAMESPACE
+                                                | SearchSpec.GROUPING_TYPE_PER_SCHEMA,
+                                        /* resultLimit= */ 2)
+                                .build());
+        documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents)
+                .containsExactly(inDoc3, inDoc2, inDoc1, inEmail5, inEmail4, inEmail2, inEmail1);
+
+        // Query with per namespace, per package and per schema type result grouping. Only the last
+        // document in each namespace should be returned. ('doc3', 'doc2', 'email5' and 'email2')
+        searchResults =
+                mDb1.search(
+                        "body",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .setResultGrouping(
+                                        SearchSpec.GROUPING_TYPE_PER_NAMESPACE
+                                                | SearchSpec.GROUPING_TYPE_PER_SCHEMA
+                                                | SearchSpec.GROUPING_TYPE_PER_PACKAGE,
+                                        /* resultLimit= */ 1)
+                                .build());
+        documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents).containsExactly(inDoc3, inDoc2, inEmail5, inEmail2);
+
+        // Query with per namespace, per package and per schema type result grouping. Only the last
+        // two documents in each namespace should be returned.('doc3', 'doc2', 'doc1', 'email5',
+        // 'email4', 'email2', 'email1')
+        searchResults =
+                mDb1.search(
+                        "body",
+                        new SearchSpec.Builder()
+                                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                                .setResultGrouping(
+                                        SearchSpec.GROUPING_TYPE_PER_NAMESPACE
+                                                | SearchSpec.GROUPING_TYPE_PER_SCHEMA
+                                                | SearchSpec.GROUPING_TYPE_PER_PACKAGE,
+                                        /* resultLimit= */ 2)
+                                .build());
+        documents = convertSearchResultsToDocuments(searchResults);
+        assertThat(documents)
+                .containsExactly(inDoc3, inDoc2, inDoc1, inEmail5, inEmail4, inEmail2, inEmail1);
+    }
+
+    // TODO(b/291122592): move to CTS once the APIs it uses are public
+    @Test
+    public void testQuery_ResultGroupingLimits_SchemaGroupingNotSupported() throws Exception {
+        assumeFalse(
+                mDb1.getFeatures()
+                        .isFeatureSupported(Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA));
+        // Schema registration
+        mDb1.setSchemaAsync(
+                        new SetSchemaRequest.Builder().addSchemas(AppSearchEmail.SCHEMA).build())
+                .get();
+
+        // Index four documents.
+        AppSearchEmail inEmail1 =
+                new AppSearchEmail.Builder("namespace1", "id1")
+                        .setFrom("[email protected]")
+                        .setTo("[email protected]", "[email protected]")
+                        .setSubject("testPut example")
+                        .setBody("This is the body of the testPut email")
+                        .build();
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                        new PutDocumentsRequest.Builder().addGenericDocuments(inEmail1).build()));
+        AppSearchEmail inEmail2 =
+                new AppSearchEmail.Builder("namespace1", "id2")
+                        .setFrom("[email protected]")
+                        .setTo("[email protected]", "[email protected]")
+                        .setSubject("testPut example")
+                        .setBody("This is the body of the testPut email")
+                        .build();
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                        new PutDocumentsRequest.Builder().addGenericDocuments(inEmail2).build()));
+        AppSearchEmail inEmail3 =
+                new AppSearchEmail.Builder("namespace2", "id3")
+                        .setFrom("[email protected]")
+                        .setTo("[email protected]", "[email protected]")
+                        .setSubject("testPut example")
+                        .setBody("This is the body of the testPut email")
+                        .build();
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                        new PutDocumentsRequest.Builder().addGenericDocuments(inEmail3).build()));
+        AppSearchEmail inEmail4 =
+                new AppSearchEmail.Builder("namespace2", "id4")
+                        .setFrom("[email protected]")
+                        .setTo("[email protected]", "[email protected]")
+                        .setSubject("testPut example")
+                        .setBody("This is the body of the testPut email")
+                        .build();
+        checkIsBatchResultSuccess(
+                mDb1.putAsync(
+                        new PutDocumentsRequest.Builder().addGenericDocuments(inEmail4).build()));
+
+        // SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA is not supported.
+        // UnsupportedOperationException will be thrown.
+        SearchSpec searchSpec1 =
+                new SearchSpec.Builder()
+                        .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                        .setResultGrouping(
+                                SearchSpec.GROUPING_TYPE_PER_SCHEMA, /* resultLimit= */ 1)
+                        .build();
+        UnsupportedOperationException exception =
+                assertThrows(
+                        UnsupportedOperationException.class,
+                        () -> mDb1.search("body", searchSpec1));
+        assertThat(exception)
+                .hasMessageThat()
+                .contains(
+                        Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA
+                                + " is not available on this"
+                                + " AppSearch implementation.");
+
+        // SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA is not supported.
+        // UnsupportedOperationException will be thrown.
+        SearchSpec searchSpec2 =
+                new SearchSpec.Builder()
+                        .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                        .setResultGrouping(
+                                SearchSpec.GROUPING_TYPE_PER_PACKAGE
+                                        | SearchSpec.GROUPING_TYPE_PER_SCHEMA,
+                                /* resultLimit= */ 1)
+                        .build();
+        exception =
+                assertThrows(
+                        UnsupportedOperationException.class,
+                        () -> mDb1.search("body", searchSpec2));
+        assertThat(exception)
+                .hasMessageThat()
+                .contains(
+                        Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA
+                                + " is not available on this"
+                                + " AppSearch implementation.");
+
+        // SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA is not supported.
+        // UnsupportedOperationException will be thrown.
+        SearchSpec searchSpec3 =
+                new SearchSpec.Builder()
+                        .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                        .setResultGrouping(
+                                SearchSpec.GROUPING_TYPE_PER_NAMESPACE
+                                        | SearchSpec.GROUPING_TYPE_PER_SCHEMA,
+                                /* resultLimit= */ 1)
+                        .build();
+        exception =
+                assertThrows(
+                        UnsupportedOperationException.class,
+                        () -> mDb1.search("body", searchSpec3));
+        assertThat(exception)
+                .hasMessageThat()
+                .contains(
+                        Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA
+                                + " is not available on this"
+                                + " AppSearch implementation.");
+
+        // SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA is not supported.
+        // UnsupportedOperationException will be thrown.
+        SearchSpec searchSpec4 =
+                new SearchSpec.Builder()
+                        .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                        .setResultGrouping(
+                                SearchSpec.GROUPING_TYPE_PER_NAMESPACE
+                                        | SearchSpec.GROUPING_TYPE_PER_SCHEMA
+                                        | SearchSpec.GROUPING_TYPE_PER_PACKAGE,
+                                /* resultLimit= */ 1)
+                        .build();
+        exception =
+                assertThrows(
+                        UnsupportedOperationException.class,
+                        () -> mDb1.search("body", searchSpec4));
+        assertThat(exception)
+                .hasMessageThat()
+                .contains(
+                        Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA
+                                + " is not available on this"
+                                + " AppSearch implementation.");
     }
 }
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSchemaCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSchemaCtsTest.java
index 76d7cdb..8939aa2 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSchemaCtsTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSchemaCtsTest.java
@@ -22,17 +22,13 @@
 import static org.junit.Assert.assertThrows;
 
 import androidx.appsearch.app.AppSearchSchema;
-import androidx.appsearch.app.AppSearchSchema.DocumentPropertyConfig;
 import androidx.appsearch.app.AppSearchSchema.LongPropertyConfig;
 import androidx.appsearch.app.AppSearchSchema.PropertyConfig;
 import androidx.appsearch.app.AppSearchSchema.StringPropertyConfig;
-import androidx.appsearch.app.PropertyPath;
 import androidx.appsearch.testutil.AppSearchEmail;
 
 import org.junit.Test;
 
-import java.util.List;
-
 public class AppSearchSchemaCtsTest {
     @Test
     public void testInvalidEnums() {
@@ -167,196 +163,6 @@
     }
 
     @Test
-    public void testPropertyConfig() {
-        AppSearchSchema schema = new AppSearchSchema.Builder("Test")
-                .addProperty(new StringPropertyConfig.Builder("string")
-                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build())
-                .addProperty(new AppSearchSchema.LongPropertyConfig.Builder("long")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(LongPropertyConfig.INDEXING_TYPE_NONE)
-                        .build())
-                .addProperty(new AppSearchSchema.LongPropertyConfig.Builder("indexableLong")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(LongPropertyConfig.INDEXING_TYPE_RANGE)
-                        .build())
-                .addProperty(new AppSearchSchema.DoublePropertyConfig.Builder("double")
-                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
-                        .build())
-                .addProperty(new AppSearchSchema.BooleanPropertyConfig.Builder("boolean")
-                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                        .build())
-                .addProperty(new AppSearchSchema.BytesPropertyConfig.Builder("bytes")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .build())
-                .addProperty(new AppSearchSchema.DocumentPropertyConfig.Builder(
-                        "document1", AppSearchEmail.SCHEMA_TYPE)
-                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
-                        .setShouldIndexNestedProperties(true)
-                        .build())
-                .addProperty(new AppSearchSchema.DocumentPropertyConfig.Builder(
-                        "document2", AppSearchEmail.SCHEMA_TYPE)
-                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
-                        .setShouldIndexNestedProperties(false)
-                        .addIndexableNestedProperties("path1", "path2", "path3")
-                        .build())
-                .addProperty(new AppSearchSchema.StringPropertyConfig.Builder("qualifiedId1")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setJoinableValueType(StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID)
-                        .build())
-                .addProperty(new AppSearchSchema.StringPropertyConfig.Builder("qualifiedId2")
-                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                        .setJoinableValueType(StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID)
-                        .build())
-                .build();
-
-        assertThat(schema.getSchemaType()).isEqualTo("Test");
-        List<PropertyConfig> properties = schema.getProperties();
-        assertThat(properties).hasSize(10);
-
-        assertThat(properties.get(0).getName()).isEqualTo("string");
-        assertThat(properties.get(0).getCardinality())
-                .isEqualTo(PropertyConfig.CARDINALITY_REQUIRED);
-        assertThat(((StringPropertyConfig) properties.get(0)).getIndexingType())
-                .isEqualTo(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS);
-        assertThat(((StringPropertyConfig) properties.get(0)).getTokenizerType())
-                .isEqualTo(StringPropertyConfig.TOKENIZER_TYPE_PLAIN);
-
-        assertThat(properties.get(1).getName()).isEqualTo("long");
-        assertThat(properties.get(1).getCardinality())
-                .isEqualTo(PropertyConfig.CARDINALITY_OPTIONAL);
-        assertThat(((LongPropertyConfig) properties.get(1)).getIndexingType())
-                .isEqualTo(LongPropertyConfig.INDEXING_TYPE_NONE);
-
-        assertThat(properties.get(2).getName()).isEqualTo("indexableLong");
-        assertThat(properties.get(2).getCardinality())
-                .isEqualTo(PropertyConfig.CARDINALITY_OPTIONAL);
-        assertThat(((LongPropertyConfig) properties.get(2)).getIndexingType())
-                .isEqualTo(LongPropertyConfig.INDEXING_TYPE_RANGE);
-
-        assertThat(properties.get(3).getName()).isEqualTo("double");
-        assertThat(properties.get(3).getCardinality())
-                .isEqualTo(PropertyConfig.CARDINALITY_REPEATED);
-        assertThat(properties.get(3)).isInstanceOf(AppSearchSchema.DoublePropertyConfig.class);
-
-        assertThat(properties.get(4).getName()).isEqualTo("boolean");
-        assertThat(properties.get(4).getCardinality())
-                .isEqualTo(PropertyConfig.CARDINALITY_REQUIRED);
-        assertThat(properties.get(4)).isInstanceOf(AppSearchSchema.BooleanPropertyConfig.class);
-
-        assertThat(properties.get(5).getName()).isEqualTo("bytes");
-        assertThat(properties.get(5).getCardinality())
-                .isEqualTo(PropertyConfig.CARDINALITY_OPTIONAL);
-        assertThat(properties.get(5)).isInstanceOf(AppSearchSchema.BytesPropertyConfig.class);
-
-        assertThat(properties.get(6).getName()).isEqualTo("document1");
-        assertThat(properties.get(6).getCardinality())
-                .isEqualTo(PropertyConfig.CARDINALITY_REPEATED);
-        assertThat(((AppSearchSchema.DocumentPropertyConfig) properties.get(6)).getSchemaType())
-                .isEqualTo(AppSearchEmail.SCHEMA_TYPE);
-        assertThat(((AppSearchSchema.DocumentPropertyConfig) properties.get(6))
-                .shouldIndexNestedProperties()).isEqualTo(true);
-
-        assertThat(properties.get(7).getName()).isEqualTo("document2");
-        assertThat(properties.get(7).getCardinality())
-                .isEqualTo(PropertyConfig.CARDINALITY_REPEATED);
-        assertThat(((AppSearchSchema.DocumentPropertyConfig) properties.get(7)).getSchemaType())
-                .isEqualTo(AppSearchEmail.SCHEMA_TYPE);
-        assertThat(((AppSearchSchema.DocumentPropertyConfig) properties.get(7))
-                .shouldIndexNestedProperties()).isEqualTo(false);
-        assertThat(((AppSearchSchema.DocumentPropertyConfig) properties.get(7))
-                .getIndexableNestedProperties()).containsExactly("path1", "path2", "path3");
-
-        assertThat(properties.get(8).getName()).isEqualTo("qualifiedId1");
-        assertThat(properties.get(8).getCardinality())
-                .isEqualTo(PropertyConfig.CARDINALITY_OPTIONAL);
-        assertThat(((StringPropertyConfig) properties.get(8)).getJoinableValueType())
-                .isEqualTo(StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID);
-
-        assertThat(properties.get(9).getName()).isEqualTo("qualifiedId2");
-        assertThat(properties.get(9).getCardinality())
-                .isEqualTo(PropertyConfig.CARDINALITY_REQUIRED);
-        assertThat(((StringPropertyConfig) properties.get(9)).getJoinableValueType())
-                .isEqualTo(StringPropertyConfig.JOINABLE_VALUE_TYPE_QUALIFIED_ID);
-    }
-
-    @Test
-    public void testParentTypes() {
-        AppSearchSchema schema = new AppSearchSchema.Builder("EmailMessage")
-                .addParentType("Email")
-                .addParentType("Message")
-                .build();
-        assertThat(schema.getParentTypes()).containsExactly("Email", "Message");
-    }
-
-    @Test
-    public void testDuplicateParentTypes() {
-        AppSearchSchema schema = new AppSearchSchema.Builder("EmailMessage")
-                .addParentType("Email")
-                .addParentType("Message")
-                .addParentType("Email")
-                .build();
-        assertThat(schema.getParentTypes()).containsExactly("Email", "Message");
-    }
-
-    @Test
-    public void testDocumentPropertyConfig_indexableNestedPropertyStrings() {
-        DocumentPropertyConfig documentPropertyConfig =
-                new DocumentPropertyConfig.Builder("property", "Schema")
-                        .addIndexableNestedProperties("prop1", "prop2", "prop1.prop2")
-                        .build();
-        assertThat(documentPropertyConfig.getIndexableNestedProperties())
-                .containsExactly("prop1", "prop2", "prop1.prop2");
-    }
-
-    @Test
-    public void testDocumentPropertyConfig_indexableNestedPropertyPropertyPaths() {
-        DocumentPropertyConfig documentPropertyConfig =
-                new DocumentPropertyConfig.Builder("property", "Schema")
-                        .addIndexableNestedPropertyPaths(new PropertyPath("prop1"),
-                                new PropertyPath("prop1.prop2"))
-                        .build();
-        assertThat(documentPropertyConfig.getIndexableNestedProperties())
-                .containsExactly("prop1", "prop1.prop2");
-    }
-
-    @Test
-    public void testDocumentPropertyConfig_indexableNestedPropertyProperty_duplicatePaths() {
-        DocumentPropertyConfig documentPropertyConfig =
-                new DocumentPropertyConfig.Builder("property", "Schema")
-                        .addIndexableNestedPropertyPaths(new PropertyPath("prop1"),
-                                new PropertyPath("prop1.prop2"))
-                        .addIndexableNestedProperties("prop1")
-                        .build();
-        assertThat(documentPropertyConfig.getIndexableNestedProperties())
-                .containsExactly("prop1", "prop1.prop2");
-    }
-
-    @Test
-    public void testDocumentPropertyConfig_reusingBuilderDoesNotAffectPreviouslyBuiltConfigs() {
-        DocumentPropertyConfig.Builder builder =
-                new DocumentPropertyConfig.Builder("property", "Schema")
-                        .addIndexableNestedProperties("prop1");
-        DocumentPropertyConfig config1 = builder.build();
-        assertThat(config1.getIndexableNestedProperties()).containsExactly("prop1");
-
-        builder.addIndexableNestedProperties("prop2");
-        DocumentPropertyConfig config2 = builder.build();
-        assertThat(config2.getIndexableNestedProperties()).containsExactly("prop1", "prop2");
-        assertThat(config1.getIndexableNestedProperties()).containsExactly("prop1");
-
-        builder.addIndexableNestedPropertyPaths(new PropertyPath("prop3"));
-        DocumentPropertyConfig config3 = builder.build();
-        assertThat(config3.getIndexableNestedProperties()).containsExactly("prop1", "prop2",
-                "prop3");
-        assertThat(config2.getIndexableNestedProperties()).containsExactly("prop1", "prop2");
-        assertThat(config1.getIndexableNestedProperties()).containsExactly("prop1");
-    }
-
-
-    @Test
     public void testInvalidStringPropertyConfigsTokenizerNone() {
         // Everything should work fine with the defaults.
         final StringPropertyConfig.Builder builder =
@@ -442,26 +248,6 @@
     }
 
     @Test
-    public void testInvalidDocumentPropertyConfig_indexableNestedProperties() {
-        // Adding indexableNestedProperties with shouldIndexNestedProperties=true should fail.
-        DocumentPropertyConfig.Builder builder =
-                new DocumentPropertyConfig.Builder("prop1", "Schema1")
-                        .setShouldIndexNestedProperties(true)
-                        .addIndexableNestedProperties("prop1");
-        IllegalArgumentException e =
-                assertThrows(IllegalArgumentException.class, () -> builder.build());
-        assertThat(e).hasMessageThat().contains(
-                "DocumentIndexingConfig#shouldIndexNestedProperties is required to be false when "
-                        + "one or more indexableNestedProperties are provided.");
-
-        builder.addIndexableNestedPropertyPaths(new PropertyPath("prop1.prop2"));
-        e = assertThrows(IllegalArgumentException.class, () -> builder.build());
-        assertThat(e).hasMessageThat().contains(
-                "DocumentIndexingConfig#shouldIndexNestedProperties is required to be false when "
-                        + "one or more indexableNestedProperties are provided.");
-    }
-
-    @Test
     public void testAppSearchSchema_toString() {
         AppSearchSchema schema = new AppSearchSchema.Builder("testSchema")
                 .addProperty(new StringPropertyConfig.Builder("string1")
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionCtsTestBase.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionCtsTestBase.java
index bf03ff2..01924df 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionCtsTestBase.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionCtsTestBase.java
@@ -343,521 +343,6 @@
         assertThat(exception).hasMessageThat().containsMatch("Invalid cycle|Infinite loop");
     }
 
-    @Test
-    public void testSetSchema_indexableNestedPropsList() throws Exception {
-        assumeTrue(mDb1.getFeatures().isFeatureSupported(
-                Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES));
-
-        AppSearchSchema personSchema = new AppSearchSchema.Builder("Person")
-                .addProperty(new StringPropertyConfig.Builder("name")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build())
-                .addProperty(new DocumentPropertyConfig.Builder("worksFor", "Organization")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setShouldIndexNestedProperties(false)
-                        .addIndexableNestedProperties("name")
-                        .build())
-                .build();
-        AppSearchSchema organizationSchema = new AppSearchSchema.Builder("Organization")
-                .addProperty(new StringPropertyConfig.Builder("name")
-                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build())
-                .addProperty(new StringPropertyConfig.Builder("notes")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build())
-                .build();
-
-        mDb1.setSchemaAsync(
-                new SetSchemaRequest.Builder().addSchemas(personSchema, organizationSchema)
-                        .build()).get();
-
-        // Test that properties in Person's indexable_nested_properties_list are indexed and
-        // searchable
-        GenericDocument org1 = new GenericDocument.Builder<>("namespace", "org1", "Organization")
-                .setPropertyString("name", "Org1")
-                .setPropertyString("notes", "Some notes")
-                .build();
-        GenericDocument person1 = new GenericDocument.Builder<>("namespace", "person1", "Person")
-                .setPropertyString("name", "Jane")
-                .setPropertyDocument("worksFor", org1)
-                .build();
-
-        AppSearchBatchResult<String, Void> putResult =
-                checkIsBatchResultSuccess(mDb1.putAsync(
-                        new PutDocumentsRequest.Builder()
-                                .addGenericDocuments(person1, org1)
-                                .build()));
-        assertThat(putResult.getSuccesses()).containsExactly("person1", null, "org1", null);
-        assertThat(putResult.getFailures()).isEmpty();
-
-        GetByDocumentIdRequest getByDocumentIdRequest =
-                new GetByDocumentIdRequest.Builder("namespace")
-                        .addIds("person1", "org1")
-                        .build();
-        List<GenericDocument> outDocuments = doGet(mDb1, getByDocumentIdRequest);
-        assertThat(outDocuments).hasSize(2);
-        assertThat(outDocuments).containsExactly(person1, org1);
-
-        // Both org1 and person should be returned for query "Org1"
-        // For org1 this matches the 'name' property and for person1 this matches the
-        // 'worksFor.name' property.
-        SearchResults searchResults = mDb1.search("Org1", new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .build());
-        outDocuments = convertSearchResultsToDocuments(searchResults);
-        assertThat(outDocuments).hasSize(2);
-        assertThat(outDocuments).containsExactly(person1, org1);
-
-        // Only org1 should be returned for query "notes", since 'worksFor.notes' is not indexed
-        // for the Person-type.
-        searchResults = mDb1.search("notes", new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .build());
-        outDocuments = convertSearchResultsToDocuments(searchResults);
-        assertThat(outDocuments).hasSize(1);
-        assertThat(outDocuments).containsExactly(org1);
-    }
-
-    @Test
-    public void testSetSchema_indexableNestedPropsList_notSupported() throws Exception {
-        assumeFalse(mDb1.getFeatures().isFeatureSupported(
-                Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES));
-
-        AppSearchSchema personSchema = new AppSearchSchema.Builder("Person")
-                .addProperty(new StringPropertyConfig.Builder("name")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build())
-                .addProperty(new DocumentPropertyConfig.Builder("worksFor", "Organization")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setShouldIndexNestedProperties(false)
-                        .addIndexableNestedProperties("name")
-                        .build())
-                .build();
-        AppSearchSchema organizationSchema = new AppSearchSchema.Builder("Organization")
-                .addProperty(new StringPropertyConfig.Builder("name")
-                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build())
-                .addProperty(new StringPropertyConfig.Builder("notes")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build())
-                .build();
-
-        SetSchemaRequest setSchemaRequest = new SetSchemaRequest.Builder()
-                .addSchemas(personSchema, organizationSchema)
-                .build();
-        UnsupportedOperationException e = assertThrows(UnsupportedOperationException.class, () ->
-                mDb1.setSchemaAsync(setSchemaRequest).get());
-        assertThat(e).hasMessageThat().contains(
-                "DocumentPropertyConfig.addIndexableNestedProperties is not supported on this "
-                        + "AppSearch implementation.");
-    }
-
-    @Test
-    public void testSetSchema_indexableNestedPropsList_nonIndexableProp() throws Exception {
-        assumeTrue(mDb1.getFeatures().isFeatureSupported(
-                Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES));
-
-        AppSearchSchema personSchema = new AppSearchSchema.Builder("Person")
-                .addProperty(new StringPropertyConfig.Builder("name")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build())
-                .addProperty(new DocumentPropertyConfig.Builder("worksFor", "Organization")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setShouldIndexNestedProperties(false)
-                        .addIndexableNestedProperties("name")
-                        .build())
-                .build();
-        AppSearchSchema organizationSchema = new AppSearchSchema.Builder("Organization")
-                .addProperty(new StringPropertyConfig.Builder("name")
-                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build())
-                .addProperty(new StringPropertyConfig.Builder("notes")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_NONE)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_NONE)
-                        .build())
-                .build();
-
-        mDb1.setSchemaAsync(
-                new SetSchemaRequest.Builder().addSchemas(personSchema, organizationSchema)
-                        .build()).get();
-
-        // Test that Person's nested properties are indexed correctly.
-        GenericDocument org1 = new GenericDocument.Builder<>("namespace", "org1", "Organization")
-                .setPropertyString("name", "Org1")
-                .setPropertyString("notes", "Some notes")
-                .build();
-        GenericDocument person1 = new GenericDocument.Builder<>("namespace", "person1", "Person")
-                .setPropertyString("name", "Jane")
-                .setPropertyDocument("worksFor", org1)
-                .build();
-
-        AppSearchBatchResult<String, Void> putResult =
-                checkIsBatchResultSuccess(mDb1.putAsync(
-                        new PutDocumentsRequest.Builder()
-                                .addGenericDocuments(person1, org1)
-                                .build()));
-        assertThat(putResult.getSuccesses()).containsExactly("person1", null, "org1", null);
-        assertThat(putResult.getFailures()).isEmpty();
-
-        GetByDocumentIdRequest getByDocumentIdRequest =
-                new GetByDocumentIdRequest.Builder("namespace")
-                        .addIds("person1", "org1")
-                        .build();
-        List<GenericDocument> outDocuments = doGet(mDb1, getByDocumentIdRequest);
-        assertThat(outDocuments).hasSize(2);
-        assertThat(outDocuments).containsExactly(person1, org1);
-
-        // Both org1 and person should be returned for query "Org1"
-        // For org1 this matches the 'name' property and for person1 this matches the
-        // 'worksFor.name' property.
-        SearchResults searchResults = mDb1.search("Org1", new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .build());
-        outDocuments = convertSearchResultsToDocuments(searchResults);
-        assertThat(outDocuments).hasSize(2);
-        assertThat(outDocuments).containsExactly(person1, org1);
-
-        // No documents should match for "notes", since both 'Organization:notes'
-        // and 'Person:worksFor.notes' are non-indexable.
-        searchResults = mDb1.search("notes", new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .build());
-        outDocuments = convertSearchResultsToDocuments(searchResults);
-        assertThat(outDocuments).hasSize(0);
-    }
-
-    @Test
-    public void testSetSchema_indexableNestedPropsList_multipleNestedLevels() throws Exception {
-        assumeTrue(mDb1.getFeatures().isFeatureSupported(
-                Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES));
-
-        AppSearchSchema emailSchema = new AppSearchSchema.Builder("Email")
-                .addProperty(new StringPropertyConfig.Builder("subject")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build())
-                .addProperty(new DocumentPropertyConfig.Builder("sender", "Person")
-                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
-                        .setShouldIndexNestedProperties(false)
-                        .addIndexableNestedProperties("name", "worksFor.name", "worksFor.notes")
-                        .build())
-                .addProperty(new DocumentPropertyConfig.Builder("recipient", "Person")
-                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
-                        .setShouldIndexNestedProperties(true)
-                        .build())
-                .build();
-        AppSearchSchema personSchema = new AppSearchSchema.Builder("Person")
-                .addProperty(new StringPropertyConfig.Builder("name")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build())
-                .addProperty(new StringPropertyConfig.Builder("age")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build())
-                .addProperty(new DocumentPropertyConfig.Builder("worksFor", "Organization")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setShouldIndexNestedProperties(false)
-                        .addIndexableNestedProperties("name", "id")
-                        .build())
-                .build();
-        AppSearchSchema organizationSchema = new AppSearchSchema.Builder("Organization")
-                .addProperty(new StringPropertyConfig.Builder("name")
-                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build())
-                .addProperty(new StringPropertyConfig.Builder("notes")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build())
-                .addProperty(new StringPropertyConfig.Builder("id")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build())
-                .build();
-
-        mDb1.setSchemaAsync(
-                new SetSchemaRequest.Builder()
-                        .addSchemas(emailSchema, personSchema, organizationSchema)
-                        .build()).get();
-
-        // Test that Email and Person's nested properties are indexed correctly.
-        GenericDocument org1 = new GenericDocument.Builder<>("namespace", "org1", "Organization")
-                .setPropertyString("name", "Org1")
-                .setPropertyString("notes", "Some notes")
-                .setPropertyString("id", "1234")
-                .build();
-        GenericDocument person1 = new GenericDocument.Builder<>("namespace", "person1", "Person")
-                .setPropertyString("name", "Jane")
-                .setPropertyString("age", "20")
-                .setPropertyDocument("worksFor", org1)
-                .build();
-        GenericDocument person2 = new GenericDocument.Builder<>("namespace", "person2", "Person")
-                .setPropertyString("name", "John")
-                .setPropertyString("age", "30")
-                .setPropertyDocument("worksFor", org1)
-                .build();
-        GenericDocument email1 = new GenericDocument.Builder<>("namespace", "email1", "Email")
-                .setPropertyString("subject", "Greetings!")
-                .setPropertyDocument("sender", person1)
-                .setPropertyDocument("recipient", person2)
-                .build();
-        AppSearchBatchResult<String, Void> putResult =
-                checkIsBatchResultSuccess(mDb1.putAsync(
-                        new PutDocumentsRequest.Builder()
-                                .addGenericDocuments(person1, org1, person2, email1)
-                                .build()));
-        assertThat(putResult.getSuccesses()).containsExactly("person1", null, "org1", null,
-                "person2", null, "email1", null);
-        assertThat(putResult.getFailures()).isEmpty();
-
-        GetByDocumentIdRequest getByDocumentIdRequest =
-                new GetByDocumentIdRequest.Builder("namespace")
-                        .addIds("person1", "org1", "person2", "email1")
-                        .build();
-        List<GenericDocument> outDocuments = doGet(mDb1, getByDocumentIdRequest);
-        assertThat(outDocuments).hasSize(4);
-        assertThat(outDocuments).containsExactly(person1, org1, person2, email1);
-
-        // Indexed properties:
-        // Email: 'subject', 'sender.name', 'sender.worksFor.name', 'sender.worksFor.notes',
-        //        'recipient.name', 'recipient.age', 'recipient.worksFor.name',
-        //        'recipient.worksFor.id'
-        //        (Email:recipient sets index_nested_props=true, so it follows the same indexing
-        //         configs as the next schema-type level (person))
-        // Person: 'name', 'age', 'worksFor.name', 'worksFor.id'
-        // Organization: 'name', 'notes', 'id'
-        //
-        // All documents should be returned for query 'Org1' because all schemaTypes index the
-        // 'Organization:name' property.
-        SearchResults searchResults = mDb1.search("Org1", new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .build());
-        outDocuments = convertSearchResultsToDocuments(searchResults);
-        assertThat(outDocuments).hasSize(4);
-        assertThat(outDocuments).containsExactly(person1, org1, person2, email1);
-
-        // org1 and email1 should be returned for query 'notes'
-        searchResults = mDb1.search("notes", new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .build());
-        outDocuments = convertSearchResultsToDocuments(searchResults);
-        assertThat(outDocuments).hasSize(2);
-        assertThat(outDocuments).containsExactly(org1, email1);
-
-        // all docs should be returned for query "1234"
-        searchResults = mDb1.search("1234", new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .build());
-        outDocuments = convertSearchResultsToDocuments(searchResults);
-        assertThat(outDocuments).hasSize(4);
-        assertThat(outDocuments).containsExactly(person1, org1, person2, email1);
-
-        // email1 should be returned for query "30", but not for "20" since sender.age is not
-        // indexed, but recipient.age is.
-        // For query "30", person2 should also be returned
-        // For query "20, person1 should be returned.
-        searchResults = mDb1.search("30", new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .build());
-        outDocuments = convertSearchResultsToDocuments(searchResults);
-        assertThat(outDocuments).hasSize(2);
-        assertThat(outDocuments).containsExactly(person2, email1);
-
-        searchResults = mDb1.search("20", new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .build());
-        outDocuments = convertSearchResultsToDocuments(searchResults);
-        assertThat(outDocuments).hasSize(1);
-        assertThat(outDocuments).containsExactly(person1);
-    }
-
-    @Test
-    public void testSetSchema_indexableNestedPropsList_circularRefs() throws Exception {
-        assumeTrue(mDb1.getFeatures().isFeatureSupported(
-                Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES));
-        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SET_SCHEMA_CIRCULAR_REFERENCES));
-
-        // Create schema with valid cycle: Person -> Organization -> Person...
-        AppSearchSchema personSchema = new AppSearchSchema.Builder("Person")
-                .addProperty(new StringPropertyConfig.Builder("name")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build())
-                .addProperty(new StringPropertyConfig.Builder("address")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build())
-                .addProperty(new DocumentPropertyConfig.Builder("worksFor", "Organization")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setShouldIndexNestedProperties(false)
-                        .addIndexableNestedProperties("name", "notes", "funder.name")
-                        .build())
-                .build();
-        AppSearchSchema organizationSchema = new AppSearchSchema.Builder("Organization")
-                .addProperty(new StringPropertyConfig.Builder("name")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build())
-                .addProperty(new StringPropertyConfig.Builder("notes")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .build())
-                .addProperty(new DocumentPropertyConfig.Builder("funder", "Person")
-                        .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
-                        .setShouldIndexNestedProperties(false)
-                        .addIndexableNestedProperties("name", "worksFor.name",
-                                "worksFor.funder.address", "worksFor.funder.worksFor.notes")
-                        .build())
-                .build();
-        mDb1.setSchemaAsync(
-                new SetSchemaRequest.Builder().addSchemas(personSchema, organizationSchema)
-                        .build()).get();
-
-        // Test that documents following the circular schema are indexed correctly, and that its
-        // sections are searchable
-        GenericDocument person1 = new GenericDocument.Builder<>("namespace", "person1", "Person")
-                .setPropertyString("name", "Person1")
-                .setPropertyString("address", "someAddress")
-                .build();
-        GenericDocument org1 = new GenericDocument.Builder<>("namespace", "org1", "Organization")
-                .setPropertyString("name", "Org1")
-                .setPropertyString("notes", "someNote")
-                .setPropertyDocument("funder", person1)
-                .build();
-        GenericDocument person2 = new GenericDocument.Builder<>("namespace", "person2", "Person")
-                .setPropertyString("name", "Person2")
-                .setPropertyString("address", "anotherAddress")
-                .setPropertyDocument("worksFor", org1)
-                .build();
-        GenericDocument org2 = new GenericDocument.Builder<>("namespace", "org2", "Organization")
-                .setPropertyString("name", "Org2")
-                .setPropertyString("notes", "anotherNote")
-                .setPropertyDocument("funder", person2)
-                .build();
-
-        AppSearchBatchResult<String, Void> putResult =
-                checkIsBatchResultSuccess(
-                        mDb1.putAsync(new PutDocumentsRequest.Builder()
-                                .addGenericDocuments(person1, org1, person2, org2)
-                                .build()));
-        assertThat(putResult.getSuccesses()).containsExactly("person1", null, "org1", null,
-                "person2", null, "org2", null);
-        assertThat(putResult.getFailures()).isEmpty();
-
-        GetByDocumentIdRequest getByDocumentIdRequest =
-                new GetByDocumentIdRequest.Builder("namespace")
-                        .addIds("person1", "person2", "org1", "org2")
-                        .build();
-        List<GenericDocument> outDocuments = doGet(mDb1, getByDocumentIdRequest);
-        assertThat(outDocuments).hasSize(4);
-        assertThat(outDocuments).containsExactly(person1, person2, org1, org2);
-
-        // Indexed properties:
-        // Person: 'name', 'address', 'worksFor.name', 'worksFor.notes', 'worksFor.funder.name'
-        // Organization: 'name', 'notes', 'funder.name', 'funder.worksFor.name',
-        //               'funder.worksFor.funder.address', 'funder.worksFor.funder.worksFor.notes'
-        //
-        // "Person1" should match person1 (name), org1 (funder.name) and person2
-        // (worksFor.funder.name)
-        SearchResults searchResults = mDb1.search("Person1",
-                new SearchSpec.Builder()
-                        .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                        .build());
-        outDocuments = convertSearchResultsToDocuments(searchResults);
-        assertThat(outDocuments).hasSize(3);
-        assertThat(outDocuments).containsExactly(person1, org1, person2);
-
-        // "someAddress" should match person1 (address) and org2 (funder.worksFor.funder.address)
-        searchResults = mDb1.search("someAddress",
-                new SearchSpec.Builder()
-                        .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                        .build());
-        outDocuments = convertSearchResultsToDocuments(searchResults);
-        assertThat(outDocuments).hasSize(2);
-        assertThat(outDocuments).containsExactly(person1, org2);
-
-        // "Org1" should match org1 (name), person2 (worksFor.name) and org2 (funder.worksFor.name)
-        searchResults = mDb1.search("Org1",
-                new SearchSpec.Builder()
-                        .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                        .build());
-        outDocuments = convertSearchResultsToDocuments(searchResults);
-        assertThat(outDocuments).hasSize(3);
-        assertThat(outDocuments).containsExactly(org1, person2, org2);
-
-        // "someNote" should match org1 (notes) and person2 (worksFor.notes)
-        searchResults = mDb1.search("someNote",
-                new SearchSpec.Builder()
-                        .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                        .build());
-        outDocuments = convertSearchResultsToDocuments(searchResults);
-        assertThat(outDocuments).hasSize(2);
-        assertThat(outDocuments).containsExactly(org1, person2);
-
-        // "Person2" should match person2 (name), org2 (funder.name)
-        searchResults = mDb1.search("Person2",
-                new SearchSpec.Builder()
-                        .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                        .build());
-        outDocuments = convertSearchResultsToDocuments(searchResults);
-        assertThat(outDocuments).hasSize(2);
-        assertThat(outDocuments).containsExactly(person2, org2);
-
-        // "anotherAddress" should match only person2 (address)
-        searchResults = mDb1.search("anotherAddress",
-                new SearchSpec.Builder()
-                        .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                        .build());
-        outDocuments = convertSearchResultsToDocuments(searchResults);
-        assertThat(outDocuments).hasSize(1);
-        assertThat(outDocuments).containsExactly(person2);
-
-        // "Org2" and "anotherNote" should both match only org2 (name, notes)
-        searchResults = mDb1.search("Org2",
-                new SearchSpec.Builder()
-                        .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                        .build());
-        outDocuments = convertSearchResultsToDocuments(searchResults);
-        assertThat(outDocuments).hasSize(1);
-        assertThat(outDocuments).containsExactly(org2);
-
-        searchResults = mDb1.search("anotherNote",
-                new SearchSpec.Builder()
-                        .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                        .build());
-        outDocuments = convertSearchResultsToDocuments(searchResults);
-        assertThat(outDocuments).hasSize(1);
-        assertThat(outDocuments).containsExactly(org2);
-    }
-
 // @exportToFramework:startStrip()
 
     @Test
@@ -1256,190 +741,6 @@
     }
 
     @Test
-    public void testGetSchema_parentTypes() throws Exception {
-        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE));
-        AppSearchSchema emailSchema =
-                new AppSearchSchema.Builder("Email").build();
-        AppSearchSchema messageSchema =
-                new AppSearchSchema.Builder("Message").build();
-        AppSearchSchema emailMessageSchema = new AppSearchSchema.Builder("EmailMessage")
-                .addProperty(
-                        new StringPropertyConfig.Builder("sender").setCardinality(
-                                PropertyConfig.CARDINALITY_REQUIRED).build())
-                .addProperty(
-                        new StringPropertyConfig.Builder("email").setCardinality(
-                                PropertyConfig.CARDINALITY_REQUIRED).build())
-                .addProperty(
-                        new StringPropertyConfig.Builder("content").setCardinality(
-                                PropertyConfig.CARDINALITY_REQUIRED).build())
-                .addParentType("Email")
-                .addParentType("Message")
-                .build();
-
-        SetSchemaRequest request = new SetSchemaRequest.Builder()
-                .addSchemas(emailMessageSchema)
-                .addSchemas(emailSchema)
-                .addSchemas(messageSchema)
-                .build();
-
-        mDb1.setSchemaAsync(request).get();
-
-        Set<AppSearchSchema> actual = mDb1.getSchemaAsync().get().getSchemas();
-        assertThat(actual).hasSize(3);
-        assertThat(actual).isEqualTo(request.getSchemas());
-    }
-
-    @Test
-    public void testGetSchema_parentTypes_notSupported() throws Exception {
-        assumeFalse(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE));
-        AppSearchSchema emailSchema =
-                new AppSearchSchema.Builder("Email").build();
-        AppSearchSchema messageSchema =
-                new AppSearchSchema.Builder("Message").build();
-        AppSearchSchema emailMessageSchema = new AppSearchSchema.Builder("EmailMessage")
-                .addParentType("Email")
-                .addParentType("Message")
-                .build();
-
-        SetSchemaRequest request = new SetSchemaRequest.Builder()
-                .addSchemas(emailMessageSchema)
-                .addSchemas(emailSchema)
-                .addSchemas(messageSchema)
-                .build();
-
-        UnsupportedOperationException e = assertThrows(UnsupportedOperationException.class, () ->
-                mDb1.setSchemaAsync(request).get());
-        assertThat(e).hasMessageThat().contains(Features.SCHEMA_ADD_PARENT_TYPE
-                + " is not available on this AppSearch implementation.");
-    }
-
-    @Test
-    public void testSetSchema_dataTypeIncompatibleWithParentTypes() throws Exception {
-        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE));
-        AppSearchSchema messageSchema =
-                new AppSearchSchema.Builder("Message")
-                        .addProperty(
-                                new LongPropertyConfig.Builder("sender").setCardinality(
-                                        PropertyConfig.CARDINALITY_REQUIRED).build())
-                        .build();
-        AppSearchSchema emailSchema =
-                new AppSearchSchema.Builder("Email")
-                        .addParentType("Message")
-                        .addProperty(
-                                new StringPropertyConfig.Builder("sender").setCardinality(
-                                        PropertyConfig.CARDINALITY_REQUIRED).build())
-                        .build();
-
-        SetSchemaRequest request = new SetSchemaRequest.Builder()
-                .addSchemas(messageSchema)
-                .addSchemas(emailSchema)
-                .build();
-
-        ExecutionException executionException = assertThrows(ExecutionException.class,
-                () -> mDb1.setSchemaAsync(request).get());
-        assertThat(executionException).hasCauseThat().isInstanceOf(AppSearchException.class);
-        AppSearchException exception = (AppSearchException) executionException.getCause();
-        assertThat(exception.getResultCode()).isEqualTo(RESULT_INVALID_ARGUMENT);
-        assertThat(exception).hasMessageThat().contains(
-                "Property sender from child type androidx.appsearch.test$/Email is not compatible"
-                        + " to the parent type androidx.appsearch.test$/Message.");
-    }
-
-    @Test
-    public void testSetSchema_documentTypeIncompatibleWithParentTypes() throws Exception {
-        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE));
-        AppSearchSchema personSchema = new AppSearchSchema.Builder("Person")
-                .build();
-        AppSearchSchema artistSchema = new AppSearchSchema.Builder("Artist")
-                .addParentType("Person")
-                .build();
-        AppSearchSchema messageSchema =
-                new AppSearchSchema.Builder("Message")
-                        .addProperty(
-                                new DocumentPropertyConfig.Builder("sender",
-                                        "Artist").setCardinality(
-                                        PropertyConfig.CARDINALITY_REQUIRED).build())
-                        .build();
-        AppSearchSchema emailSchema =
-                new AppSearchSchema.Builder("Email")
-                        .addParentType("Message")
-                        // "sender" is defined as an Artist in the parent type Message, which
-                        // requires "sender"'s type here to be a subtype of Artist. Thus, this is
-                        // incompatible because Person is not a subtype of Artist.
-                        .addProperty(
-                                new DocumentPropertyConfig.Builder("sender",
-                                        "Person").setCardinality(
-                                        PropertyConfig.CARDINALITY_REQUIRED).build())
-                        .build();
-
-        SetSchemaRequest request = new SetSchemaRequest.Builder()
-                .addSchemas(personSchema)
-                .addSchemas(artistSchema)
-                .addSchemas(messageSchema)
-                .addSchemas(emailSchema)
-                .build();
-
-        ExecutionException executionException = assertThrows(ExecutionException.class,
-                () -> mDb1.setSchemaAsync(request).get());
-        assertThat(executionException).hasCauseThat().isInstanceOf(AppSearchException.class);
-        AppSearchException exception = (AppSearchException) executionException.getCause();
-        assertThat(exception.getResultCode()).isEqualTo(RESULT_INVALID_ARGUMENT);
-        assertThat(exception).hasMessageThat().contains(
-                "Property sender from child type androidx.appsearch.test$/Email is not compatible"
-                        + " to the parent type androidx.appsearch.test$/Message.");
-    }
-
-    @Test
-    public void testSetSchema_compatibleWithParentTypes() throws Exception {
-        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE));
-        AppSearchSchema personSchema = new AppSearchSchema.Builder("Person")
-                .build();
-        AppSearchSchema artistSchema = new AppSearchSchema.Builder("Artist")
-                .addParentType("Person")
-                .build();
-        AppSearchSchema messageSchema =
-                new AppSearchSchema.Builder("Message")
-                        .addProperty(
-                                new DocumentPropertyConfig.Builder("sender",
-                                        "Person").setCardinality(
-                                        PropertyConfig.CARDINALITY_REQUIRED).build())
-                        .addProperty(new StringPropertyConfig.Builder("note")
-                                .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                                .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                                .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                .build())
-                        .build();
-        AppSearchSchema emailSchema =
-                new AppSearchSchema.Builder("Email")
-                        .addParentType("Message")
-                        .addProperty(
-                                // Artist is a subtype of Person, so compatible
-                                new DocumentPropertyConfig.Builder("sender",
-                                        "Artist").setCardinality(
-                                        PropertyConfig.CARDINALITY_REQUIRED).build())
-                        .addProperty(new StringPropertyConfig.Builder("note")
-                                .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                                // A different indexing or tokenizer type is ok.
-                                .setIndexingType(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
-                                .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_VERBATIM)
-                                .build())
-                        .build();
-
-        SetSchemaRequest request = new SetSchemaRequest.Builder()
-                .addSchemas(personSchema)
-                .addSchemas(artistSchema)
-                .addSchemas(messageSchema)
-                .addSchemas(emailSchema)
-                .build();
-
-        mDb1.setSchemaAsync(request).get();
-
-        Set<AppSearchSchema> actual = mDb1.getSchemaAsync().get().getSchemas();
-        assertThat(actual).hasSize(4);
-        assertThat(actual).isEqualTo(request.getSchemas());
-    }
-
-    @Test
     public void testGetNamespaces() throws Exception {
         // Schema registration
         mDb1.setSchemaAsync(
@@ -2841,78 +2142,6 @@
     }
 
     @Test
-    public void testQuery_typeFilterWithPolymorphism() throws Exception {
-        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE));
-
-        // Schema registration
-        AppSearchSchema personSchema = new AppSearchSchema.Builder("Person")
-                .addProperty(new StringPropertyConfig.Builder("name")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .build()
-                ).build();
-        AppSearchSchema artistSchema = new AppSearchSchema.Builder("Artist")
-                .addParentType("Person")
-                .addProperty(new StringPropertyConfig.Builder("name")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .build()
-                ).build();
-        mDb1.setSchemaAsync(
-                new SetSchemaRequest.Builder()
-                        .addSchemas(personSchema)
-                        .addSchemas(artistSchema)
-                        .addSchemas(AppSearchEmail.SCHEMA)
-                        .build()).get();
-
-        // Index some documents
-        GenericDocument personDoc = new GenericDocument.Builder<>("namespace", "id1", "Person")
-                .setPropertyString("name", "Foo")
-                .build();
-        GenericDocument artistDoc = new GenericDocument.Builder<>("namespace", "id2", "Artist")
-                .setPropertyString("name", "Foo")
-                .build();
-        AppSearchEmail emailDoc =
-                new AppSearchEmail.Builder("namespace", "id3")
-                        .setFrom("[email protected]")
-                        .setTo("[email protected]", "[email protected]")
-                        .setSubject("testPut example")
-                        .setBody("Foo")
-                        .build();
-        checkIsBatchResultSuccess(mDb1.putAsync(
-                new PutDocumentsRequest.Builder().addGenericDocuments(personDoc, artistDoc,
-                        emailDoc).build()));
-
-        // Query for the documents
-        SearchResults searchResults = mDb1.search("Foo", new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .build());
-        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).hasSize(3);
-        assertThat(documents).containsExactly(personDoc, artistDoc, emailDoc);
-
-        // Query with a filter for the "Person" type should also include the "Artist" type.
-        searchResults = mDb1.search("Foo", new SearchSpec.Builder()
-                .addFilterSchemas("Person")
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).hasSize(2);
-        assertThat(documents).containsExactly(personDoc, artistDoc);
-
-        // Query with a filters for the "Artist" type should not include the "Person" type.
-        searchResults = mDb1.search("Foo", new SearchSpec.Builder()
-                .addFilterSchemas("Artist")
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).hasSize(1);
-        assertThat(documents).containsExactly(artistDoc);
-    }
-
-    @Test
     public void testQuery_packageFilter() throws Exception {
         // Schema registration
         mDb1.setSchemaAsync(
@@ -3162,92 +2391,6 @@
     }
 
     @Test
-    public void testQuery_projectionWithPolymorphism() throws Exception {
-        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE));
-
-        // Schema registration
-        AppSearchSchema personSchema = new AppSearchSchema.Builder("Person")
-                .addProperty(new StringPropertyConfig.Builder("name")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .build()
-                )
-                .addProperty(new StringPropertyConfig.Builder("emailAddress")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .build()
-                ).build();
-        AppSearchSchema artistSchema = new AppSearchSchema.Builder("Artist")
-                .addParentType("Person")
-                .addProperty(new StringPropertyConfig.Builder("name")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .build()
-                )
-                .addProperty(new StringPropertyConfig.Builder("emailAddress")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .build()
-                )
-                .addProperty(new StringPropertyConfig.Builder("company")
-                        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .build()
-                ).build();
-        mDb1.setSchemaAsync(
-                new SetSchemaRequest.Builder()
-                        .addSchemas(personSchema)
-                        .addSchemas(artistSchema)
-                        .build()).get();
-
-        // Index two documents
-        GenericDocument personDoc = new GenericDocument.Builder<>("namespace", "id1", "Person")
-                .setCreationTimestampMillis(1000)
-                .setPropertyString("name", "Foo Person")
-                .setPropertyString("emailAddress", "[email protected]")
-                .build();
-        GenericDocument artistDoc = new GenericDocument.Builder<>("namespace", "id2", "Artist")
-                .setCreationTimestampMillis(1000)
-                .setPropertyString("name", "Foo Artist")
-                .setPropertyString("emailAddress", "[email protected]")
-                .setPropertyString("company", "Company")
-                .build();
-        checkIsBatchResultSuccess(mDb1.putAsync(
-                new PutDocumentsRequest.Builder()
-                        .addGenericDocuments(personDoc, artistDoc).build()));
-
-        // Query with type property paths {"Person", ["name"]}, {"Artist", ["emailAddress"]}
-        // This will be expanded to paths {"Person", ["name"]}, {"Artist", ["name", "emailAddress"]}
-        // via polymorphism.
-        SearchResults searchResults = mDb1.search("Foo", new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .addProjection("Person", ImmutableList.of("name"))
-                .addProjection("Artist", ImmutableList.of("emailAddress"))
-                .build());
-        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
-
-        // The person document should have been returned with only the "name" property. The artist
-        // document should have been returned with all of its properties.
-        GenericDocument expectedPerson =
-                new GenericDocument.Builder<>("namespace", "id1", "Person")
-                        .setCreationTimestampMillis(1000)
-                        .setPropertyString("name", "Foo Person")
-                        .build();
-        GenericDocument expectedArtist =
-                new GenericDocument.Builder<>("namespace", "id2", "Artist")
-                        .setCreationTimestampMillis(1000)
-                        .setPropertyString("name", "Foo Artist")
-                        .setPropertyString("emailAddress", "[email protected]")
-                        .build();
-        assertThat(documents).containsExactly(expectedPerson, expectedArtist);
-    }
-
-    @Test
     public void testQuery_projectionEmpty() throws Exception {
         // Schema registration
         mDb1.setSchemaAsync(
@@ -3611,78 +2754,6 @@
     }
 
     @Test
-    public void testQuery_indexBasedOnParentTypePolymorphism() throws Exception {
-        assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE));
-
-        // Schema registration
-        AppSearchSchema personSchema = new AppSearchSchema.Builder("Person")
-                .addProperty(new StringPropertyConfig.Builder("name")
-                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .build())
-                .build();
-        AppSearchSchema artistSchema = new AppSearchSchema.Builder("Artist")
-                .addParentType("Person")
-                .addProperty(new StringPropertyConfig.Builder("name")
-                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .build())
-                .addProperty(new StringPropertyConfig.Builder("company")
-                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                        .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
-                        .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
-                        .build())
-                .build();
-        AppSearchSchema messageSchema =
-                new AppSearchSchema.Builder("Message")
-                        .addProperty(
-                                new DocumentPropertyConfig.Builder("sender", "Person")
-                                        .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
-                                        .setShouldIndexNestedProperties(true)
-                                        .build())
-                        .build();
-        mDb1.setSchemaAsync(
-                new SetSchemaRequest.Builder()
-                        .addSchemas(personSchema)
-                        .addSchemas(artistSchema)
-                        .addSchemas(messageSchema)
-                        .build()).get();
-
-        // Index some an artistDoc and a messageDoc
-        GenericDocument artistDoc = new GenericDocument.Builder<>("namespace", "id1", "Artist")
-                .setPropertyString("name", "Foo")
-                .setPropertyString("company", "Bar")
-                .build();
-        GenericDocument messageDoc = new GenericDocument.Builder<>("namespace", "id2", "Message")
-                // sender is defined as a Person, which accepts an Artist because Artist <: Person.
-                // However, indexing will be based on what's defined in Person, so the "company"
-                // property in artistDoc cannot be used to search this messageDoc.
-                .setPropertyDocument("sender", artistDoc)
-                .build();
-        checkIsBatchResultSuccess(mDb1.putAsync(
-                new PutDocumentsRequest.Builder().addGenericDocuments(artistDoc,
-                        messageDoc).build()));
-
-        // Query for the documents
-        SearchResults searchResults = mDb1.search("Foo", new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .build());
-        List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).hasSize(2);
-        assertThat(documents).containsExactly(artistDoc, messageDoc);
-
-        // The "company" property in artistDoc cannot be used to search messageDoc.
-        searchResults = mDb1.search("Bar", new SearchSpec.Builder()
-                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
-                .build());
-        documents = convertSearchResultsToDocuments(searchResults);
-        assertThat(documents).hasSize(1);
-        assertThat(documents).containsExactly(artistDoc);
-    }
-
-    @Test
     public void testSnippet() throws Exception {
         // Schema registration
         AppSearchSchema genericSchema = new AppSearchSchema.Builder("Generic")
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/Document.java b/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/Document.java
index 447da96..e4b390d 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/Document.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/Document.java
@@ -16,6 +16,7 @@
 // @exportToFramework:skipFile()
 package androidx.appsearch.annotation;
 
+import androidx.annotation.NonNull;
 import androidx.appsearch.app.AppSearchSchema;
 
 import java.lang.annotation.Documented;
@@ -253,6 +254,47 @@
                 default AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE;
 
         /**
+         * Configures how a property should be converted to and from a {@link String}.
+         *
+         * <p>Useful for representing properties using rich types that boil down to simple string
+         * values in the database.
+         *
+         * <p>The referenced class must satisfy the following:
+         *
+         * <ol>
+         *     <li>
+         *         Have a static method called {@code serialize} that converts the property's Java
+         *         type to a {@link String}.
+         *     </li>
+         *     <li>
+         *         Have a static method called {@code deserialize} that converts a {@link String} to
+         *         the property's Java type or returns null if deserialization failed.
+         *     </li>
+         * </ol>
+         *
+         * <p>For example:
+         *
+         * <pre>
+         * {@code
+         * @Document("Entity")
+         * public final class MyEntity {
+         *
+         *     @Document.StringProperty(serializer = SomeRichTypeSerializer.class)
+         *     public SomeRichType getMyProperty();
+         *
+         *     public final class SomeRichTypeSerializer {
+         *       public static String serialize(SomeRichType instance) {...}
+         *
+         *       @Nullable
+         *       public static SomeRichType deserialize(String string) {...}
+         *     }
+         * }
+         * }
+         * </pre>
+         */
+        Class<?> serializer() default DefaultSerializer.class;
+
+        /**
          * Configures whether this property must be specified for the document to be valid.
          *
          * <p>This attribute does not apply to properties of a repeated type (e.g. a list).
@@ -262,6 +304,20 @@
          * this attribute to {@code true}.
          */
         boolean required() default false;
+
+        final class DefaultSerializer {
+            private DefaultSerializer() {}
+
+            @NonNull
+            public static String serialize(@NonNull String value) {
+                return value;
+            }
+
+            @NonNull
+            public static String deserialize(@NonNull String string) {
+                return string;
+            }
+        }
     }
 
     /**
@@ -326,6 +382,29 @@
                 default AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_NONE;
 
         /**
+         * Configures how a property should be converted to and from a {@link Long}.
+         *
+         * <p>Useful for representing properties using rich types that boil down to simple 64-bit
+         * integer values in the database.
+         *
+         * <p>The referenced class must satisfy the following:
+         *
+         * <ol>
+         *     <li>
+         *         Have a static method called {@code serialize} that converts the property's Java
+         *         type to a {@link Long}.
+         *     </li>
+         *     <li>
+         *         Have a static method called {@code deserialize} that converts a {@link Long} to
+         *         the property's Java type or returns null if deserialization failed.
+         *     </li>
+         * </ol>
+         *
+         * <p>See {@link StringProperty#serializer()} for an example of a serializer.
+         */
+        Class<?> serializer() default DefaultSerializer.class;
+
+        /**
          * Configures whether this property must be specified for the document to be valid.
          *
          * <p>This attribute does not apply to properties of a repeated type (e.g. a list).
@@ -335,6 +414,18 @@
          * this attribute to {@code true}.
          */
         boolean required() default false;
+
+        final class DefaultSerializer {
+            private DefaultSerializer() {}
+
+            public static long serialize(long value) {
+                return value;
+            }
+
+            public static long deserialize(long l) {
+                return l;
+            }
+        }
     }
 
     /**
@@ -353,6 +444,29 @@
         String name() default "";
 
         /**
+         * Configures how a property should be converted to and from a {@link Double}.
+         *
+         * <p>Useful for representing properties using rich types that boil down to simple
+         * double-precision decimal values in the database.
+         *
+         * <p>The referenced class must satisfy the following:
+         *
+         * <ol>
+         *     <li>
+         *         Have a static method called {@code serialize} that converts the property's Java
+         *         type to a {@link Double}.
+         *     </li>
+         *     <li>
+         *         Have a static method called {@code deserialize} that converts a {@link Double} to
+         *         the property's Java type or returns null if deserialization failed.
+         *     </li>
+         * </ol>
+         *
+         * <p>See {@link StringProperty#serializer()} for an example of a serializer.
+         */
+        Class<?> serializer() default DefaultSerializer.class;
+
+        /**
          * Configures whether this property must be specified for the document to be valid.
          *
          * <p>This attribute does not apply to properties of a repeated type (e.g. a list).
@@ -362,6 +476,18 @@
          * this attribute to {@code true}.
          */
         boolean required() default false;
+
+        final class DefaultSerializer {
+            private DefaultSerializer() {}
+
+            public static double serialize(double value) {
+                return value;
+            }
+
+            public static double deserialize(double d) {
+                return d;
+            }
+        }
     }
 
     /** Configures a boolean member field of a class as a property known to AppSearch. */
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSchema.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSchema.java
index 8548810..597924f 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSchema.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchSchema.java
@@ -145,6 +145,11 @@
 
     /**
      * Returns the list of parent types of this schema for polymorphism.
+     *
+     * <!--@exportToFramework:ifJetpack()--><!--@exportToFramework:else()
+     * @exportToFramework:hide TODO(b/291122592): Unhide in Mainline when API updates via Mainline
+     *   are possible.
+     * -->
      */
     @NonNull
     public List<String> getParentTypes() {
@@ -264,8 +269,12 @@
          * of its parents based on the above rules. For example, if LocalBusiness is defined as a
          * subtype of both Place and Organization, then the compatibility of LocalBusiness with
          * Place and the compatibility of LocalBusiness with Organization will both be checked.
+         *
+         * <!--@exportToFramework:ifJetpack()--><!--@exportToFramework:else()
+         * @exportToFramework:hide TODO(b/291122592): Unhide in Mainline when API updates via
+         *   Mainline are possible.
+         * -->
          */
-        // TODO(b/280698873): Disallow polymorphism in AppSearch framework for Android T.
         @CanIgnoreReturnValue
         @NonNull
         // @exportToFramework:startStrip()
@@ -318,6 +327,7 @@
          * <p>NOTE: The integer values of these constants must match the proto enum constants in
          * com.google.android.icing.proto.PropertyConfigProto.DataType.Code.
          *
+         * @exportToFramework:hide
          */
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         @IntDef(value = {
@@ -369,6 +379,7 @@
          * <p>NOTE: The integer values of these constants must match the proto enum constants in
          * com.google.android.icing.proto.PropertyConfigProto.Cardinality.Code.
          *
+         * @exportToFramework:hide
          */
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         @IntDef(value = {
@@ -594,6 +605,7 @@
          * <p>NOTE: The integer values of these constants must match the proto enum constants in
          * com.google.android.icing.proto.IndexingConfig.TokenizerType.Code.
          *
+         * @exportToFramework:hide
          */
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         @IntDef(value = {
@@ -1210,7 +1222,14 @@
             return mBundle.getBoolean(INDEX_NESTED_PROPERTIES_FIELD);
         }
 
-        /** Returns the list of indexable nested properties for the nested document. */
+        /**
+         * Returns the list of indexable nested properties for the nested document.
+         *
+         * <!--@exportToFramework:ifJetpack()--><!--@exportToFramework:else()
+         * @exportToFramework:hide TODO(b/291122592): Unhide in Mainline when API updates via
+         *   Mainline are possible.
+         * -->
+         */
         @NonNull
         public List<String> getIndexableNestedProperties() {
             List<String> indexableNestedPropertiesList =
@@ -1306,6 +1325,11 @@
              * required to be false if any indexable nested property is added this way for the
              * document property. Attempting to build a DocumentPropertyConfig when this is not
              * true throws {@link IllegalArgumentException}.
+             *
+             * <!--@exportToFramework:ifJetpack()--><!--@exportToFramework:else()
+             * @exportToFramework:hide TODO(b/291122592): Unhide in Mainline when API updates via
+             *   Mainline are possible.
+             * -->
              */
             @CanIgnoreReturnValue
             @NonNull
@@ -1324,6 +1348,11 @@
              * Adds one or more property paths for indexing from the nested document property.
              *
              * @see #addIndexableNestedProperties(String...)
+             *
+             * <!--@exportToFramework:ifJetpack()--><!--@exportToFramework:else()
+             * @exportToFramework:hide TODO(b/291122592): Unhide in Mainline when API updates via
+             *   Mainline are possible.
+             * -->
              */
             @CanIgnoreReturnValue
             @SuppressLint("MissingGetterMatchingBuilder")
@@ -1343,6 +1372,11 @@
              * Adds one or more properties for indexing from the nested document property.
              *
              * @see #addIndexableNestedProperties(String...)
+             *
+             * <!--@exportToFramework:ifJetpack()--><!--@exportToFramework:else()
+             * @exportToFramework:hide TODO(b/291122592): Unhide in Mainline when API updates via
+             *   Mainline are possible.
+             * -->
              */
             @CanIgnoreReturnValue
             @NonNull
@@ -1362,6 +1396,11 @@
              * Adds one or more property paths for indexing from the nested document property.
              *
              * @see #addIndexableNestedProperties(String...)
+             *
+             * <!--@exportToFramework:ifJetpack()--><!--@exportToFramework:else()
+             * @exportToFramework:hide TODO(b/291122592): Unhide in Mainline when API updates via
+             *   Mainline are possible.
+             * -->
              */
             @CanIgnoreReturnValue
             @SuppressLint("MissingGetterMatchingBuilder")
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSpec.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSpec.java
index 919cf29..a007c4a 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSpec.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchSpec.java
@@ -214,10 +214,13 @@
     /**
      * Results should be grouped together by schema type for the purpose of enforcing a limit on the
      * number of results returned per schema type.
-     * <!--@exportToFramework:hide-->
+     *
+     * <!--@exportToFramework:ifJetpack()--><!--@exportToFramework:else()
+     * @exportToFramework:hide TODO(b/291122592): Unhide in Mainline when API updates via
+     *   Mainline are possible.
+     * -->
      */
     // @exportToFramework:startStrip()
-    // TODO(b/258715421) start exporting this when it is unhidden in framework
     @RequiresFeature(
             enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
             name = Features.SEARCH_SPEC_GROUPING_TYPE_PER_SCHEMA)
diff --git a/appsearch/exportToFramework.py b/appsearch/exportToFramework.py
index 48ec64b..aec945a 100755
--- a/appsearch/exportToFramework.py
+++ b/appsearch/exportToFramework.py
@@ -169,6 +169,7 @@
             .replace('/*@exportToFramework:CurrentTimeMillisLong*/', '@CurrentTimeMillisLong')
             .replace('/*@exportToFramework:UnsupportedAppUsage*/', '@UnsupportedAppUsage')
             .replace('<!--@exportToFramework:hide-->', '@hide')
+            .replace('@exportToFramework:hide', '@hide')
             .replace('// @exportToFramework:skipFile()', '')
         )
         contents = re.sub(r'\/\/ @exportToFramework:copyToPath\([^)]+\)', '', contents)
diff --git a/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/utils/BaselineProfileProjectSetupRule.kt b/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/utils/BaselineProfileProjectSetupRule.kt
index 7900dc7..7db968c 100644
--- a/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/utils/BaselineProfileProjectSetupRule.kt
+++ b/benchmark/baseline-profile-gradle-plugin/src/test/kotlin/androidx/baselineprofile/gradle/utils/BaselineProfileProjectSetupRule.kt
@@ -166,7 +166,7 @@
 
             val kotlinGradlePluginDependency = if (addKotlinGradlePluginToClasspath) {
                 """
-             "org.jetbrains.kotlin:kotlin-gradle-plugin:${appTargetSetupRule.props.kotlinVersion}"
+             "${appTargetSetupRule.props.kgpDependency}"
                     """.trimIndent()
             } else {
                 null
diff --git a/benchmark/benchmark-common/api/1.2.0-beta01.txt b/benchmark/benchmark-common/api/1.2.0-beta01.txt
new file mode 100644
index 0000000..eb18450
--- /dev/null
+++ b/benchmark/benchmark-common/api/1.2.0-beta01.txt
@@ -0,0 +1,117 @@
+// Signature format: 4.0
+package androidx.benchmark {
+
+  public final class BenchmarkState {
+    ctor @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkStateApi public BenchmarkState(optional Integer? warmupCount, optional Integer? repeatCount);
+    method @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkStateApi public java.util.List<java.lang.Double> getMeasurementTimeNs();
+    method public boolean keepRunning();
+    method public void pauseTiming();
+    method @SuppressCompatibility @androidx.benchmark.BenchmarkState.Companion.ExperimentalExternalReport public static void reportData(String className, String testName, @IntRange(from=0L) long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, @IntRange(from=0L) int warmupIterations, @IntRange(from=0L) long thermalThrottleSleepSeconds, @IntRange(from=1L) int repeatIterations);
+    method public void resumeTiming();
+    field public static final androidx.benchmark.BenchmarkState.Companion Companion;
+  }
+
+  public static final class BenchmarkState.Companion {
+    method @SuppressCompatibility @androidx.benchmark.BenchmarkState.Companion.ExperimentalExternalReport public void reportData(String className, String testName, @IntRange(from=0L) long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, @IntRange(from=0L) int warmupIterations, @IntRange(from=0L) long thermalThrottleSleepSeconds, @IntRange(from=1L) int repeatIterations);
+  }
+
+  @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public static @interface BenchmarkState.Companion.ExperimentalExternalReport {
+  }
+
+  @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalBenchmarkConfigApi {
+  }
+
+  @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalBenchmarkStateApi {
+  }
+
+  @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public abstract class MetricCapture {
+    ctor public MetricCapture(java.util.List<java.lang.String> names);
+    method public abstract void capturePaused();
+    method public abstract void captureResumed();
+    method public abstract void captureStart(long timeNs);
+    method public abstract void captureStop(long timeNs, long[] output, int offset);
+    method public final java.util.List<java.lang.String> getNames();
+    property public final java.util.List<java.lang.String> names;
+  }
+
+  @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public final class MicrobenchmarkConfig {
+    ctor public MicrobenchmarkConfig(optional java.util.List<? extends androidx.benchmark.MetricCapture> metrics, optional boolean shouldEnableTraceAppTag, optional boolean shouldEnablePerfettoSdkTracing, optional androidx.benchmark.ProfilerConfig? profiler);
+    method public java.util.List<androidx.benchmark.MetricCapture> getMetrics();
+    method public androidx.benchmark.ProfilerConfig? getProfiler();
+    method public boolean getShouldEnablePerfettoSdkTracing();
+    method public boolean getShouldEnableTraceAppTag();
+    property public final java.util.List<androidx.benchmark.MetricCapture> metrics;
+    property public final androidx.benchmark.ProfilerConfig? profiler;
+    property public final boolean shouldEnablePerfettoSdkTracing;
+    property public final boolean shouldEnableTraceAppTag;
+  }
+
+  @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public abstract sealed class ProfilerConfig {
+  }
+
+  public static final class ProfilerConfig.MethodTracing extends androidx.benchmark.ProfilerConfig {
+    ctor public ProfilerConfig.MethodTracing();
+  }
+
+  public static final class ProfilerConfig.StackSampling extends androidx.benchmark.ProfilerConfig {
+    ctor public ProfilerConfig.StackSampling();
+  }
+
+  @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public final class TimeCapture extends androidx.benchmark.MetricCapture {
+    ctor public TimeCapture();
+    method public void capturePaused();
+    method public void captureResumed();
+    method public void captureStart(long timeNs);
+    method public void captureStop(long timeNs, long[] output, int offset);
+  }
+
+}
+
+package androidx.benchmark.perfetto {
+
+  @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface ExperimentalPerfettoCaptureApi {
+  }
+
+  @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public abstract sealed class PerfettoConfig {
+  }
+
+  public static final class PerfettoConfig.Binary extends androidx.benchmark.perfetto.PerfettoConfig {
+    ctor public PerfettoConfig.Binary(byte[] bytes);
+    method public byte[] getBytes();
+    property public final byte[] bytes;
+  }
+
+  public static final class PerfettoConfig.Text extends androidx.benchmark.perfetto.PerfettoConfig {
+    ctor public PerfettoConfig.Text(String text);
+    method public String getText();
+    property public final String text;
+  }
+
+  @SuppressCompatibility @RequiresApi(23) @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public final class PerfettoTrace {
+    ctor public PerfettoTrace(String path);
+    method public String getPath();
+    method public static void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, optional String? userspaceTracingPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, optional String? userspaceTracingPackage, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? traceCallback, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void record(String fileLabel, optional java.util.List<java.lang.String> appTagPackages, optional String? userspaceTracingPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void record(String fileLabel, optional java.util.List<java.lang.String> appTagPackages, optional String? userspaceTracingPackage, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? traceCallback, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void record(String fileLabel, optional java.util.List<java.lang.String> appTagPackages, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void record(String fileLabel, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    property public final String path;
+    field public static final androidx.benchmark.perfetto.PerfettoTrace.Companion Companion;
+  }
+
+  public static final class PerfettoTrace.Companion {
+    method public void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, optional String? userspaceTracingPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, optional String? userspaceTracingPackage, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? traceCallback, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public void record(String fileLabel, optional java.util.List<java.lang.String> appTagPackages, optional String? userspaceTracingPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public void record(String fileLabel, optional java.util.List<java.lang.String> appTagPackages, optional String? userspaceTracingPackage, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? traceCallback, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public void record(String fileLabel, optional java.util.List<java.lang.String> appTagPackages, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public void record(String fileLabel, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+  }
+
+}
+
diff --git a/benchmark/benchmark-common/api/current.ignore b/benchmark/benchmark-common/api/current.ignore
deleted file mode 100644
index f150239..0000000
--- a/benchmark/benchmark-common/api/current.ignore
+++ /dev/null
@@ -1,59 +0,0 @@
-// Baseline format: 1.0
-BecameUnchecked: androidx.benchmark.BenchmarkState#reportData(String, String, long, java.util.List<java.lang.Long>, int, long, int):
-    Removed method androidx.benchmark.BenchmarkState.reportData(String,String,long,java.util.List<java.lang.Long>,int,long,int) from compatibility checked API surface
-BecameUnchecked: androidx.benchmark.BenchmarkState#reportData(String, String, long, java.util.List<java.lang.Long>, int, long, int) parameter #0:
-    Removed parameter className in androidx.benchmark.BenchmarkState.reportData(String className, String testName, long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, int warmupIterations, long thermalThrottleSleepSeconds, int repeatIterations) from compatibility checked API surface
-BecameUnchecked: androidx.benchmark.BenchmarkState#reportData(String, String, long, java.util.List<java.lang.Long>, int, long, int) parameter #1:
-    Removed parameter testName in androidx.benchmark.BenchmarkState.reportData(String className, String testName, long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, int warmupIterations, long thermalThrottleSleepSeconds, int repeatIterations) from compatibility checked API surface
-BecameUnchecked: androidx.benchmark.BenchmarkState#reportData(String, String, long, java.util.List<java.lang.Long>, int, long, int) parameter #2:
-    Removed parameter totalRunTimeNs in androidx.benchmark.BenchmarkState.reportData(String className, String testName, long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, int warmupIterations, long thermalThrottleSleepSeconds, int repeatIterations) from compatibility checked API surface
-BecameUnchecked: androidx.benchmark.BenchmarkState#reportData(String, String, long, java.util.List<java.lang.Long>, int, long, int) parameter #3:
-    Removed parameter dataNs in androidx.benchmark.BenchmarkState.reportData(String className, String testName, long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, int warmupIterations, long thermalThrottleSleepSeconds, int repeatIterations) from compatibility checked API surface
-BecameUnchecked: androidx.benchmark.BenchmarkState#reportData(String, String, long, java.util.List<java.lang.Long>, int, long, int) parameter #4:
-    Removed parameter warmupIterations in androidx.benchmark.BenchmarkState.reportData(String className, String testName, long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, int warmupIterations, long thermalThrottleSleepSeconds, int repeatIterations) from compatibility checked API surface
-BecameUnchecked: androidx.benchmark.BenchmarkState#reportData(String, String, long, java.util.List<java.lang.Long>, int, long, int) parameter #5:
-    Removed parameter thermalThrottleSleepSeconds in androidx.benchmark.BenchmarkState.reportData(String className, String testName, long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, int warmupIterations, long thermalThrottleSleepSeconds, int repeatIterations) from compatibility checked API surface
-BecameUnchecked: androidx.benchmark.BenchmarkState#reportData(String, String, long, java.util.List<java.lang.Long>, int, long, int) parameter #6:
-    Removed parameter repeatIterations in androidx.benchmark.BenchmarkState.reportData(String className, String testName, long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, int warmupIterations, long thermalThrottleSleepSeconds, int repeatIterations) from compatibility checked API surface
-BecameUnchecked: androidx.benchmark.BenchmarkState.Companion#reportData(String, String, long, java.util.List<java.lang.Long>, int, long, int):
-    Removed method androidx.benchmark.BenchmarkState.Companion.reportData(String,String,long,java.util.List<java.lang.Long>,int,long,int) from compatibility checked API surface
-BecameUnchecked: androidx.benchmark.BenchmarkState.Companion#reportData(String, String, long, java.util.List<java.lang.Long>, int, long, int) parameter #0:
-    Removed parameter className in androidx.benchmark.BenchmarkState.Companion.reportData(String className, String testName, long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, int warmupIterations, long thermalThrottleSleepSeconds, int repeatIterations) from compatibility checked API surface
-BecameUnchecked: androidx.benchmark.BenchmarkState.Companion#reportData(String, String, long, java.util.List<java.lang.Long>, int, long, int) parameter #1:
-    Removed parameter testName in androidx.benchmark.BenchmarkState.Companion.reportData(String className, String testName, long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, int warmupIterations, long thermalThrottleSleepSeconds, int repeatIterations) from compatibility checked API surface
-BecameUnchecked: androidx.benchmark.BenchmarkState.Companion#reportData(String, String, long, java.util.List<java.lang.Long>, int, long, int) parameter #2:
-    Removed parameter totalRunTimeNs in androidx.benchmark.BenchmarkState.Companion.reportData(String className, String testName, long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, int warmupIterations, long thermalThrottleSleepSeconds, int repeatIterations) from compatibility checked API surface
-BecameUnchecked: androidx.benchmark.BenchmarkState.Companion#reportData(String, String, long, java.util.List<java.lang.Long>, int, long, int) parameter #3:
-    Removed parameter dataNs in androidx.benchmark.BenchmarkState.Companion.reportData(String className, String testName, long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, int warmupIterations, long thermalThrottleSleepSeconds, int repeatIterations) from compatibility checked API surface
-BecameUnchecked: androidx.benchmark.BenchmarkState.Companion#reportData(String, String, long, java.util.List<java.lang.Long>, int, long, int) parameter #4:
-    Removed parameter warmupIterations in androidx.benchmark.BenchmarkState.Companion.reportData(String className, String testName, long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, int warmupIterations, long thermalThrottleSleepSeconds, int repeatIterations) from compatibility checked API surface
-BecameUnchecked: androidx.benchmark.BenchmarkState.Companion#reportData(String, String, long, java.util.List<java.lang.Long>, int, long, int) parameter #5:
-    Removed parameter thermalThrottleSleepSeconds in androidx.benchmark.BenchmarkState.Companion.reportData(String className, String testName, long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, int warmupIterations, long thermalThrottleSleepSeconds, int repeatIterations) from compatibility checked API surface
-BecameUnchecked: androidx.benchmark.BenchmarkState.Companion#reportData(String, String, long, java.util.List<java.lang.Long>, int, long, int) parameter #6:
-    Removed parameter repeatIterations in androidx.benchmark.BenchmarkState.Companion.reportData(String className, String testName, long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, int warmupIterations, long thermalThrottleSleepSeconds, int repeatIterations) from compatibility checked API surface
-BecameUnchecked: androidx.benchmark.BenchmarkState.Companion.ExperimentalExternalReport:
-    Removed class androidx.benchmark.BenchmarkState.Companion.ExperimentalExternalReport from compatibility checked API surface
-
-
-RemovedClass: androidx.benchmark.Api21Kt:
-    Removed class androidx.benchmark.Api21Kt
-RemovedClass: androidx.benchmark.Api24Kt:
-    Removed class androidx.benchmark.Api24Kt
-RemovedClass: androidx.benchmark.Api27Kt:
-    Removed class androidx.benchmark.Api27Kt
-RemovedClass: androidx.benchmark.Api29Kt:
-    Removed class androidx.benchmark.Api29Kt
-RemovedClass: androidx.benchmark.ArgumentsKt:
-    Removed class androidx.benchmark.ArgumentsKt
-RemovedClass: androidx.benchmark.ConfigurationErrorKt:
-    Removed class androidx.benchmark.ConfigurationErrorKt
-RemovedClass: androidx.benchmark.MetricNameUtilsKt:
-    Removed class androidx.benchmark.MetricNameUtilsKt
-RemovedClass: androidx.benchmark.ProfilerKt:
-    Removed class androidx.benchmark.ProfilerKt
-RemovedClass: androidx.benchmark.UserspaceTracingKt:
-    Removed class androidx.benchmark.UserspaceTracingKt
-RemovedClass: androidx.benchmark.perfetto.PerfettoConfigKt:
-    Removed class androidx.benchmark.perfetto.PerfettoConfigKt
-RemovedClass: androidx.benchmark.perfetto.UiStateKt:
-    Removed class androidx.benchmark.perfetto.UiStateKt
diff --git a/benchmark/benchmark-common/api/current.txt b/benchmark/benchmark-common/api/current.txt
index 6369afc..eb18450 100644
--- a/benchmark/benchmark-common/api/current.txt
+++ b/benchmark/benchmark-common/api/current.txt
@@ -18,9 +18,53 @@
   @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public static @interface BenchmarkState.Companion.ExperimentalExternalReport {
   }
 
+  @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalBenchmarkConfigApi {
+  }
+
   @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalBenchmarkStateApi {
   }
 
+  @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public abstract class MetricCapture {
+    ctor public MetricCapture(java.util.List<java.lang.String> names);
+    method public abstract void capturePaused();
+    method public abstract void captureResumed();
+    method public abstract void captureStart(long timeNs);
+    method public abstract void captureStop(long timeNs, long[] output, int offset);
+    method public final java.util.List<java.lang.String> getNames();
+    property public final java.util.List<java.lang.String> names;
+  }
+
+  @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public final class MicrobenchmarkConfig {
+    ctor public MicrobenchmarkConfig(optional java.util.List<? extends androidx.benchmark.MetricCapture> metrics, optional boolean shouldEnableTraceAppTag, optional boolean shouldEnablePerfettoSdkTracing, optional androidx.benchmark.ProfilerConfig? profiler);
+    method public java.util.List<androidx.benchmark.MetricCapture> getMetrics();
+    method public androidx.benchmark.ProfilerConfig? getProfiler();
+    method public boolean getShouldEnablePerfettoSdkTracing();
+    method public boolean getShouldEnableTraceAppTag();
+    property public final java.util.List<androidx.benchmark.MetricCapture> metrics;
+    property public final androidx.benchmark.ProfilerConfig? profiler;
+    property public final boolean shouldEnablePerfettoSdkTracing;
+    property public final boolean shouldEnableTraceAppTag;
+  }
+
+  @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public abstract sealed class ProfilerConfig {
+  }
+
+  public static final class ProfilerConfig.MethodTracing extends androidx.benchmark.ProfilerConfig {
+    ctor public ProfilerConfig.MethodTracing();
+  }
+
+  public static final class ProfilerConfig.StackSampling extends androidx.benchmark.ProfilerConfig {
+    ctor public ProfilerConfig.StackSampling();
+  }
+
+  @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public final class TimeCapture extends androidx.benchmark.MetricCapture {
+    ctor public TimeCapture();
+    method public void capturePaused();
+    method public void captureResumed();
+    method public void captureStart(long timeNs);
+    method public void captureStop(long timeNs, long[] output, int offset);
+  }
+
 }
 
 package androidx.benchmark.perfetto {
diff --git a/benchmark/benchmark-common/api/res-1.2.0-beta01.txt b/benchmark/benchmark-common/api/res-1.2.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/benchmark/benchmark-common/api/res-1.2.0-beta01.txt
diff --git a/benchmark/benchmark-common/api/restricted_1.2.0-beta01.txt b/benchmark/benchmark-common/api/restricted_1.2.0-beta01.txt
new file mode 100644
index 0000000..fb75b90
--- /dev/null
+++ b/benchmark/benchmark-common/api/restricted_1.2.0-beta01.txt
@@ -0,0 +1,119 @@
+// Signature format: 4.0
+package androidx.benchmark {
+
+  public final class BenchmarkState {
+    ctor @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkStateApi public BenchmarkState(optional Integer? warmupCount, optional Integer? repeatCount);
+    method @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkStateApi public java.util.List<java.lang.Double> getMeasurementTimeNs();
+    method public boolean keepRunning();
+    method @kotlin.PublishedApi internal boolean keepRunningInternal();
+    method public void pauseTiming();
+    method @SuppressCompatibility @androidx.benchmark.BenchmarkState.Companion.ExperimentalExternalReport public static void reportData(String className, String testName, @IntRange(from=0L) long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, @IntRange(from=0L) int warmupIterations, @IntRange(from=0L) long thermalThrottleSleepSeconds, @IntRange(from=1L) int repeatIterations);
+    method public void resumeTiming();
+    field public static final androidx.benchmark.BenchmarkState.Companion Companion;
+    field @kotlin.PublishedApi internal int iterationsRemaining;
+  }
+
+  public static final class BenchmarkState.Companion {
+    method @SuppressCompatibility @androidx.benchmark.BenchmarkState.Companion.ExperimentalExternalReport public void reportData(String className, String testName, @IntRange(from=0L) long totalRunTimeNs, java.util.List<java.lang.Long> dataNs, @IntRange(from=0L) int warmupIterations, @IntRange(from=0L) long thermalThrottleSleepSeconds, @IntRange(from=1L) int repeatIterations);
+  }
+
+  @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public static @interface BenchmarkState.Companion.ExperimentalExternalReport {
+  }
+
+  @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalBenchmarkConfigApi {
+  }
+
+  @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalBenchmarkStateApi {
+  }
+
+  @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public abstract class MetricCapture {
+    ctor public MetricCapture(java.util.List<java.lang.String> names);
+    method public abstract void capturePaused();
+    method public abstract void captureResumed();
+    method public abstract void captureStart(long timeNs);
+    method public abstract void captureStop(long timeNs, long[] output, int offset);
+    method public final java.util.List<java.lang.String> getNames();
+    property public final java.util.List<java.lang.String> names;
+  }
+
+  @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public final class MicrobenchmarkConfig {
+    ctor public MicrobenchmarkConfig(optional java.util.List<? extends androidx.benchmark.MetricCapture> metrics, optional boolean shouldEnableTraceAppTag, optional boolean shouldEnablePerfettoSdkTracing, optional androidx.benchmark.ProfilerConfig? profiler);
+    method public java.util.List<androidx.benchmark.MetricCapture> getMetrics();
+    method public androidx.benchmark.ProfilerConfig? getProfiler();
+    method public boolean getShouldEnablePerfettoSdkTracing();
+    method public boolean getShouldEnableTraceAppTag();
+    property public final java.util.List<androidx.benchmark.MetricCapture> metrics;
+    property public final androidx.benchmark.ProfilerConfig? profiler;
+    property public final boolean shouldEnablePerfettoSdkTracing;
+    property public final boolean shouldEnableTraceAppTag;
+  }
+
+  @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public abstract sealed class ProfilerConfig {
+  }
+
+  public static final class ProfilerConfig.MethodTracing extends androidx.benchmark.ProfilerConfig {
+    ctor public ProfilerConfig.MethodTracing();
+  }
+
+  public static final class ProfilerConfig.StackSampling extends androidx.benchmark.ProfilerConfig {
+    ctor public ProfilerConfig.StackSampling();
+  }
+
+  @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public final class TimeCapture extends androidx.benchmark.MetricCapture {
+    ctor public TimeCapture();
+    method public void capturePaused();
+    method public void captureResumed();
+    method public void captureStart(long timeNs);
+    method public void captureStop(long timeNs, long[] output, int offset);
+  }
+
+}
+
+package androidx.benchmark.perfetto {
+
+  @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface ExperimentalPerfettoCaptureApi {
+  }
+
+  @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public abstract sealed class PerfettoConfig {
+  }
+
+  public static final class PerfettoConfig.Binary extends androidx.benchmark.perfetto.PerfettoConfig {
+    ctor public PerfettoConfig.Binary(byte[] bytes);
+    method public byte[] getBytes();
+    property public final byte[] bytes;
+  }
+
+  public static final class PerfettoConfig.Text extends androidx.benchmark.perfetto.PerfettoConfig {
+    ctor public PerfettoConfig.Text(String text);
+    method public String getText();
+    property public final String text;
+  }
+
+  @SuppressCompatibility @RequiresApi(23) @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public final class PerfettoTrace {
+    ctor public PerfettoTrace(String path);
+    method public String getPath();
+    method public static void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, optional String? userspaceTracingPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, optional String? userspaceTracingPackage, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? traceCallback, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void record(String fileLabel, optional java.util.List<java.lang.String> appTagPackages, optional String? userspaceTracingPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void record(String fileLabel, optional java.util.List<java.lang.String> appTagPackages, optional String? userspaceTracingPackage, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? traceCallback, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void record(String fileLabel, optional java.util.List<java.lang.String> appTagPackages, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public static void record(String fileLabel, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    property public final String path;
+    field public static final androidx.benchmark.perfetto.PerfettoTrace.Companion Companion;
+  }
+
+  public static final class PerfettoTrace.Companion {
+    method public void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, optional String? userspaceTracingPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, optional String? userspaceTracingPackage, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? traceCallback, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, optional String highlightPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public void record(String fileLabel, androidx.benchmark.perfetto.PerfettoConfig config, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public void record(String fileLabel, optional java.util.List<java.lang.String> appTagPackages, optional String? userspaceTracingPackage, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public void record(String fileLabel, optional java.util.List<java.lang.String> appTagPackages, optional String? userspaceTracingPackage, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? traceCallback, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public void record(String fileLabel, optional java.util.List<java.lang.String> appTagPackages, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+    method public void record(String fileLabel, kotlin.jvm.functions.Function0<kotlin.Unit> block);
+  }
+
+}
+
diff --git a/benchmark/benchmark-common/api/restricted_current.ignore b/benchmark/benchmark-common/api/restricted_current.ignore
deleted file mode 100644
index bd6e5a8..0000000
--- a/benchmark/benchmark-common/api/restricted_current.ignore
+++ /dev/null
@@ -1,23 +0,0 @@
-// Baseline format: 1.0
-RemovedClass: androidx.benchmark.Api21Kt:
-    Removed class androidx.benchmark.Api21Kt
-RemovedClass: androidx.benchmark.Api24Kt:
-    Removed class androidx.benchmark.Api24Kt
-RemovedClass: androidx.benchmark.Api27Kt:
-    Removed class androidx.benchmark.Api27Kt
-RemovedClass: androidx.benchmark.Api29Kt:
-    Removed class androidx.benchmark.Api29Kt
-RemovedClass: androidx.benchmark.ArgumentsKt:
-    Removed class androidx.benchmark.ArgumentsKt
-RemovedClass: androidx.benchmark.ConfigurationErrorKt:
-    Removed class androidx.benchmark.ConfigurationErrorKt
-RemovedClass: androidx.benchmark.MetricNameUtilsKt:
-    Removed class androidx.benchmark.MetricNameUtilsKt
-RemovedClass: androidx.benchmark.ProfilerKt:
-    Removed class androidx.benchmark.ProfilerKt
-RemovedClass: androidx.benchmark.UserspaceTracingKt:
-    Removed class androidx.benchmark.UserspaceTracingKt
-RemovedClass: androidx.benchmark.perfetto.PerfettoConfigKt:
-    Removed class androidx.benchmark.perfetto.PerfettoConfigKt
-RemovedClass: androidx.benchmark.perfetto.UiStateKt:
-    Removed class androidx.benchmark.perfetto.UiStateKt
diff --git a/benchmark/benchmark-common/api/restricted_current.txt b/benchmark/benchmark-common/api/restricted_current.txt
index 883a688..fb75b90 100644
--- a/benchmark/benchmark-common/api/restricted_current.txt
+++ b/benchmark/benchmark-common/api/restricted_current.txt
@@ -20,9 +20,53 @@
   @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public static @interface BenchmarkState.Companion.ExperimentalExternalReport {
   }
 
+  @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalBenchmarkConfigApi {
+  }
+
   @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalBenchmarkStateApi {
   }
 
+  @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public abstract class MetricCapture {
+    ctor public MetricCapture(java.util.List<java.lang.String> names);
+    method public abstract void capturePaused();
+    method public abstract void captureResumed();
+    method public abstract void captureStart(long timeNs);
+    method public abstract void captureStop(long timeNs, long[] output, int offset);
+    method public final java.util.List<java.lang.String> getNames();
+    property public final java.util.List<java.lang.String> names;
+  }
+
+  @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public final class MicrobenchmarkConfig {
+    ctor public MicrobenchmarkConfig(optional java.util.List<? extends androidx.benchmark.MetricCapture> metrics, optional boolean shouldEnableTraceAppTag, optional boolean shouldEnablePerfettoSdkTracing, optional androidx.benchmark.ProfilerConfig? profiler);
+    method public java.util.List<androidx.benchmark.MetricCapture> getMetrics();
+    method public androidx.benchmark.ProfilerConfig? getProfiler();
+    method public boolean getShouldEnablePerfettoSdkTracing();
+    method public boolean getShouldEnableTraceAppTag();
+    property public final java.util.List<androidx.benchmark.MetricCapture> metrics;
+    property public final androidx.benchmark.ProfilerConfig? profiler;
+    property public final boolean shouldEnablePerfettoSdkTracing;
+    property public final boolean shouldEnableTraceAppTag;
+  }
+
+  @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public abstract sealed class ProfilerConfig {
+  }
+
+  public static final class ProfilerConfig.MethodTracing extends androidx.benchmark.ProfilerConfig {
+    ctor public ProfilerConfig.MethodTracing();
+  }
+
+  public static final class ProfilerConfig.StackSampling extends androidx.benchmark.ProfilerConfig {
+    ctor public ProfilerConfig.StackSampling();
+  }
+
+  @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public final class TimeCapture extends androidx.benchmark.MetricCapture {
+    ctor public TimeCapture();
+    method public void capturePaused();
+    method public void captureResumed();
+    method public void captureStart(long timeNs);
+    method public void captureStop(long timeNs, long[] output, int offset);
+  }
+
 }
 
 package androidx.benchmark.perfetto {
diff --git a/benchmark/benchmark-common/build.gradle b/benchmark/benchmark-common/build.gradle
index c335fcb..e8d4694 100644
--- a/benchmark/benchmark-common/build.gradle
+++ b/benchmark/benchmark-common/build.gradle
@@ -70,7 +70,7 @@
     api("androidx.annotation:annotation-experimental:1.0.0")
     implementation("androidx.tracing:tracing-ktx:1.0.0")
     implementation(project(":tracing:tracing-perfetto-handshake"))
-    implementation(libs.testMonitor)
+    implementation("androidx.test:monitor:1.6.1")
     implementation(libs.wireRuntime)
 
     androidTestImplementation(libs.testRules)
@@ -89,7 +89,8 @@
     kotlinOptions {
         // Enable using experimental APIs from within same version group
         freeCompilerArgs += [
-                "-opt-in=androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi"
+                "-opt-in=androidx.benchmark.ExperimentalBenchmarkConfigApi",
+                "-opt-in=androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi",
         ]
     }
 }
diff --git a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/BenchmarkStateConfigTest.kt b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/BenchmarkStateConfigTest.kt
index 9782080..100979c 100644
--- a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/BenchmarkStateConfigTest.kt
+++ b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/BenchmarkStateConfigTest.kt
@@ -80,7 +80,7 @@
             profiler = null,
             warmupCount = 100,
             measurementCount = 1000,
-            cpuEventCountersMask = 0x0,
+            metrics = arrayOf(TimeCapture()),
         ),
         expectedWarmups = 0,
         expectedMeasurements = 1,
@@ -96,7 +96,7 @@
             profiler = null,
             warmupCount = 100,
             measurementCount = 1000,
-            cpuEventCountersMask = 0x0,
+            metrics = arrayOf(TimeCapture()),
         ),
         expectedWarmups = 0,
         expectedMeasurements = 10,
@@ -113,7 +113,7 @@
             profiler = null,
             warmupCount = null,
             measurementCount = null,
-            cpuEventCountersMask = 0x0,
+            metrics = arrayOf(TimeCapture()),
         ),
         expectedWarmups = null,
         expectedMeasurements = 55, // includes allocations
@@ -129,7 +129,7 @@
             profiler = null,
             warmupCount = 10,
             measurementCount = 100,
-            cpuEventCountersMask = 0x0,
+            metrics = arrayOf(TimeCapture()),
         ),
         expectedWarmups = 10,
         expectedMeasurements = 105, // includes allocations
@@ -145,7 +145,7 @@
             profiler = MethodTracing,
             warmupCount = 5,
             measurementCount = 10,
-            cpuEventCountersMask = 0x0,
+            metrics = arrayOf(TimeCapture()),
         ),
         expectedWarmups = 5,
         expectedMeasurements = 15, // profiler not measured, not accounted for here
@@ -162,7 +162,7 @@
             profiler = MethodTracing,
             warmupCount = 100,
             measurementCount = 10,
-            cpuEventCountersMask = 0x0,
+            metrics = arrayOf(TimeCapture()),
         ),
         expectedWarmups = 100,
         expectedMeasurements = 10,
diff --git a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt
index 393f8df..c2ddc3a 100644
--- a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt
+++ b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt
@@ -287,47 +287,46 @@
     }
 
     private fun validateProfilerUsage(simplifiedTimingOnlyMode: Boolean?) {
-        try {
-            profilerOverride = StackSamplingLegacy
+        val config = MicrobenchmarkConfig(profiler = ProfilerConfig.StackSamplingLegacy())
 
-            val benchmarkState = if (simplifiedTimingOnlyMode != null) {
-                BenchmarkState(simplifiedTimingOnlyMode = simplifiedTimingOnlyMode)
+        val benchmarkState = if (simplifiedTimingOnlyMode != null) {
+            BenchmarkState(
+                config = config,
+                simplifiedTimingOnlyMode = simplifiedTimingOnlyMode
+            )
+        } else {
+            BenchmarkState(config)
+        }
+
+        // count iters with profiler enabled vs disabled
+        var profilerDisabledIterations = 0
+        var profilerEnabledIterations = 0
+        var profilerAllocationIterations = 0
+        while (benchmarkState.keepRunning()) {
+            if (StackSamplingLegacy.isRunning) {
+                profilerEnabledIterations++
             } else {
-                BenchmarkState()
-            }
+                profilerDisabledIterations++
 
-            // count iters with profiler enabled vs disabled
-            var profilerDisabledIterations = 0
-            var profilerEnabledIterations = 0
-            var profilerAllocationIterations = 0
-            while (benchmarkState.keepRunning()) {
-                if (StackSamplingLegacy.isRunning) {
-                    profilerEnabledIterations++
-                } else {
-                    profilerDisabledIterations++
-
-                    if (profilerEnabledIterations != 0) {
-                        // profiler will only be disabled after running during allocation phase
-                        profilerAllocationIterations++
-                    }
+                if (profilerEnabledIterations != 0) {
+                    // profiler will only be disabled after running during allocation phase
+                    profilerAllocationIterations++
                 }
             }
+        }
 
-            if (simplifiedTimingOnlyMode == true) {
-                // profiler should be always disabled
-                assertNotEquals(0, profilerDisabledIterations)
-                assertEquals(0, profilerEnabledIterations)
-                assertEquals(0, profilerAllocationIterations)
-            } else {
-                // first, profiler disabled (timing) ...
-                assertNotEquals(0, profilerDisabledIterations)
-                // then enabled (profiling) ...
-                assertNotEquals(0, profilerEnabledIterations)
-                // then disabled again (allocs)
-                assertNotEquals(0, profilerAllocationIterations)
-            }
-        } finally {
-            profilerOverride = null
+        if (simplifiedTimingOnlyMode == true) {
+            // profiler should be always disabled
+            assertNotEquals(0, profilerDisabledIterations)
+            assertEquals(0, profilerEnabledIterations)
+            assertEquals(0, profilerAllocationIterations)
+        } else {
+            // first, profiler disabled (timing) ...
+            assertNotEquals(0, profilerDisabledIterations)
+            // then enabled (profiling) ...
+            assertNotEquals(0, profilerEnabledIterations)
+            // then disabled again (allocs)
+            assertNotEquals(0, profilerAllocationIterations)
         }
     }
 
diff --git a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/DeviceInfoTest.kt b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/DeviceInfoTest.kt
new file mode 100644
index 0000000..bd91e74
--- /dev/null
+++ b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/DeviceInfoTest.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.benchmark
+
+import androidx.benchmark.perfetto.PerfettoHelper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import kotlin.test.assertFalse
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceInfoTest {
+    @SdkSuppress(minSdkVersion = PerfettoHelper.MIN_SDK_VERSION)
+    @Test
+    fun misconfiguredForTracing() {
+        // NOTE: tests device capability, not implementation of DeviceInfo
+        assertFalse(
+            DeviceInfo.misconfiguredForTracing,
+            "${DeviceInfo.typeLabel} is incorrectly configured for tracing," +
+                " and is not CTS compatible. All Perfetto/Atrace capture will fail."
+        )
+    }
+}
diff --git a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoHelperTest.kt b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoHelperTest.kt
index 4df7561..8097084b 100644
--- a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoHelperTest.kt
+++ b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/PerfettoHelperTest.kt
@@ -19,7 +19,7 @@
 import androidx.benchmark.perfetto.PerfettoCapture
 import androidx.benchmark.perfetto.PerfettoConfig
 import androidx.benchmark.perfetto.PerfettoHelper
-import androidx.benchmark.perfetto.PerfettoHelper.Companion.LOWEST_BUNDLED_VERSION_SUPPORTED
+import androidx.benchmark.perfetto.PerfettoHelper.Companion.MIN_BUNDLED_SDK_VERSION
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
@@ -34,7 +34,7 @@
 import org.junit.runner.RunWith
 
 @LargeTest
-@SdkSuppress(minSdkVersion = 23)
+@SdkSuppress(minSdkVersion = PerfettoHelper.MIN_SDK_VERSION)
 @RunWith(AndroidJUnit4::class)
 class PerfettoHelperTest {
     @Before
@@ -71,7 +71,7 @@
         assertFalse(capture.isRunning())
     }
 
-    @SdkSuppress(minSdkVersion = LOWEST_BUNDLED_VERSION_SUPPORTED)
+    @SdkSuppress(minSdkVersion = MIN_BUNDLED_SDK_VERSION)
     @Test
     fun stopAllPerfettoProcesses_bundled() = validateStopAllPerfettoProcesses(unbundled = false)
 
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Arguments.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Arguments.kt
index 50b3f6b..f82a89b 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Arguments.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Arguments.kt
@@ -31,12 +31,6 @@
 @VisibleForTesting
 public var argumentSource: Bundle? = null
 
-/**
- * Allows tests to override profiler
- */
-@VisibleForTesting
-internal var profilerOverride: Profiler? = null
-
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 object Arguments {
     // public properties are shared by micro + macro benchmarks
@@ -50,7 +44,14 @@
      *
      * Currently internal/experimental
      */
-    val fullTracingEnable: Boolean
+    private val _fullTracingEnable: Boolean
+    val fullTracingEnable: Boolean get() = fullTracingEnableOverride ?: _fullTracingEnable
+
+    /**
+     * Allows tests to override whether full tracing is enabled
+     */
+    @VisibleForTesting
+    var fullTracingEnableOverride: Boolean? = null
 
     val enabledRules: Set<RuleType>
 
@@ -69,9 +70,7 @@
     internal val outputEnable: Boolean
     internal val startupMode: Boolean
     internal val iterations: Int?
-    private val _profiler: Profiler?
     internal val profiler: Profiler?
-        get() = if (profilerOverride != null) profilerOverride else _profiler
     internal val profilerSampleFrequency: Int
     internal val profilerSampleDurationSeconds: Long
     internal val thermalThrottleSleepDurationSeconds: Long
@@ -121,7 +120,7 @@
         iterations =
             arguments.getBenchmarkArgument("iterations")?.toInt()
 
-        fullTracingEnable =
+        _fullTracingEnable =
             (arguments.getBenchmarkArgument("fullTracing.enable")?.toBoolean() ?: false)
 
         // Transform comma-delimited list into set of suppressed errors
@@ -164,7 +163,7 @@
         enableCompilation =
             arguments.getBenchmarkArgument("compilation.enabled")?.toBoolean() ?: !dryRunMode
 
-        _profiler = arguments.getProfiler(outputEnable)
+        profiler = arguments.getProfiler(outputEnable)
         profilerSampleFrequency =
             arguments.getBenchmarkArgument("profiling.sampleFrequency")?.ifBlank { null }
                 ?.toInt()
@@ -173,10 +172,10 @@
             arguments.getBenchmarkArgument("profiling.sampleDurationSeconds")?.ifBlank { null }
                 ?.toLong()
                 ?: 5
-        if (_profiler != null) {
+        if (profiler != null) {
             Log.d(
                 BenchmarkState.TAG,
-                "Profiler ${_profiler.javaClass.simpleName}, freq " +
+                "Profiler ${profiler.javaClass.simpleName}, freq " +
                     "$profilerSampleFrequency, duration $profilerSampleDurationSeconds"
             )
         }
@@ -215,7 +214,7 @@
     fun methodTracingEnabled(): Boolean {
         return when {
             dryRunMode -> false
-            _profiler == MethodTracing -> true
+            profiler == MethodTracing -> true
             else -> false
         }
     }
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/BenchmarkState.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/BenchmarkState.kt
index 22c18e8..b6ab1fc 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/BenchmarkState.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/BenchmarkState.kt
@@ -77,21 +77,28 @@
      * Constructor used for standard uses of BenchmarkState, e.g. in BenchmarkRule
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    constructor() : this(warmupCount = null, simplifiedTimingOnlyMode = false)
+    constructor(
+        config: MicrobenchmarkConfig? = null
+    ) : this(
+        warmupCount = null,
+        simplifiedTimingOnlyMode = false,
+        config = config
+    )
 
     internal constructor(
         warmupCount: Int? = null,
         measurementCount: Int? = null,
-        simplifiedTimingOnlyMode: Boolean = false
+        simplifiedTimingOnlyMode: Boolean = false,
+        config: MicrobenchmarkConfig? = null
     ) : this(
         MicrobenchmarkPhase.Config(
             dryRunMode = Arguments.dryRunMode,
             startupMode = Arguments.startupMode,
-            profiler = Arguments.profiler,
+            profiler = config?.profiler?.profiler ?: Arguments.profiler,
             warmupCount = warmupCount,
             measurementCount = Arguments.iterations ?: measurementCount,
             simplifiedTimingOnlyMode = simplifiedTimingOnlyMode,
-            cpuEventCountersMask = Arguments.cpuEventCounterMask
+            metrics = config?.metrics?.toTypedArray() ?: DEFAULT_METRICS
         )
     )
 
@@ -544,6 +551,19 @@
 
         private var firstBenchmark = true
 
+        private val DEFAULT_METRICS: Array<MetricCapture> =
+            if (Arguments.cpuEventCounterMask != 0) {
+                arrayOf(
+                    TimeCapture(),
+                    CpuEventCounterCapture(
+                        MicrobenchmarkPhase.cpuEventCounter,
+                        Arguments.cpuEventCounterMask
+                    )
+                )
+            } else {
+                arrayOf(TimeCapture())
+            }
+
         @RequiresOptIn
         @Retention(AnnotationRetention.BINARY)
         @Target(AnnotationTarget.FUNCTION)
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/DeviceInfo.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/DeviceInfo.kt
index 9b81627..a8169f6 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/DeviceInfo.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/DeviceInfo.kt
@@ -37,6 +37,8 @@
         Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic") ||
         "google_sdk" == Build.PRODUCT
 
+    val typeLabel = if (isEmulator) "emulator" else "device"
+
     val isEngBuild = Build.FINGERPRINT.contains(":eng/")
 
     val isRooted = Build.FINGERPRINT.contains(":userdebug/") ||
@@ -78,6 +80,14 @@
      */
     val errors: List<ConfigurationError>
 
+    /**
+     * Tracks whether the virtual kernel files have been properly configured on this OS build.
+     *
+     * If not, only recourse is to try a different device.
+     */
+    val misconfiguredForTracing = !File("/sys/kernel/tracing/trace_marker").exists() &&
+        !File("/sys/kernel/debug/tracing/trace_marker").exists()
+
     init {
         val context = InstrumentationRegistry.getInstrumentation().targetContext
 
diff --git a/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material3/IncludeFontPaddingHelper.desktop.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/ExperimentalBenchmarkConfigApi.kt
similarity index 61%
rename from compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material3/IncludeFontPaddingHelper.desktop.kt
rename to benchmark/benchmark-common/src/main/java/androidx/benchmark/ExperimentalBenchmarkConfigApi.kt
index 27e6c24..30314c7 100644
--- a/compose/material3/material3/src/desktopMain/kotlin/androidx/compose/material3/IncludeFontPaddingHelper.desktop.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/ExperimentalBenchmarkConfigApi.kt
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-package androidx.compose.material3
+package androidx.benchmark
 
-import androidx.compose.ui.text.TextStyle
-
-// TODO(b/237588251) remove this once the default includeFontPadding is false
-/* NOOP includeFontPadding doesn't exist on desktop */
-internal actual fun copyAndSetFontPadding(
-    style: TextStyle,
-    includeFontPadding: Boolean
-): TextStyle = style
+/**
+ * Annotation indicating experimental API primarily intended to allow configuration of
+ * microbenchmarks from code, overriding instrumentation arguments
+ * like `androidx.benchmark.profiling.mode`, and custom microbenchmark metrics.
+ */
+@RequiresOptIn
+@Retention(AnnotationRetention.BINARY)
+public annotation class ExperimentalBenchmarkConfigApi
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/MetricCapture.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/MetricCapture.kt
index 5ef654e..2d7a00f 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/MetricCapture.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/MetricCapture.kt
@@ -18,35 +18,56 @@
 
 import android.os.Debug
 
-internal abstract class MetricCapture(
+/**
+ * Microbenchmark metric.
+ *
+ * Note that the API is designed around low overhead, even in the case of multiple submetrics (such
+ * as cpu perf event counters) that must be started/stopped together for efficiency.
+ *
+ * This class may be initialized on a different thread from where measurement occurs, but all
+ * `capture` methods must be invoked from the same thread.
+ */
+@ExperimentalBenchmarkConfigApi
+abstract class MetricCapture(
     val names: List<String>
 ) {
     /**
      * Starts collecting data for a run.
      *
-     * Must be called at the start of each run.
+     * Called at the start of each run.
      */
     abstract fun captureStart(timeNs: Long)
 
     /**
-     * Marks the end of a run, and stores one metric value in the output array since the last call
-     * to start.
+     * Mark the end of a run, and store offset metrics in the output array, per sub metric.
      *
-     * Should be called when a run stops.
+     * To output values, store them in the output array offset by both the parameter offset,
+     * and their submetric index, for example:
+     *
+     * ```
+     * class MyMetricCapture("firstSubMetricName", "secondSubMetricName") {
+     *     //...
+     *     override fun captureStop(timeNs: Long, output: LongArray, offset: Int) {
+     *         output[offset + 0] = firstSubMetricValue
+     *         output[offset + 1] = secondSubMetricValue
+     *     }
+     * }
+     * ```
+     *
+     * @param timeNs Time of metric capture start, in monotonic time (System..
+     * @param output LongArray sized to hold all simultaneous sub metric outputs, use `offset` as
+     *  the initial position in `output` to start writing submetrics.
+     * @param offset Offset into the output array to start writing sub metrics.
      */
     abstract fun captureStop(timeNs: Long, output: LongArray, offset: Int)
 
     /**
-     * Pauses data collection.
-     *
-     * Call when you want to not capture the following part of a run.
+     * Pause data collection.
      */
     abstract fun capturePaused()
 
     /**
-     * Resumes data collection.
-     *
-     * Call when you want to resume capturing a capturePaused-ed run.
+     * Resume data collection
      */
     abstract fun captureResumed()
 
@@ -59,7 +80,13 @@
     }
 }
 
-internal class TimeCapture : MetricCapture(
+/**
+ * Time metric, which reports time in nanos, based on the time passed to [captureStop].
+ *
+ * Reports "timeNs"
+ */
+@ExperimentalBenchmarkConfigApi
+public class TimeCapture : MetricCapture(
     names = listOf("timeNs")
 ) {
     private var currentStarted = 0L
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/MicrobenchmarkConfig.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/MicrobenchmarkConfig.kt
new file mode 100644
index 0000000..52e5c3e
--- /dev/null
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/MicrobenchmarkConfig.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.benchmark
+
+/**
+ * Experimental config object for microbenchmarks for defining custom metrics, tracing behavior,
+ * and profiling, which overrides options set in
+ * [instrumentation arguments](https://developer.android.com/topic/performance/benchmarking/microbenchmark-instrumentation-args).
+ */
+@ExperimentalBenchmarkConfigApi
+class MicrobenchmarkConfig constructor(
+    /**
+     * Timing metrics for primary phase, post-warmup
+     *
+     * Defaults to [TimeCapture].
+     */
+    val metrics: List<MetricCapture> = listOf(TimeCapture()),
+
+    /**
+     * Set to true to enable capture of `trace("foo") {}` blocks in the output Perfetto trace.
+     *
+     * Defaults to false to minimize interference.
+     */
+    val shouldEnableTraceAppTag: Boolean = false,
+
+    /**
+     * Set to true to enable capture of tracing-perfetto trace events, such as in Compose
+     * composition tracing.
+     *
+     * Defaults to false to minimize interference.
+     */
+    val shouldEnablePerfettoSdkTracing: Boolean = false,
+
+    /**
+     * Optional profiler to be used after the primary timing phase.
+     */
+    val profiler: ProfilerConfig? = null,
+)
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/MicrobenchmarkPhase.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/MicrobenchmarkPhase.kt
index eb6bf1f1..e8cba451 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/MicrobenchmarkPhase.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/MicrobenchmarkPhase.kt
@@ -103,7 +103,7 @@
         private val THROTTLE_BACKOFF_S = Arguments.thermalThrottleSleepDurationSeconds
 
         // static instance ensures there's only one, and we don't leak native memory
-        private val cpuEventCounter: CpuEventCounter by lazy {
+        internal val cpuEventCounter: CpuEventCounter by lazy {
             // As this is only ever enabled by experimental arguments, we force enable this
             // permanently once the first benchmark uses it, for local runs only.
             CpuEventCounter.forceEnable()?.let { errorMessage ->
@@ -146,19 +146,12 @@
             loopMode: LoopMode,
             measurementCount: Int,
             simplifiedTimingOnlyMode: Boolean,
-            cpuEventCountersMask: Int,
+            metrics: Array<MetricCapture>
         ) = MicrobenchmarkPhase(
             label = "Benchmark Time",
             measurementCount = measurementCount,
             loopMode = loopMode,
-            metrics = if (cpuEventCountersMask != 0) {
-                arrayOf(
-                    TimeCapture(),
-                    CpuEventCounterCapture(cpuEventCounter, cpuEventCountersMask)
-                )
-            } else {
-                arrayOf(TimeCapture())
-            },
+            metrics = metrics,
             thermalThrottleSleepsMax = if (simplifiedTimingOnlyMode) 0 else 2
         )
 
@@ -205,7 +198,7 @@
         val profiler: Profiler?,
         val warmupCount: Int?,
         val measurementCount: Int?,
-        val cpuEventCountersMask: Int,
+        val metrics: Array<MetricCapture>,
     ) {
         val warmupManager = WarmupManager(overrideCount = warmupCount)
         init {
@@ -237,14 +230,16 @@
                         // only timing phase has a complex impl of pause/resume, then behavior
                         // changes drastically, and the warmupManager will estimate a far faster
                         // impl of `measureRepeated { runWithTimingDisabled }`
-                        collectCpuEventInstructions = cpuEventCountersMask != 0
+                        collectCpuEventInstructions = metrics.any {
+                            it is CpuEventCounterCapture && it.names.isNotEmpty()
+                        }
                     ),
                     // Regular timing phase
                     timingMeasurementPhase(
                         measurementCount = measurementCount ?: 50,
                         loopMode = loopMode,
-                        simplifiedTimingOnlyMode = simplifiedTimingOnlyMode,
-                        cpuEventCountersMask = cpuEventCountersMask
+                        metrics = metrics,
+                        simplifiedTimingOnlyMode = simplifiedTimingOnlyMode
                     ),
                     if (simplifiedTimingOnlyMode || profiler == null) {
                         null
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/ProfilerConfig.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/ProfilerConfig.kt
new file mode 100644
index 0000000..1038397
--- /dev/null
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/ProfilerConfig.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.benchmark
+
+import android.os.Build
+
+/**
+ * Profiler configuration object.
+ *
+ * Will eventually allow further configuration, e.g. sampling frequency.
+ */
+@ExperimentalBenchmarkConfigApi
+sealed class ProfilerConfig(
+    internal val profiler: Profiler
+) {
+    class StackSampling() : ProfilerConfig(
+        profiler = if (Build.VERSION.SDK_INT >= 29) {
+            StackSamplingSimpleperf
+        } else {
+            StackSamplingLegacy
+        }
+    )
+
+    class MethodTracing() : ProfilerConfig(MethodTracing)
+
+    // used for tests
+    internal class StackSamplingLegacy() : ProfilerConfig(profiler = StackSamplingLegacy)
+}
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCapture.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCapture.kt
index d6c81da3..8cd40a0 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCapture.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCapture.kt
@@ -22,6 +22,7 @@
 import androidx.annotation.RestrictTo
 import androidx.benchmark.Outputs
 import androidx.benchmark.Shell
+import androidx.benchmark.perfetto.PerfettoCapture.PerfettoSdkConfig.InitialProcessState
 import androidx.benchmark.perfetto.PerfettoHelper.Companion.isAbiSupported
 import androidx.benchmark.userspaceTrace
 import androidx.test.platform.app.InstrumentationRegistry
@@ -85,17 +86,24 @@
         helper.stopCollecting(destinationPath)
     }
 
-    /**
-     * Enables Perfetto SDK tracing in an app if present. Provides required binary dependencies to
-     * the app if they're missing and the [provideBinariesIfMissing] parameter is set to `true`.
-     *
-     * Note: if the app process is not running, it will be launched making the method a bad choice
-     * for cold tracing. TODO(245426369): implement cold startup tracing support
-     */
+    /** Enables Perfetto SDK tracing in an app if present */
     @RequiresApi(30) // TODO(234351579): Support API < 30
-    fun enableAndroidxTracingPerfetto(
+    fun enableAndroidxTracingPerfetto(config: PerfettoSdkConfig): String? =
+        enableAndroidxTracingPerfetto(
+            targetPackage = config.targetPackage,
+            provideBinariesIfMissing = config.provideBinariesIfMissing,
+            isColdStartupTracing = when (config.processState) {
+                InitialProcessState.Alive -> false
+                InitialProcessState.NotAlive -> true
+                InitialProcessState.Unknown -> Shell.isPackageAlive(config.targetPackage)
+            }
+        )
+
+    @RequiresApi(30) // TODO(234351579): Support API < 30
+    private fun enableAndroidxTracingPerfetto(
         targetPackage: String,
-        provideBinariesIfMissing: Boolean
+        provideBinariesIfMissing: Boolean,
+        isColdStartupTracing: Boolean
     ): String? {
         if (!isAbiSupported()) {
             throw IllegalStateException("Unsupported ABI (${Build.SUPPORTED_ABIS.joinToString()})")
@@ -113,27 +121,35 @@
                     }
                 }.toMap()
             },
-            executeShellCommand = Shell::executeScriptCaptureStdout
+            executeShellCommand = { cmd ->
+                val (stdout, stderr) = Shell.executeScriptCaptureStdoutStderr(cmd)
+                listOf(stdout, stderr).filter { it.isNotBlank() }.joinToString(
+                    separator = System.lineSeparator()
+                )
+            }
         )
 
-        // negotiate enabling tracing in the app
-        val response = handshake.enableTracingImmediate().let {
-            if (it.resultCode == RESULT_CODE_ERROR_BINARY_MISSING && provideBinariesIfMissing) {
-                val baseApk = File(
-                    InstrumentationRegistry.getInstrumentation()
-                        .context.applicationInfo.publicSourceDir!!
-                )
-                handshake.enableTracingImmediate(
-                    PerfettoSdkHandshake.LibrarySource.aarLibrarySource(
-                        baseApk,
-                        Outputs.dirUsableByAppAndShell
-                    ) { srcFile, dstFile ->
-                        Shell.executeScriptSilent("mkdir -p ${dstFile.parentFile!!.path}")
-                        Shell.executeScriptSilent("mv ${srcFile.path} ${dstFile.path}")
-                    })
-            } // provide binaries and retry
-            else
-                it // no retry
+        // try without supplying external Perfetto SDK tracing binaries
+        val responseFirstPass = if (isColdStartupTracing) {
+            handshake.enableTracingColdStart()
+        } else {
+            handshake.enableTracingImmediate()
+        }
+
+        // if required, retry by supplying external Perfetto SDK tracing binaries
+        val response = if (responseFirstPass.resultCode == RESULT_CODE_ERROR_BINARY_MISSING &&
+            provideBinariesIfMissing
+        ) {
+            val librarySource = constructLibrarySource()
+            if (isColdStartupTracing) {
+                // do not support persistent for now
+                handshake.enableTracingColdStart(persistent = false, librarySource)
+            } else {
+                handshake.enableTracingImmediate(librarySource)
+            }
+        } else {
+            // no retry
+            responseFirstPass
         }
 
         // process the response
@@ -165,4 +181,39 @@
             else -> throw RuntimeException("Unrecognized result code: ${response.resultCode}.")
         }
     }
+
+    private fun constructLibrarySource(): PerfettoSdkHandshake.LibrarySource {
+        val baseApk = File(
+            InstrumentationRegistry.getInstrumentation().context.applicationInfo.publicSourceDir!!
+        )
+
+        val mvTmpFileDstFile = { srcFile: File, dstFile: File ->
+            Shell.executeScriptSilent("mkdir -p ${dstFile.parentFile!!.path}")
+            Shell.executeScriptSilent("mv ${srcFile.path} ${dstFile.path}")
+        }
+
+        return PerfettoSdkHandshake.LibrarySource.apkLibrarySource(
+            baseApk,
+            Outputs.dirUsableByAppAndShell,
+            mvTmpFileDstFile
+        )
+    }
+
+    class PerfettoSdkConfig(
+        val targetPackage: String,
+        val processState: InitialProcessState,
+        val provideBinariesIfMissing: Boolean = true
+    ) {
+        /** State of process before tracing begins. */
+        enum class InitialProcessState {
+            /** will schedule tracing on next cold start */
+            NotAlive,
+
+            /** enable tracing on the target process immediately */
+            Alive,
+
+            /** trigger cold start vs running tracing based on a check if process is alive */
+            Unknown
+        }
+    }
 }
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCaptureWrapper.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCaptureWrapper.kt
index 3f16542..278416e 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCaptureWrapper.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCaptureWrapper.kt
@@ -55,17 +55,14 @@
     @RequiresApi(23)
     private fun start(
         config: PerfettoConfig,
-        userspaceTracingPackage: String?
+        perfettoSdkConfig: PerfettoCapture.PerfettoSdkConfig?
     ): Boolean {
         capture?.apply {
             Log.d(LOG_TAG, "Recording perfetto trace")
-            if (userspaceTracingPackage != null &&
+            if (perfettoSdkConfig != null &&
                 Build.VERSION.SDK_INT >= 30
             ) {
-                val result = enableAndroidxTracingPerfetto(
-                    targetPackage = userspaceTracingPackage,
-                    provideBinariesIfMissing = true
-                ) ?: "Success"
+                val result = enableAndroidxTracingPerfetto(perfettoSdkConfig) ?: "Success"
                 Log.d(LOG_TAG, "Enable full tracing result=$result")
             }
             start(config)
@@ -93,7 +90,7 @@
     fun record(
         fileLabel: String,
         config: PerfettoConfig,
-        userspaceTracingPackage: String?,
+        perfettoSdkConfig: PerfettoCapture.PerfettoSdkConfig?,
         traceCallback: ((String) -> Unit)? = null,
         enableTracing: Boolean = true,
         block: () -> Unit
@@ -124,7 +121,7 @@
         val path: String
         try {
             propOverride?.forceValue()
-            start(config, userspaceTracingPackage)
+            start(config, perfettoSdkConfig)
             try {
                 block()
             } finally {
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.kt
index dd2f36e..2491c74 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.kt
@@ -23,6 +23,7 @@
 import androidx.annotation.RestrictTo
 import androidx.benchmark.DeviceInfo.deviceSummaryString
 import androidx.benchmark.Shell
+import androidx.benchmark.perfetto.PerfettoHelper.Companion.MIN_SDK_VERSION
 import androidx.benchmark.userspaceTrace
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.tracing.trace
@@ -36,14 +37,14 @@
  *
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-@RequiresApi(23)
+@RequiresApi(MIN_SDK_VERSION)
 public class PerfettoHelper(
-    private val unbundled: Boolean = Build.VERSION.SDK_INT < LOWEST_BUNDLED_VERSION_SUPPORTED
+    private val unbundled: Boolean = Build.VERSION.SDK_INT < MIN_BUNDLED_SDK_VERSION
 ) {
     init {
-        require(unbundled || Build.VERSION.SDK_INT >= LOWEST_BUNDLED_VERSION_SUPPORTED) {
+        require(unbundled || Build.VERSION.SDK_INT >= MIN_BUNDLED_SDK_VERSION) {
             "Perfetto capture using the os version of perfetto requires API " +
-                "$LOWEST_BUNDLED_VERSION_SUPPORTED or greater."
+                "$MIN_BUNDLED_SDK_VERSION or greater."
         }
     }
 
@@ -366,7 +367,8 @@
     companion object {
         internal const val LOG_TAG = "PerfettoCapture"
 
-        const val LOWEST_BUNDLED_VERSION_SUPPORTED = 29
+        const val MIN_SDK_VERSION = 23
+        const val MIN_BUNDLED_SDK_VERSION = 29
 
         // Command to start the perfetto tracing in the background.
         // perfetto --background -c /data/misc/perfetto-traces/trace_config.pb -o
@@ -451,7 +453,7 @@
             // this there as well. Can't use bundled /system/bin/traced_probes, as that requires
             // root, and unbundled tracebox otherwise not used/installed on higher APIs, outside
             // of tests.
-            if (Build.VERSION.SDK_INT < LOWEST_BUNDLED_VERSION_SUPPORTED) {
+            if (Build.VERSION.SDK_INT < MIN_BUNDLED_SDK_VERSION) {
                 val output = Shell.executeScriptCaptureStdoutStderr(
                     "$unbundledPerfettoShellPath traced_probes --cleanup-after-crash"
                 )
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoTrace.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoTrace.kt
index 3b98d0f..e2f2e0a 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoTrace.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoTrace.kt
@@ -17,10 +17,11 @@
 package androidx.benchmark.perfetto
 
 import androidx.annotation.RequiresApi
+import androidx.benchmark.perfetto.PerfettoCapture.PerfettoSdkConfig.InitialProcessState
 import androidx.test.platform.app.InstrumentationRegistry
 import java.io.File
 
-@RequiresApi(23)
+@RequiresApi(23) // should match PerfettoHelper.MIN_SDK_VERSION
 @ExperimentalPerfettoCaptureApi
 class PerfettoTrace(
     /**
@@ -157,7 +158,12 @@
             PerfettoCaptureWrapper().record(
                 fileLabel = fileLabel,
                 config,
-                userspaceTracingPackage,
+                perfettoSdkConfig = userspaceTracingPackage?.let {
+                    PerfettoCapture.PerfettoSdkConfig(
+                        it,
+                        InitialProcessState.Unknown
+                    )
+                },
                 traceCallback = { path ->
                     File(path).appendUiState(
                         UiState(
diff --git a/benchmark/benchmark-junit4/api/1.2.0-beta01.txt b/benchmark/benchmark-junit4/api/1.2.0-beta01.txt
new file mode 100644
index 0000000..aea3355
--- /dev/null
+++ b/benchmark/benchmark-junit4/api/1.2.0-beta01.txt
@@ -0,0 +1,35 @@
+// Signature format: 4.0
+package androidx.benchmark.junit4 {
+
+  public class AndroidBenchmarkRunner extends androidx.test.runner.AndroidJUnitRunner {
+    ctor public AndroidBenchmarkRunner();
+  }
+
+  public final class BenchmarkRule implements org.junit.rules.TestRule {
+    ctor public BenchmarkRule();
+    ctor @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public BenchmarkRule(androidx.benchmark.MicrobenchmarkConfig config);
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public androidx.benchmark.BenchmarkState getState();
+  }
+
+  public final class BenchmarkRule.Scope {
+    method public inline <T> T runWithTimingDisabled(kotlin.jvm.functions.Function0<? extends T> block);
+  }
+
+  public final class BenchmarkRuleKt {
+    method public static inline void measureRepeated(androidx.benchmark.junit4.BenchmarkRule, kotlin.jvm.functions.Function1<? super androidx.benchmark.junit4.BenchmarkRule.Scope,kotlin.Unit> block);
+  }
+
+  @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public final class PerfettoTraceRule implements org.junit.rules.TestRule {
+    ctor public PerfettoTraceRule(optional boolean enableAppTagTracing, optional boolean enableUserspaceTracing, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? traceCallback);
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public boolean getEnableAppTagTracing();
+    method public boolean getEnableUserspaceTracing();
+    method public kotlin.jvm.functions.Function1<androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? getTraceCallback();
+    property public final boolean enableAppTagTracing;
+    property public final boolean enableUserspaceTracing;
+    property public final kotlin.jvm.functions.Function1<androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? traceCallback;
+  }
+
+}
+
diff --git a/benchmark/benchmark-junit4/api/current.txt b/benchmark/benchmark-junit4/api/current.txt
index 14173a7..aea3355 100644
--- a/benchmark/benchmark-junit4/api/current.txt
+++ b/benchmark/benchmark-junit4/api/current.txt
@@ -7,6 +7,7 @@
 
   public final class BenchmarkRule implements org.junit.rules.TestRule {
     ctor public BenchmarkRule();
+    ctor @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public BenchmarkRule(androidx.benchmark.MicrobenchmarkConfig config);
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
     method public androidx.benchmark.BenchmarkState getState();
   }
diff --git a/benchmark/benchmark-junit4/api/res-1.2.0-beta01.txt b/benchmark/benchmark-junit4/api/res-1.2.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/benchmark/benchmark-junit4/api/res-1.2.0-beta01.txt
diff --git a/benchmark/benchmark-junit4/api/restricted_1.2.0-beta01.txt b/benchmark/benchmark-junit4/api/restricted_1.2.0-beta01.txt
new file mode 100644
index 0000000..0dab2ea
--- /dev/null
+++ b/benchmark/benchmark-junit4/api/restricted_1.2.0-beta01.txt
@@ -0,0 +1,36 @@
+// Signature format: 4.0
+package androidx.benchmark.junit4 {
+
+  public class AndroidBenchmarkRunner extends androidx.test.runner.AndroidJUnitRunner {
+    ctor public AndroidBenchmarkRunner();
+  }
+
+  public final class BenchmarkRule implements org.junit.rules.TestRule {
+    ctor public BenchmarkRule();
+    ctor @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public BenchmarkRule(androidx.benchmark.MicrobenchmarkConfig config);
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public androidx.benchmark.BenchmarkState getState();
+  }
+
+  public final class BenchmarkRule.Scope {
+    method @kotlin.PublishedApi internal androidx.benchmark.BenchmarkState getOuterState();
+    method public inline <T> T runWithTimingDisabled(kotlin.jvm.functions.Function0<? extends T> block);
+  }
+
+  public final class BenchmarkRuleKt {
+    method public static inline void measureRepeated(androidx.benchmark.junit4.BenchmarkRule, kotlin.jvm.functions.Function1<? super androidx.benchmark.junit4.BenchmarkRule.Scope,kotlin.Unit> block);
+  }
+
+  @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi public final class PerfettoTraceRule implements org.junit.rules.TestRule {
+    ctor public PerfettoTraceRule(optional boolean enableAppTagTracing, optional boolean enableUserspaceTracing, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? traceCallback);
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public boolean getEnableAppTagTracing();
+    method public boolean getEnableUserspaceTracing();
+    method public kotlin.jvm.functions.Function1<androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? getTraceCallback();
+    property public final boolean enableAppTagTracing;
+    property public final boolean enableUserspaceTracing;
+    property public final kotlin.jvm.functions.Function1<androidx.benchmark.perfetto.PerfettoTrace,kotlin.Unit>? traceCallback;
+  }
+
+}
+
diff --git a/benchmark/benchmark-junit4/api/restricted_current.ignore b/benchmark/benchmark-junit4/api/restricted_current.ignore
deleted file mode 100644
index b2f8308..0000000
--- a/benchmark/benchmark-junit4/api/restricted_current.ignore
+++ /dev/null
@@ -1,3 +0,0 @@
-// Baseline format: 1.0
-RemovedClass: androidx.benchmark.junit4.PerfettoRule:
-    Removed class androidx.benchmark.junit4.PerfettoRule
diff --git a/benchmark/benchmark-junit4/api/restricted_current.txt b/benchmark/benchmark-junit4/api/restricted_current.txt
index 09ef59c..0dab2ea 100644
--- a/benchmark/benchmark-junit4/api/restricted_current.txt
+++ b/benchmark/benchmark-junit4/api/restricted_current.txt
@@ -7,6 +7,7 @@
 
   public final class BenchmarkRule implements org.junit.rules.TestRule {
     ctor public BenchmarkRule();
+    ctor @SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public BenchmarkRule(androidx.benchmark.MicrobenchmarkConfig config);
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
     method public androidx.benchmark.BenchmarkState getState();
   }
diff --git a/benchmark/benchmark-junit4/build.gradle b/benchmark/benchmark-junit4/build.gradle
index cfb08c2..ca453a3 100644
--- a/benchmark/benchmark-junit4/build.gradle
+++ b/benchmark/benchmark-junit4/build.gradle
@@ -57,7 +57,8 @@
     kotlinOptions {
         // Enable using experimental APIs from within same version group
         freeCompilerArgs += [
-                "-opt-in=androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi"
+                "-opt-in=androidx.benchmark.ExperimentalBenchmarkConfigApi",
+                "-opt-in=androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi",
         ]
     }
 }
diff --git a/benchmark/benchmark-junit4/src/androidTest/java/androidx/benchmark/junit4/ActivityBenchmarkTests.kt b/benchmark/benchmark-junit4/src/androidTest/java/androidx/benchmark/junit4/ActivityBenchmarkTests.kt
index 5a4185b5..d1d337c 100644
--- a/benchmark/benchmark-junit4/src/androidTest/java/androidx/benchmark/junit4/ActivityBenchmarkTests.kt
+++ b/benchmark/benchmark-junit4/src/androidTest/java/androidx/benchmark/junit4/ActivityBenchmarkTests.kt
@@ -41,7 +41,7 @@
 @RunWith(AndroidJUnit4::class)
 class ActivityScenarioTest {
     @get:Rule
-    val benchmarkRule = BenchmarkRule(enableReport = false)
+    val benchmarkRule = BenchmarkRule()
 
     private lateinit var activityScenario: ActivityScenario<Activity>
 
@@ -62,7 +62,7 @@
 @RunWith(AndroidJUnit4::class)
 class ActivityScenarioRuleTest {
     @get:Rule
-    val benchmarkRule = BenchmarkRule(enableReport = false)
+    val benchmarkRule = BenchmarkRule()
 
     @get:Rule
     val activityRule = ActivityScenarioRule(Activity::class.java)
@@ -79,7 +79,7 @@
 @RunWith(AndroidJUnit4::class)
 class ActivityTestRuleTest {
     @get:Rule
-    val benchmarkRule = BenchmarkRule(enableReport = false)
+    val benchmarkRule = BenchmarkRule()
 
     @Suppress("DEPRECATION")
     @get:Rule
diff --git a/benchmark/benchmark-junit4/src/androidTest/java/androidx/benchmark/junit4/BenchmarkRuleAnnotationTest.kt b/benchmark/benchmark-junit4/src/androidTest/java/androidx/benchmark/junit4/BenchmarkRuleAnnotationTest.kt
index 887ffc4..2e8ee5d 100644
--- a/benchmark/benchmark-junit4/src/androidTest/java/androidx/benchmark/junit4/BenchmarkRuleAnnotationTest.kt
+++ b/benchmark/benchmark-junit4/src/androidTest/java/androidx/benchmark/junit4/BenchmarkRuleAnnotationTest.kt
@@ -26,7 +26,7 @@
 class BenchmarkRuleAnnotationTest {
     @Suppress("MemberVisibilityCanBePrivate") // intentionally public
     // NOTE: not annotated, so will throw when state is accessed
-    val unannotatedRule = BenchmarkRule(enableReport = false)
+    val unannotatedRule = BenchmarkRule()
 
     @Test(expected = IllegalStateException::class)
     fun throwsIfNotAnnotated() {
diff --git a/benchmark/benchmark-junit4/src/androidTest/java/androidx/benchmark/junit4/BenchmarkRuleNotUsedTest.kt b/benchmark/benchmark-junit4/src/androidTest/java/androidx/benchmark/junit4/BenchmarkRuleNotUsedTest.kt
index fc57f68..f45ad61 100644
--- a/benchmark/benchmark-junit4/src/androidTest/java/androidx/benchmark/junit4/BenchmarkRuleNotUsedTest.kt
+++ b/benchmark/benchmark-junit4/src/androidTest/java/androidx/benchmark/junit4/BenchmarkRuleNotUsedTest.kt
@@ -26,7 +26,7 @@
 @RunWith(AndroidJUnit4::class)
 public class BenchmarkRuleNotUsedTest {
     @get:Rule
-    public val benchmarkRule: BenchmarkRule = BenchmarkRule(enableReport = true)
+    public val benchmarkRule: BenchmarkRule = BenchmarkRule()
 
     /**
      * Previously this test would fail, because BenchmarkState().report(...) used in the
diff --git a/benchmark/benchmark-junit4/src/androidTest/java/androidx/benchmark/junit4/BenchmarkRuleTest.kt b/benchmark/benchmark-junit4/src/androidTest/java/androidx/benchmark/junit4/BenchmarkRuleTest.kt
index 3c34893..2e3f44e 100644
--- a/benchmark/benchmark-junit4/src/androidTest/java/androidx/benchmark/junit4/BenchmarkRuleTest.kt
+++ b/benchmark/benchmark-junit4/src/androidTest/java/androidx/benchmark/junit4/BenchmarkRuleTest.kt
@@ -28,7 +28,7 @@
 @RunWith(AndroidJUnit4::class)
 public class BenchmarkRuleTest {
     @get:Rule
-    public val benchmarkRule: BenchmarkRule = BenchmarkRule(enableReport = false)
+    public val benchmarkRule: BenchmarkRule = BenchmarkRule()
 
     @Test
     public fun runWithTimingDisabled() {
diff --git a/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/BenchmarkRule.kt b/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/BenchmarkRule.kt
index 024b80e..5fcf6977 100644
--- a/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/BenchmarkRule.kt
+++ b/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/BenchmarkRule.kt
@@ -17,11 +17,16 @@
 package androidx.benchmark.junit4
 
 import android.Manifest
+import android.os.Build
 import android.util.Log
 import androidx.annotation.RestrictTo
 import androidx.benchmark.Arguments
 import androidx.benchmark.BenchmarkState
+import androidx.benchmark.DeviceInfo
+import androidx.benchmark.ExperimentalBenchmarkConfigApi
+import androidx.benchmark.MicrobenchmarkConfig
 import androidx.benchmark.UserspaceTracing
+import androidx.benchmark.perfetto.PerfettoCapture
 import androidx.benchmark.perfetto.PerfettoCaptureWrapper
 import androidx.benchmark.perfetto.PerfettoConfig
 import androidx.benchmark.perfetto.UiState
@@ -80,21 +85,25 @@
  * See the [Benchmark Guide](https://developer.android.com/studio/profile/benchmark)
  * for more information on writing Benchmarks.
  */
-public class BenchmarkRule internal constructor(
+public class BenchmarkRule private constructor(
+    private val config: MicrobenchmarkConfig?,
     /**
-     * Used to disable reporting, for correctness tests that shouldn't report values
-     * (and would trigger warnings if they did, e.g. debuggable=true)
-     * Is always true when called non-internally.
+     * This param is ignored, and just present to disambiguate the internal (nullable) vs external
+     * (non-null) variants of the constructor, since a lint failure occurs if they have the same
+     * signature, even if the external variant uses `this(config as MicrobenchmarkConfig?)`.
+     *
+     * In the future, we should just always pass a "default" config object, which can reference
+     * default values from Arguments, but that's a deeper change.
      */
-    private val enableReport: Boolean,
-    private val packages: List<String> = emptyList() // TODO: revisit if needed
+    @Suppress("UNUSED_PARAMETER") ignored: Boolean = true
 ) : TestRule {
-    public constructor() : this(true)
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    public constructor(packages: List<String>) : this(true, packages)
+    constructor() : this(config = null, ignored = true)
+
+    @ExperimentalBenchmarkConfigApi
+    constructor(config: MicrobenchmarkConfig) : this(config, ignored = true)
 
     internal // synthetic access
-    val internalState = BenchmarkState()
+    var internalState = BenchmarkState(config)
 
     /**
      * Object used for benchmarking in Java.
@@ -213,14 +222,30 @@
             val tracePath = PerfettoCaptureWrapper().record(
                 fileLabel = uniqueName,
                 config = PerfettoConfig.Benchmark(
-                    appTagPackages = packages,
+                    appTagPackages = if (config?.shouldEnableTraceAppTag == true) {
+                        listOf(InstrumentationRegistry.getInstrumentation().context.packageName)
+                    } else {
+                        emptyList()
+                    },
                     useStackSamplingConfig = false
                 ),
-                userspaceTracingPackage = null,
+                // TODO(290918736): add support for Perfetto SDK Tracing in
+                //  Microbenchmark in other cases, outside of MicrobenchmarkConfig
+                perfettoSdkConfig = if (config?.shouldEnablePerfettoSdkTracing == true &&
+                    Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
+                ) {
+                    PerfettoCapture.PerfettoSdkConfig(
+                        InstrumentationRegistry.getInstrumentation().context.packageName,
+                        PerfettoCapture.PerfettoSdkConfig.InitialProcessState.Alive
+                    )
+                } else {
+                    null
+                },
 
-                // optimize throughput in dryRunMode, since trace isn't useful, and extremely
-                // expensive on some emulators. Could alternately use UserspaceTracing if desired
-                enableTracing = !Arguments.dryRunMode
+                // Optimize throughput in dryRunMode, since trace isn't useful, and extremely
+                //   expensive on some emulators. Could alternately use UserspaceTracing if desired
+                // Additionally, skip on misconfigured devices to still enable benchmarking.
+                enableTracing = !Arguments.dryRunMode && !DeviceInfo.misconfiguredForTracing
             ) {
                 UserspaceTracing.commitToTrace() // clear buffer
 
@@ -243,14 +268,12 @@
                 )
             }
 
-            if (enableReport) {
-                internalState.report(
-                    fullClassName = description.className,
-                    simpleClassName = description.testClass.simpleName,
-                    methodName = invokeMethodName,
-                    tracePath = tracePath
-                )
-            }
+            internalState.report(
+                fullClassName = description.className,
+                simpleClassName = description.testClass.simpleName,
+                methodName = invokeMethodName,
+                tracePath = tracePath
+            )
         }
 
     internal companion object {
diff --git a/benchmark/benchmark-macro-junit4/api/1.1.0-beta01.txt b/benchmark/benchmark-macro-junit4/api/1.1.0-beta01.txt
index 4b1ab53..0addb43 100644
--- a/benchmark/benchmark-macro-junit4/api/1.1.0-beta01.txt
+++ b/benchmark/benchmark-macro-junit4/api/1.1.0-beta01.txt
@@ -1,13 +1,20 @@
 // Signature format: 4.0
 package androidx.benchmark.macro.junit4 {
 
+  @SuppressCompatibility @RequiresApi(28) @androidx.benchmark.macro.ExperimentalBaselineProfilesApi public final class BaselineProfileRule implements org.junit.rules.TestRule {
+    ctor public BaselineProfileRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void collectBaselineProfile(String packageName, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+    method public void collectBaselineProfile(String packageName, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+  }
+
   public final class MacrobenchmarkRule implements org.junit.rules.TestRule {
     ctor public MacrobenchmarkRule();
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
   }
 
 }
diff --git a/benchmark/benchmark-macro-junit4/api/1.1.0-beta02.txt b/benchmark/benchmark-macro-junit4/api/1.1.0-beta02.txt
index 4b1ab53..0addb43 100644
--- a/benchmark/benchmark-macro-junit4/api/1.1.0-beta02.txt
+++ b/benchmark/benchmark-macro-junit4/api/1.1.0-beta02.txt
@@ -1,13 +1,20 @@
 // Signature format: 4.0
 package androidx.benchmark.macro.junit4 {
 
+  @SuppressCompatibility @RequiresApi(28) @androidx.benchmark.macro.ExperimentalBaselineProfilesApi public final class BaselineProfileRule implements org.junit.rules.TestRule {
+    ctor public BaselineProfileRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void collectBaselineProfile(String packageName, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+    method public void collectBaselineProfile(String packageName, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+  }
+
   public final class MacrobenchmarkRule implements org.junit.rules.TestRule {
     ctor public MacrobenchmarkRule();
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
   }
 
 }
diff --git a/benchmark/benchmark-macro-junit4/api/1.1.0-beta03.txt b/benchmark/benchmark-macro-junit4/api/1.1.0-beta03.txt
index 4b1ab53..53be289 100644
--- a/benchmark/benchmark-macro-junit4/api/1.1.0-beta03.txt
+++ b/benchmark/benchmark-macro-junit4/api/1.1.0-beta03.txt
@@ -1,13 +1,19 @@
 // Signature format: 4.0
 package androidx.benchmark.macro.junit4 {
 
+  @SuppressCompatibility @RequiresApi(28) @androidx.benchmark.macro.ExperimentalBaselineProfilesApi public final class BaselineProfileRule implements org.junit.rules.TestRule {
+    ctor public BaselineProfileRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void collectBaselineProfile(String packageName, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+  }
+
   public final class MacrobenchmarkRule implements org.junit.rules.TestRule {
     ctor public MacrobenchmarkRule();
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
   }
 
 }
diff --git a/benchmark/benchmark-macro-junit4/api/1.1.0-beta04.txt b/benchmark/benchmark-macro-junit4/api/1.1.0-beta04.txt
index 4b1ab53..53be289 100644
--- a/benchmark/benchmark-macro-junit4/api/1.1.0-beta04.txt
+++ b/benchmark/benchmark-macro-junit4/api/1.1.0-beta04.txt
@@ -1,13 +1,19 @@
 // Signature format: 4.0
 package androidx.benchmark.macro.junit4 {
 
+  @SuppressCompatibility @RequiresApi(28) @androidx.benchmark.macro.ExperimentalBaselineProfilesApi public final class BaselineProfileRule implements org.junit.rules.TestRule {
+    ctor public BaselineProfileRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void collectBaselineProfile(String packageName, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+  }
+
   public final class MacrobenchmarkRule implements org.junit.rules.TestRule {
     ctor public MacrobenchmarkRule();
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
   }
 
 }
diff --git a/benchmark/benchmark-macro-junit4/api/1.1.0-beta05.txt b/benchmark/benchmark-macro-junit4/api/1.1.0-beta05.txt
index 4b1ab53..53be289 100644
--- a/benchmark/benchmark-macro-junit4/api/1.1.0-beta05.txt
+++ b/benchmark/benchmark-macro-junit4/api/1.1.0-beta05.txt
@@ -1,13 +1,19 @@
 // Signature format: 4.0
 package androidx.benchmark.macro.junit4 {
 
+  @SuppressCompatibility @RequiresApi(28) @androidx.benchmark.macro.ExperimentalBaselineProfilesApi public final class BaselineProfileRule implements org.junit.rules.TestRule {
+    ctor public BaselineProfileRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void collectBaselineProfile(String packageName, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+  }
+
   public final class MacrobenchmarkRule implements org.junit.rules.TestRule {
     ctor public MacrobenchmarkRule();
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
   }
 
 }
diff --git a/benchmark/benchmark-macro-junit4/api/1.2.0-beta01.txt b/benchmark/benchmark-macro-junit4/api/1.2.0-beta01.txt
new file mode 100644
index 0000000..d4c3495
--- /dev/null
+++ b/benchmark/benchmark-macro-junit4/api/1.2.0-beta01.txt
@@ -0,0 +1,26 @@
+// Signature format: 4.0
+package androidx.benchmark.macro.junit4 {
+
+  @RequiresApi(28) public final class BaselineProfileRule implements org.junit.rules.TestRule {
+    ctor public BaselineProfileRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void collect(String packageName, optional int maxIterations, optional int stableIterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, optional boolean strictStability, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+    method public void collect(String packageName, optional int maxIterations, optional int stableIterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, optional boolean strictStability, optional kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean> filterPredicate, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+    method public void collect(String packageName, optional int maxIterations, optional int stableIterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+    method public void collect(String packageName, optional int maxIterations, optional int stableIterations, optional String? outputFilePrefix, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+    method public void collect(String packageName, optional int maxIterations, optional int stableIterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+    method public void collect(String packageName, optional int maxIterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+    method public void collect(String packageName, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+  }
+
+  public final class MacrobenchmarkRule implements org.junit.rules.TestRule {
+    ctor public MacrobenchmarkRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+  }
+
+}
+
diff --git a/benchmark/benchmark-macro-junit4/api/res-1.2.0-beta01.txt b/benchmark/benchmark-macro-junit4/api/res-1.2.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/benchmark/benchmark-macro-junit4/api/res-1.2.0-beta01.txt
diff --git a/benchmark/benchmark-macro-junit4/api/restricted_1.1.0-beta01.txt b/benchmark/benchmark-macro-junit4/api/restricted_1.1.0-beta01.txt
index 4b1ab53..0addb43 100644
--- a/benchmark/benchmark-macro-junit4/api/restricted_1.1.0-beta01.txt
+++ b/benchmark/benchmark-macro-junit4/api/restricted_1.1.0-beta01.txt
@@ -1,13 +1,20 @@
 // Signature format: 4.0
 package androidx.benchmark.macro.junit4 {
 
+  @SuppressCompatibility @RequiresApi(28) @androidx.benchmark.macro.ExperimentalBaselineProfilesApi public final class BaselineProfileRule implements org.junit.rules.TestRule {
+    ctor public BaselineProfileRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void collectBaselineProfile(String packageName, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+    method public void collectBaselineProfile(String packageName, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+  }
+
   public final class MacrobenchmarkRule implements org.junit.rules.TestRule {
     ctor public MacrobenchmarkRule();
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
   }
 
 }
diff --git a/benchmark/benchmark-macro-junit4/api/restricted_1.1.0-beta02.txt b/benchmark/benchmark-macro-junit4/api/restricted_1.1.0-beta02.txt
index 4b1ab53..0addb43 100644
--- a/benchmark/benchmark-macro-junit4/api/restricted_1.1.0-beta02.txt
+++ b/benchmark/benchmark-macro-junit4/api/restricted_1.1.0-beta02.txt
@@ -1,13 +1,20 @@
 // Signature format: 4.0
 package androidx.benchmark.macro.junit4 {
 
+  @SuppressCompatibility @RequiresApi(28) @androidx.benchmark.macro.ExperimentalBaselineProfilesApi public final class BaselineProfileRule implements org.junit.rules.TestRule {
+    ctor public BaselineProfileRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void collectBaselineProfile(String packageName, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+    method public void collectBaselineProfile(String packageName, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+  }
+
   public final class MacrobenchmarkRule implements org.junit.rules.TestRule {
     ctor public MacrobenchmarkRule();
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
   }
 
 }
diff --git a/benchmark/benchmark-macro-junit4/api/restricted_1.1.0-beta03.txt b/benchmark/benchmark-macro-junit4/api/restricted_1.1.0-beta03.txt
index 4b1ab53..53be289 100644
--- a/benchmark/benchmark-macro-junit4/api/restricted_1.1.0-beta03.txt
+++ b/benchmark/benchmark-macro-junit4/api/restricted_1.1.0-beta03.txt
@@ -1,13 +1,19 @@
 // Signature format: 4.0
 package androidx.benchmark.macro.junit4 {
 
+  @SuppressCompatibility @RequiresApi(28) @androidx.benchmark.macro.ExperimentalBaselineProfilesApi public final class BaselineProfileRule implements org.junit.rules.TestRule {
+    ctor public BaselineProfileRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void collectBaselineProfile(String packageName, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+  }
+
   public final class MacrobenchmarkRule implements org.junit.rules.TestRule {
     ctor public MacrobenchmarkRule();
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
   }
 
 }
diff --git a/benchmark/benchmark-macro-junit4/api/restricted_1.1.0-beta04.txt b/benchmark/benchmark-macro-junit4/api/restricted_1.1.0-beta04.txt
index 4b1ab53..53be289 100644
--- a/benchmark/benchmark-macro-junit4/api/restricted_1.1.0-beta04.txt
+++ b/benchmark/benchmark-macro-junit4/api/restricted_1.1.0-beta04.txt
@@ -1,13 +1,19 @@
 // Signature format: 4.0
 package androidx.benchmark.macro.junit4 {
 
+  @SuppressCompatibility @RequiresApi(28) @androidx.benchmark.macro.ExperimentalBaselineProfilesApi public final class BaselineProfileRule implements org.junit.rules.TestRule {
+    ctor public BaselineProfileRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void collectBaselineProfile(String packageName, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+  }
+
   public final class MacrobenchmarkRule implements org.junit.rules.TestRule {
     ctor public MacrobenchmarkRule();
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
   }
 
 }
diff --git a/benchmark/benchmark-macro-junit4/api/restricted_1.1.0-beta05.txt b/benchmark/benchmark-macro-junit4/api/restricted_1.1.0-beta05.txt
index 4b1ab53..53be289 100644
--- a/benchmark/benchmark-macro-junit4/api/restricted_1.1.0-beta05.txt
+++ b/benchmark/benchmark-macro-junit4/api/restricted_1.1.0-beta05.txt
@@ -1,13 +1,19 @@
 // Signature format: 4.0
 package androidx.benchmark.macro.junit4 {
 
+  @SuppressCompatibility @RequiresApi(28) @androidx.benchmark.macro.ExperimentalBaselineProfilesApi public final class BaselineProfileRule implements org.junit.rules.TestRule {
+    ctor public BaselineProfileRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void collectBaselineProfile(String packageName, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+  }
+
   public final class MacrobenchmarkRule implements org.junit.rules.TestRule {
     ctor public MacrobenchmarkRule();
     method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
-    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
   }
 
 }
diff --git a/benchmark/benchmark-macro-junit4/api/restricted_1.2.0-beta01.txt b/benchmark/benchmark-macro-junit4/api/restricted_1.2.0-beta01.txt
new file mode 100644
index 0000000..d4c3495
--- /dev/null
+++ b/benchmark/benchmark-macro-junit4/api/restricted_1.2.0-beta01.txt
@@ -0,0 +1,26 @@
+// Signature format: 4.0
+package androidx.benchmark.macro.junit4 {
+
+  @RequiresApi(28) public final class BaselineProfileRule implements org.junit.rules.TestRule {
+    ctor public BaselineProfileRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void collect(String packageName, optional int maxIterations, optional int stableIterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, optional boolean strictStability, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+    method public void collect(String packageName, optional int maxIterations, optional int stableIterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, optional boolean strictStability, optional kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.Boolean> filterPredicate, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+    method public void collect(String packageName, optional int maxIterations, optional int stableIterations, optional String? outputFilePrefix, optional boolean includeInStartupProfile, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+    method public void collect(String packageName, optional int maxIterations, optional int stableIterations, optional String? outputFilePrefix, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+    method public void collect(String packageName, optional int maxIterations, optional int stableIterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+    method public void collect(String packageName, optional int maxIterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+    method public void collect(String packageName, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> profileBlock);
+  }
+
+  public final class MacrobenchmarkRule implements org.junit.rules.TestRule {
+    ctor public MacrobenchmarkRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, optional androidx.benchmark.macro.StartupMode? startupMode, @IntRange(from=1L) int iterations, optional kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> setupBlock, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, optional androidx.benchmark.macro.CompilationMode compilationMode, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+    method public void measureRepeated(String packageName, java.util.List<? extends androidx.benchmark.macro.Metric> metrics, @IntRange(from=1L) int iterations, kotlin.jvm.functions.Function1<? super androidx.benchmark.macro.MacrobenchmarkScope,kotlin.Unit> measureBlock);
+  }
+
+}
+
diff --git a/benchmark/benchmark-macro-junit4/build.gradle b/benchmark/benchmark-macro-junit4/build.gradle
index 5fa8211..75bd5ec 100644
--- a/benchmark/benchmark-macro-junit4/build.gradle
+++ b/benchmark/benchmark-macro-junit4/build.gradle
@@ -36,10 +36,10 @@
     api(libs.kotlinStdlib)
     api("androidx.annotation:annotation:1.1.0")
     api(project(":benchmark:benchmark-macro"))
+    api("androidx.test.uiautomator:uiautomator:2.2.0")
     implementation(project(":benchmark:benchmark-common"))
     implementation("androidx.test:rules:1.5.0")
     implementation("androidx.test:runner:1.5.0")
-    implementation("androidx.test.uiautomator:uiautomator:2.2.0")
 
     androidTestImplementation(project(":internal-testutils-ktx"))
     androidTestImplementation(libs.testExtJunit)
diff --git a/benchmark/benchmark-macro/api/1.2.0-beta01.txt b/benchmark/benchmark-macro/api/1.2.0-beta01.txt
new file mode 100644
index 0000000..6b770f4
--- /dev/null
+++ b/benchmark/benchmark-macro/api/1.2.0-beta01.txt
@@ -0,0 +1,261 @@
+// Signature format: 4.0
+package androidx.benchmark.macro {
+
+  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public final class AudioUnderrunMetric extends androidx.benchmark.macro.Metric {
+    ctor public AudioUnderrunMetric();
+  }
+
+  public enum BaselineProfileMode {
+    method public static androidx.benchmark.macro.BaselineProfileMode valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.benchmark.macro.BaselineProfileMode[] values();
+    enum_constant public static final androidx.benchmark.macro.BaselineProfileMode Disable;
+    enum_constant public static final androidx.benchmark.macro.BaselineProfileMode Require;
+    enum_constant public static final androidx.benchmark.macro.BaselineProfileMode UseIfAvailable;
+  }
+
+  public abstract sealed class CompilationMode {
+    field public static final androidx.benchmark.macro.CompilationMode.Companion Companion;
+    field public static final androidx.benchmark.macro.CompilationMode DEFAULT;
+  }
+
+  public static final class CompilationMode.Companion {
+  }
+
+  public static final class CompilationMode.Full extends androidx.benchmark.macro.CompilationMode {
+    ctor public CompilationMode.Full();
+  }
+
+  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMacrobenchmarkApi public static final class CompilationMode.Ignore extends androidx.benchmark.macro.CompilationMode {
+    ctor public CompilationMode.Ignore();
+  }
+
+  @RequiresApi(24) public static final class CompilationMode.None extends androidx.benchmark.macro.CompilationMode {
+    ctor public CompilationMode.None();
+  }
+
+  @RequiresApi(24) public static final class CompilationMode.Partial extends androidx.benchmark.macro.CompilationMode {
+    ctor public CompilationMode.Partial();
+    ctor public CompilationMode.Partial(optional androidx.benchmark.macro.BaselineProfileMode baselineProfileMode);
+    ctor public CompilationMode.Partial(optional androidx.benchmark.macro.BaselineProfileMode baselineProfileMode, optional @IntRange(from=0L) int warmupIterations);
+    method public androidx.benchmark.macro.BaselineProfileMode getBaselineProfileMode();
+    method public int getWarmupIterations();
+    property public final androidx.benchmark.macro.BaselineProfileMode baselineProfileMode;
+    property public final int warmupIterations;
+  }
+
+  @SuppressCompatibility @kotlin.RequiresOptIn(message="This Macrobenchmark API is experimental.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface ExperimentalMacrobenchmarkApi {
+  }
+
+  @SuppressCompatibility @kotlin.RequiresOptIn(message="This Metric API is experimental.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface ExperimentalMetricApi {
+  }
+
+  public final class FrameTimingMetric extends androidx.benchmark.macro.Metric {
+    ctor public FrameTimingMetric();
+  }
+
+  public final class MacrobenchmarkScope {
+    ctor public MacrobenchmarkScope(String packageName, boolean launchWithClearTask);
+    method public void dropKernelPageCache();
+    method public void dropShaderCache();
+    method public androidx.test.uiautomator.UiDevice getDevice();
+    method public Integer? getIteration();
+    method public String getPackageName();
+    method public void killProcess();
+    method public void killProcess(optional boolean useKillAll);
+    method public void pressHome();
+    method public void pressHome(optional long delayDurationMs);
+    method public void startActivityAndWait();
+    method public void startActivityAndWait(android.content.Intent intent);
+    method public void startActivityAndWait(optional kotlin.jvm.functions.Function1<? super android.content.Intent,kotlin.Unit> block);
+    property public final androidx.test.uiautomator.UiDevice device;
+    property public final Integer? iteration;
+    property public final String packageName;
+  }
+
+  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public final class MemoryCountersMetric extends androidx.benchmark.macro.TraceMetric {
+    ctor public MemoryCountersMetric();
+    method public java.util.List<androidx.benchmark.macro.Metric.Measurement> getResult(androidx.benchmark.macro.Metric.CaptureInfo captureInfo, androidx.benchmark.perfetto.PerfettoTraceProcessor.Session traceSession);
+  }
+
+  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public final class MemoryUsageMetric extends androidx.benchmark.macro.TraceMetric {
+    ctor public MemoryUsageMetric(androidx.benchmark.macro.MemoryUsageMetric.Mode mode, optional java.util.List<? extends androidx.benchmark.macro.MemoryUsageMetric.SubMetric> subMetrics);
+    method public java.util.List<androidx.benchmark.macro.Metric.Measurement> getResult(androidx.benchmark.macro.Metric.CaptureInfo captureInfo, androidx.benchmark.perfetto.PerfettoTraceProcessor.Session traceSession);
+  }
+
+  public enum MemoryUsageMetric.Mode {
+    method public static androidx.benchmark.macro.MemoryUsageMetric.Mode valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.benchmark.macro.MemoryUsageMetric.Mode[] values();
+    enum_constant public static final androidx.benchmark.macro.MemoryUsageMetric.Mode Last;
+    enum_constant public static final androidx.benchmark.macro.MemoryUsageMetric.Mode Max;
+  }
+
+  public enum MemoryUsageMetric.SubMetric {
+    method public static androidx.benchmark.macro.MemoryUsageMetric.SubMetric valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.benchmark.macro.MemoryUsageMetric.SubMetric[] values();
+    enum_constant public static final androidx.benchmark.macro.MemoryUsageMetric.SubMetric Gpu;
+    enum_constant public static final androidx.benchmark.macro.MemoryUsageMetric.SubMetric HeapSize;
+    enum_constant public static final androidx.benchmark.macro.MemoryUsageMetric.SubMetric RssAnon;
+    enum_constant public static final androidx.benchmark.macro.MemoryUsageMetric.SubMetric RssFile;
+    enum_constant public static final androidx.benchmark.macro.MemoryUsageMetric.SubMetric RssShmem;
+  }
+
+  public abstract sealed class Metric {
+  }
+
+  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public static final class Metric.CaptureInfo {
+    ctor public Metric.CaptureInfo(int apiLevel, String targetPackageName, String testPackageName, androidx.benchmark.macro.StartupMode? startupMode);
+    method public int component1();
+    method public String component2();
+    method public String component3();
+    method public androidx.benchmark.macro.StartupMode? component4();
+    method public androidx.benchmark.macro.Metric.CaptureInfo copy(int apiLevel, String targetPackageName, String testPackageName, androidx.benchmark.macro.StartupMode? startupMode);
+    method public int getApiLevel();
+    method public androidx.benchmark.macro.StartupMode? getStartupMode();
+    method public String getTargetPackageName();
+    method public String getTestPackageName();
+    property public final int apiLevel;
+    property public final androidx.benchmark.macro.StartupMode? startupMode;
+    property public final String targetPackageName;
+    property public final String testPackageName;
+  }
+
+  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public static final class Metric.Measurement {
+    ctor public Metric.Measurement(String name, double data);
+    ctor public Metric.Measurement(String name, java.util.List<java.lang.Double> dataSamples);
+    method public String component1();
+    method public java.util.List<java.lang.Double> component2();
+    method public boolean component3();
+    method public androidx.benchmark.macro.Metric.Measurement copy(String name, java.util.List<java.lang.Double> data, boolean requireSingleValue);
+    method public java.util.List<java.lang.Double> getData();
+    method public String getName();
+    method public boolean getRequireSingleValue();
+    property public final java.util.List<java.lang.Double> data;
+    property public final String name;
+    property public final boolean requireSingleValue;
+  }
+
+  public final class MetricResultExtensionsKt {
+    method @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public static void assertEqualMeasurements(java.util.List<androidx.benchmark.macro.Metric.Measurement> expected, java.util.List<androidx.benchmark.macro.Metric.Measurement> observed, double threshold);
+  }
+
+  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public enum PowerCategory {
+    method public static androidx.benchmark.macro.PowerCategory valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.benchmark.macro.PowerCategory[] values();
+    enum_constant public static final androidx.benchmark.macro.PowerCategory CPU;
+    enum_constant public static final androidx.benchmark.macro.PowerCategory DISPLAY;
+    enum_constant public static final androidx.benchmark.macro.PowerCategory GPS;
+    enum_constant public static final androidx.benchmark.macro.PowerCategory GPU;
+    enum_constant public static final androidx.benchmark.macro.PowerCategory MACHINE_LEARNING;
+    enum_constant public static final androidx.benchmark.macro.PowerCategory MEMORY;
+    enum_constant public static final androidx.benchmark.macro.PowerCategory NETWORK;
+    enum_constant public static final androidx.benchmark.macro.PowerCategory UNCATEGORIZED;
+  }
+
+  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public enum PowerCategoryDisplayLevel {
+    method public static androidx.benchmark.macro.PowerCategoryDisplayLevel valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.benchmark.macro.PowerCategoryDisplayLevel[] values();
+    enum_constant public static final androidx.benchmark.macro.PowerCategoryDisplayLevel BREAKDOWN;
+    enum_constant public static final androidx.benchmark.macro.PowerCategoryDisplayLevel TOTAL;
+  }
+
+  @SuppressCompatibility @RequiresApi(29) @androidx.benchmark.macro.ExperimentalMetricApi public final class PowerMetric extends androidx.benchmark.macro.Metric {
+    ctor public PowerMetric(androidx.benchmark.macro.PowerMetric.Type type);
+    method public static androidx.benchmark.macro.PowerMetric.Type.Battery Battery();
+    method public static androidx.benchmark.macro.PowerMetric.Type.Energy Energy(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> categories);
+    method public static androidx.benchmark.macro.PowerMetric.Type.Power Power(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> categories);
+    field public static final androidx.benchmark.macro.PowerMetric.Companion Companion;
+  }
+
+  public static final class PowerMetric.Companion {
+    method public androidx.benchmark.macro.PowerMetric.Type.Battery Battery();
+    method public androidx.benchmark.macro.PowerMetric.Type.Energy Energy(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> categories);
+    method public androidx.benchmark.macro.PowerMetric.Type.Power Power(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> categories);
+  }
+
+  public abstract static sealed class PowerMetric.Type {
+    method public final java.util.Map<androidx.benchmark.macro.PowerCategory,androidx.benchmark.macro.PowerCategoryDisplayLevel> getCategories();
+    method public final void setCategories(java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel>);
+    property public final java.util.Map<androidx.benchmark.macro.PowerCategory,androidx.benchmark.macro.PowerCategoryDisplayLevel> categories;
+  }
+
+  public static final class PowerMetric.Type.Battery extends androidx.benchmark.macro.PowerMetric.Type {
+    ctor public PowerMetric.Type.Battery();
+  }
+
+  public static final class PowerMetric.Type.Energy extends androidx.benchmark.macro.PowerMetric.Type {
+    ctor public PowerMetric.Type.Energy(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> energyCategories);
+  }
+
+  public static final class PowerMetric.Type.Power extends androidx.benchmark.macro.PowerMetric.Type {
+    ctor public PowerMetric.Type.Power(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> powerCategories);
+  }
+
+  public enum StartupMode {
+    method public static androidx.benchmark.macro.StartupMode valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.benchmark.macro.StartupMode[] values();
+    enum_constant public static final androidx.benchmark.macro.StartupMode COLD;
+    enum_constant public static final androidx.benchmark.macro.StartupMode HOT;
+    enum_constant public static final androidx.benchmark.macro.StartupMode WARM;
+  }
+
+  public final class StartupTimingMetric extends androidx.benchmark.macro.Metric {
+    ctor public StartupTimingMetric();
+  }
+
+  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public abstract class TraceMetric extends androidx.benchmark.macro.Metric {
+    ctor public TraceMetric();
+    method public abstract java.util.List<androidx.benchmark.macro.Metric.Measurement> getResult(androidx.benchmark.macro.Metric.CaptureInfo captureInfo, androidx.benchmark.perfetto.PerfettoTraceProcessor.Session traceSession);
+  }
+
+  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public final class TraceSectionMetric extends androidx.benchmark.macro.Metric {
+    ctor public TraceSectionMetric(String sectionName, optional androidx.benchmark.macro.TraceSectionMetric.Mode mode);
+  }
+
+  public enum TraceSectionMetric.Mode {
+    method public static androidx.benchmark.macro.TraceSectionMetric.Mode valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.benchmark.macro.TraceSectionMetric.Mode[] values();
+    enum_constant public static final androidx.benchmark.macro.TraceSectionMetric.Mode First;
+    enum_constant public static final androidx.benchmark.macro.TraceSectionMetric.Mode Sum;
+  }
+
+}
+
+package androidx.benchmark.perfetto {
+
+  @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface ExperimentalPerfettoTraceProcessorApi {
+  }
+
+  @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoTraceProcessorApi public final class PerfettoTraceProcessor {
+    ctor public PerfettoTraceProcessor();
+    method public <T> T loadTrace(androidx.benchmark.perfetto.PerfettoTrace trace, kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTraceProcessor.Session,? extends T> block);
+    method public static <T> T runServer(kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTraceProcessor,? extends T> block);
+    field public static final androidx.benchmark.perfetto.PerfettoTraceProcessor.Companion Companion;
+  }
+
+  public static final class PerfettoTraceProcessor.Companion {
+    method public <T> T runServer(kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTraceProcessor,? extends T> block);
+  }
+
+  public static final class PerfettoTraceProcessor.Session {
+    method public kotlin.sequences.Sequence<androidx.benchmark.perfetto.Row> query(@org.intellij.lang.annotations.Language("sql") String query);
+    method public byte[] rawQuery(@org.intellij.lang.annotations.Language("sql") String query);
+  }
+
+  @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoTraceProcessorApi public final class Row implements kotlin.jvm.internal.markers.KMappedMarker java.util.Map<java.lang.String,java.lang.Object> {
+    ctor public Row(java.util.Map<java.lang.String,?> map);
+    method public byte[] bytes(String columnName);
+    method public double double(String columnName);
+    method public long long(String columnName);
+    method public byte[]? nullableBytes(String columnName);
+    method public Double? nullableDouble(String columnName);
+    method public Long? nullableLong(String columnName);
+    method public String? nullableString(String columnName);
+    method public String string(String columnName);
+  }
+
+  public final class RowKt {
+    method @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoTraceProcessorApi public static androidx.benchmark.perfetto.Row rowOf(kotlin.Pair<java.lang.String,?>... pairs);
+  }
+
+}
+
diff --git a/benchmark/benchmark-macro/api/res-1.2.0-beta01.txt b/benchmark/benchmark-macro/api/res-1.2.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/benchmark/benchmark-macro/api/res-1.2.0-beta01.txt
diff --git a/benchmark/benchmark-macro/api/restricted_1.2.0-beta01.txt b/benchmark/benchmark-macro/api/restricted_1.2.0-beta01.txt
new file mode 100644
index 0000000..32628f3
--- /dev/null
+++ b/benchmark/benchmark-macro/api/restricted_1.2.0-beta01.txt
@@ -0,0 +1,283 @@
+// Signature format: 4.0
+package androidx.benchmark.macro {
+
+  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public final class AudioUnderrunMetric extends androidx.benchmark.macro.Metric {
+    ctor public AudioUnderrunMetric();
+  }
+
+  public enum BaselineProfileMode {
+    method public static androidx.benchmark.macro.BaselineProfileMode valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.benchmark.macro.BaselineProfileMode[] values();
+    enum_constant public static final androidx.benchmark.macro.BaselineProfileMode Disable;
+    enum_constant public static final androidx.benchmark.macro.BaselineProfileMode Require;
+    enum_constant public static final androidx.benchmark.macro.BaselineProfileMode UseIfAvailable;
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class BatteryCharge {
+    method public boolean hasMinimumCharge(optional boolean throwOnMissingMetrics);
+    field public static final androidx.benchmark.macro.BatteryCharge INSTANCE;
+  }
+
+  public abstract sealed class CompilationMode {
+    field public static final androidx.benchmark.macro.CompilationMode.Companion Companion;
+    field public static final androidx.benchmark.macro.CompilationMode DEFAULT;
+  }
+
+  public static final class CompilationMode.Companion {
+  }
+
+  public static final class CompilationMode.Full extends androidx.benchmark.macro.CompilationMode {
+    ctor public CompilationMode.Full();
+  }
+
+  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMacrobenchmarkApi public static final class CompilationMode.Ignore extends androidx.benchmark.macro.CompilationMode {
+    ctor public CompilationMode.Ignore();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final class CompilationMode.Interpreted extends androidx.benchmark.macro.CompilationMode {
+    field public static final androidx.benchmark.macro.CompilationMode.Interpreted INSTANCE;
+  }
+
+  @RequiresApi(24) public static final class CompilationMode.None extends androidx.benchmark.macro.CompilationMode {
+    ctor public CompilationMode.None();
+  }
+
+  @RequiresApi(24) public static final class CompilationMode.Partial extends androidx.benchmark.macro.CompilationMode {
+    ctor public CompilationMode.Partial();
+    ctor public CompilationMode.Partial(optional androidx.benchmark.macro.BaselineProfileMode baselineProfileMode);
+    ctor public CompilationMode.Partial(optional androidx.benchmark.macro.BaselineProfileMode baselineProfileMode, optional @IntRange(from=0L) int warmupIterations);
+    method public androidx.benchmark.macro.BaselineProfileMode getBaselineProfileMode();
+    method public int getWarmupIterations();
+    property public final androidx.benchmark.macro.BaselineProfileMode baselineProfileMode;
+    property public final int warmupIterations;
+  }
+
+  public final class CompilationModeKt {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static boolean isSupportedWithVmSettings(androidx.benchmark.macro.CompilationMode);
+  }
+
+  @SuppressCompatibility @kotlin.RequiresOptIn(message="This Macrobenchmark API is experimental.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface ExperimentalMacrobenchmarkApi {
+  }
+
+  @SuppressCompatibility @kotlin.RequiresOptIn(message="This Metric API is experimental.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface ExperimentalMetricApi {
+  }
+
+  public final class FrameTimingMetric extends androidx.benchmark.macro.Metric {
+    ctor public FrameTimingMetric();
+  }
+
+  public final class MacrobenchmarkScope {
+    ctor public MacrobenchmarkScope(String packageName, boolean launchWithClearTask);
+    method public void dropKernelPageCache();
+    method public void dropShaderCache();
+    method public androidx.test.uiautomator.UiDevice getDevice();
+    method public Integer? getIteration();
+    method public String getPackageName();
+    method public void killProcess();
+    method public void killProcess(optional boolean useKillAll);
+    method public void pressHome();
+    method public void pressHome(optional long delayDurationMs);
+    method public void startActivityAndWait();
+    method public void startActivityAndWait(android.content.Intent intent);
+    method public void startActivityAndWait(optional kotlin.jvm.functions.Function1<? super android.content.Intent,kotlin.Unit> block);
+    property public final androidx.test.uiautomator.UiDevice device;
+    property public final Integer? iteration;
+    property public final String packageName;
+  }
+
+  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public final class MemoryCountersMetric extends androidx.benchmark.macro.TraceMetric {
+    ctor public MemoryCountersMetric();
+    method public java.util.List<androidx.benchmark.macro.Metric.Measurement> getResult(androidx.benchmark.macro.Metric.CaptureInfo captureInfo, androidx.benchmark.perfetto.PerfettoTraceProcessor.Session traceSession);
+  }
+
+  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public final class MemoryUsageMetric extends androidx.benchmark.macro.TraceMetric {
+    ctor public MemoryUsageMetric(androidx.benchmark.macro.MemoryUsageMetric.Mode mode, optional java.util.List<? extends androidx.benchmark.macro.MemoryUsageMetric.SubMetric> subMetrics);
+    method public java.util.List<androidx.benchmark.macro.Metric.Measurement> getResult(androidx.benchmark.macro.Metric.CaptureInfo captureInfo, androidx.benchmark.perfetto.PerfettoTraceProcessor.Session traceSession);
+  }
+
+  public enum MemoryUsageMetric.Mode {
+    method public static androidx.benchmark.macro.MemoryUsageMetric.Mode valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.benchmark.macro.MemoryUsageMetric.Mode[] values();
+    enum_constant public static final androidx.benchmark.macro.MemoryUsageMetric.Mode Last;
+    enum_constant public static final androidx.benchmark.macro.MemoryUsageMetric.Mode Max;
+  }
+
+  public enum MemoryUsageMetric.SubMetric {
+    method public static androidx.benchmark.macro.MemoryUsageMetric.SubMetric valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.benchmark.macro.MemoryUsageMetric.SubMetric[] values();
+    enum_constant public static final androidx.benchmark.macro.MemoryUsageMetric.SubMetric Gpu;
+    enum_constant public static final androidx.benchmark.macro.MemoryUsageMetric.SubMetric HeapSize;
+    enum_constant public static final androidx.benchmark.macro.MemoryUsageMetric.SubMetric RssAnon;
+    enum_constant public static final androidx.benchmark.macro.MemoryUsageMetric.SubMetric RssFile;
+    enum_constant public static final androidx.benchmark.macro.MemoryUsageMetric.SubMetric RssShmem;
+  }
+
+  public abstract sealed class Metric {
+  }
+
+  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public static final class Metric.CaptureInfo {
+    ctor public Metric.CaptureInfo(int apiLevel, String targetPackageName, String testPackageName, androidx.benchmark.macro.StartupMode? startupMode);
+    method public int component1();
+    method public String component2();
+    method public String component3();
+    method public androidx.benchmark.macro.StartupMode? component4();
+    method public androidx.benchmark.macro.Metric.CaptureInfo copy(int apiLevel, String targetPackageName, String testPackageName, androidx.benchmark.macro.StartupMode? startupMode);
+    method public int getApiLevel();
+    method public androidx.benchmark.macro.StartupMode? getStartupMode();
+    method public String getTargetPackageName();
+    method public String getTestPackageName();
+    property public final int apiLevel;
+    property public final androidx.benchmark.macro.StartupMode? startupMode;
+    property public final String targetPackageName;
+    property public final String testPackageName;
+  }
+
+  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public static final class Metric.Measurement {
+    ctor public Metric.Measurement(String name, double data);
+    ctor public Metric.Measurement(String name, java.util.List<java.lang.Double> dataSamples);
+    method public String component1();
+    method public java.util.List<java.lang.Double> component2();
+    method public boolean component3();
+    method public androidx.benchmark.macro.Metric.Measurement copy(String name, java.util.List<java.lang.Double> data, boolean requireSingleValue);
+    method public java.util.List<java.lang.Double> getData();
+    method public String getName();
+    method public boolean getRequireSingleValue();
+    property public final java.util.List<java.lang.Double> data;
+    property public final String name;
+    property public final boolean requireSingleValue;
+  }
+
+  public final class MetricResultExtensionsKt {
+    method @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public static void assertEqualMeasurements(java.util.List<androidx.benchmark.macro.Metric.Measurement> expected, java.util.List<androidx.benchmark.macro.Metric.Measurement> observed, double threshold);
+  }
+
+  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public enum PowerCategory {
+    method public static androidx.benchmark.macro.PowerCategory valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.benchmark.macro.PowerCategory[] values();
+    enum_constant public static final androidx.benchmark.macro.PowerCategory CPU;
+    enum_constant public static final androidx.benchmark.macro.PowerCategory DISPLAY;
+    enum_constant public static final androidx.benchmark.macro.PowerCategory GPS;
+    enum_constant public static final androidx.benchmark.macro.PowerCategory GPU;
+    enum_constant public static final androidx.benchmark.macro.PowerCategory MACHINE_LEARNING;
+    enum_constant public static final androidx.benchmark.macro.PowerCategory MEMORY;
+    enum_constant public static final androidx.benchmark.macro.PowerCategory NETWORK;
+    enum_constant public static final androidx.benchmark.macro.PowerCategory UNCATEGORIZED;
+  }
+
+  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public enum PowerCategoryDisplayLevel {
+    method public static androidx.benchmark.macro.PowerCategoryDisplayLevel valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.benchmark.macro.PowerCategoryDisplayLevel[] values();
+    enum_constant public static final androidx.benchmark.macro.PowerCategoryDisplayLevel BREAKDOWN;
+    enum_constant public static final androidx.benchmark.macro.PowerCategoryDisplayLevel TOTAL;
+  }
+
+  @SuppressCompatibility @RequiresApi(29) @androidx.benchmark.macro.ExperimentalMetricApi public final class PowerMetric extends androidx.benchmark.macro.Metric {
+    ctor public PowerMetric(androidx.benchmark.macro.PowerMetric.Type type);
+    method public static androidx.benchmark.macro.PowerMetric.Type.Battery Battery();
+    method public static androidx.benchmark.macro.PowerMetric.Type.Energy Energy(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> categories);
+    method public static androidx.benchmark.macro.PowerMetric.Type.Power Power(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> categories);
+    field public static final androidx.benchmark.macro.PowerMetric.Companion Companion;
+  }
+
+  public static final class PowerMetric.Companion {
+    method public androidx.benchmark.macro.PowerMetric.Type.Battery Battery();
+    method public androidx.benchmark.macro.PowerMetric.Type.Energy Energy(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> categories);
+    method public androidx.benchmark.macro.PowerMetric.Type.Power Power(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> categories);
+  }
+
+  public abstract static sealed class PowerMetric.Type {
+    method public final java.util.Map<androidx.benchmark.macro.PowerCategory,androidx.benchmark.macro.PowerCategoryDisplayLevel> getCategories();
+    method public final void setCategories(java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel>);
+    property public final java.util.Map<androidx.benchmark.macro.PowerCategory,androidx.benchmark.macro.PowerCategoryDisplayLevel> categories;
+  }
+
+  public static final class PowerMetric.Type.Battery extends androidx.benchmark.macro.PowerMetric.Type {
+    ctor public PowerMetric.Type.Battery();
+  }
+
+  public static final class PowerMetric.Type.Energy extends androidx.benchmark.macro.PowerMetric.Type {
+    ctor public PowerMetric.Type.Energy(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> energyCategories);
+  }
+
+  public static final class PowerMetric.Type.Power extends androidx.benchmark.macro.PowerMetric.Type {
+    ctor public PowerMetric.Type.Power(optional java.util.Map<androidx.benchmark.macro.PowerCategory,? extends androidx.benchmark.macro.PowerCategoryDisplayLevel> powerCategories);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class PowerRail {
+    method public boolean hasMetrics(optional boolean throwOnMissingMetrics);
+    field public static final androidx.benchmark.macro.PowerRail INSTANCE;
+  }
+
+  public enum StartupMode {
+    method public static androidx.benchmark.macro.StartupMode valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.benchmark.macro.StartupMode[] values();
+    enum_constant public static final androidx.benchmark.macro.StartupMode COLD;
+    enum_constant public static final androidx.benchmark.macro.StartupMode HOT;
+    enum_constant public static final androidx.benchmark.macro.StartupMode WARM;
+  }
+
+  @RequiresApi(29) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class StartupTimingLegacyMetric extends androidx.benchmark.macro.Metric {
+    ctor public StartupTimingLegacyMetric();
+  }
+
+  public final class StartupTimingMetric extends androidx.benchmark.macro.Metric {
+    ctor public StartupTimingMetric();
+  }
+
+  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public abstract class TraceMetric extends androidx.benchmark.macro.Metric {
+    ctor public TraceMetric();
+    method public abstract java.util.List<androidx.benchmark.macro.Metric.Measurement> getResult(androidx.benchmark.macro.Metric.CaptureInfo captureInfo, androidx.benchmark.perfetto.PerfettoTraceProcessor.Session traceSession);
+  }
+
+  @SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public final class TraceSectionMetric extends androidx.benchmark.macro.Metric {
+    ctor public TraceSectionMetric(String sectionName, optional androidx.benchmark.macro.TraceSectionMetric.Mode mode);
+  }
+
+  public enum TraceSectionMetric.Mode {
+    method public static androidx.benchmark.macro.TraceSectionMetric.Mode valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.benchmark.macro.TraceSectionMetric.Mode[] values();
+    enum_constant public static final androidx.benchmark.macro.TraceSectionMetric.Mode First;
+    enum_constant public static final androidx.benchmark.macro.TraceSectionMetric.Mode Sum;
+  }
+
+}
+
+package androidx.benchmark.perfetto {
+
+  @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface ExperimentalPerfettoTraceProcessorApi {
+  }
+
+  @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoTraceProcessorApi public final class PerfettoTraceProcessor {
+    ctor public PerfettoTraceProcessor();
+    method public <T> T loadTrace(androidx.benchmark.perfetto.PerfettoTrace trace, kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTraceProcessor.Session,? extends T> block);
+    method public static <T> T runServer(kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTraceProcessor,? extends T> block);
+    field public static final androidx.benchmark.perfetto.PerfettoTraceProcessor.Companion Companion;
+  }
+
+  public static final class PerfettoTraceProcessor.Companion {
+    method public <T> T runServer(kotlin.jvm.functions.Function1<? super androidx.benchmark.perfetto.PerfettoTraceProcessor,? extends T> block);
+  }
+
+  public static final class PerfettoTraceProcessor.Session {
+    method public kotlin.sequences.Sequence<androidx.benchmark.perfetto.Row> query(@org.intellij.lang.annotations.Language("sql") String query);
+    method public byte[] rawQuery(@org.intellij.lang.annotations.Language("sql") String query);
+  }
+
+  @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoTraceProcessorApi public final class Row implements kotlin.jvm.internal.markers.KMappedMarker java.util.Map<java.lang.String,java.lang.Object> {
+    ctor public Row(java.util.Map<java.lang.String,?> map);
+    method public byte[] bytes(String columnName);
+    method public double double(String columnName);
+    method public long long(String columnName);
+    method public byte[]? nullableBytes(String columnName);
+    method public Double? nullableDouble(String columnName);
+    method public Long? nullableLong(String columnName);
+    method public String? nullableString(String columnName);
+    method public String string(String columnName);
+  }
+
+  public final class RowKt {
+    method @SuppressCompatibility @androidx.benchmark.perfetto.ExperimentalPerfettoTraceProcessorApi public static androidx.benchmark.perfetto.Row rowOf(kotlin.Pair<java.lang.String,?>... pairs);
+  }
+
+}
+
diff --git a/benchmark/benchmark-macro/build.gradle b/benchmark/benchmark-macro/build.gradle
index 6c554c0..5a04a11 100644
--- a/benchmark/benchmark-macro/build.gradle
+++ b/benchmark/benchmark-macro/build.gradle
@@ -62,7 +62,7 @@
     implementation("androidx.core:core:1.9.0")
     implementation("androidx.profileinstaller:profileinstaller:1.3.0")
     implementation("androidx.tracing:tracing-ktx:1.1.0-rc01")
-    implementation(libs.testCore)
+    implementation("androidx.test:core:1.5.0")
     implementation(libs.testUiautomator)
     implementation(libs.wireRuntime)
 
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupTimingMetricTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupTimingMetricTest.kt
index e4c39f9..719e6a8 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupTimingMetricTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupTimingMetricTest.kt
@@ -22,6 +22,8 @@
 import androidx.annotation.RequiresApi
 import androidx.benchmark.DeviceInfo
 import androidx.benchmark.Outputs
+import androidx.benchmark.perfetto.PerfettoCapture.PerfettoSdkConfig
+import androidx.benchmark.perfetto.PerfettoCapture.PerfettoSdkConfig.InitialProcessState
 import androidx.benchmark.perfetto.PerfettoCaptureWrapper
 import androidx.benchmark.perfetto.PerfettoConfig
 import androidx.benchmark.perfetto.PerfettoHelper.Companion.isAbiSupported
@@ -310,7 +312,7 @@
             },
             useStackSamplingConfig = false
         ),
-        userspaceTracingPackage = packageName,
+        perfettoSdkConfig = PerfettoSdkConfig(packageName, InitialProcessState.Unknown),
         block = measureBlock
     )!!
 
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureSweepTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureSweepTest.kt
index 1f95b755..117726f 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureSweepTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureSweepTest.kt
@@ -23,7 +23,7 @@
 import androidx.benchmark.perfetto.PerfettoCapture
 import androidx.benchmark.perfetto.PerfettoConfig
 import androidx.benchmark.perfetto.PerfettoHelper
-import androidx.benchmark.perfetto.PerfettoHelper.Companion.LOWEST_BUNDLED_VERSION_SUPPORTED
+import androidx.benchmark.perfetto.PerfettoHelper.Companion.MIN_BUNDLED_SDK_VERSION
 import androidx.benchmark.perfetto.PerfettoHelper.Companion.isAbiSupported
 import androidx.benchmark.perfetto.PerfettoTraceProcessor
 import androidx.test.filters.LargeTest
@@ -49,7 +49,7 @@
  * Note: this test is defined in benchmark-macro instead of benchmark-common so that it can
  * validate trace contents with TraceProcessor
  */
-@SdkSuppress(minSdkVersion = 23)
+@SdkSuppress(minSdkVersion = MIN_BUNDLED_SDK_VERSION)
 @LargeTest
 @RunWith(Parameterized::class)
 class PerfettoCaptureSweepTest(
@@ -66,7 +66,7 @@
     }
 
     @Ignore("b/258216025")
-    @SdkSuppress(minSdkVersion = LOWEST_BUNDLED_VERSION_SUPPORTED, maxSdkVersion = 33)
+    @SdkSuppress(minSdkVersion = MIN_BUNDLED_SDK_VERSION, maxSdkVersion = 33)
     @Test
     fun captureAndValidateTrace_bundled() {
         if (Build.VERSION.SDK_INT == 33 && Build.VERSION.CODENAME != "REL") {
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureTest.kt
index e56cb23..c708fe0 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureTest.kt
@@ -17,7 +17,7 @@
 package androidx.benchmark.macro.perfetto
 
 import androidx.benchmark.perfetto.PerfettoCapture
-import androidx.benchmark.perfetto.PerfettoHelper.Companion.LOWEST_BUNDLED_VERSION_SUPPORTED
+import androidx.benchmark.perfetto.PerfettoHelper.Companion.MIN_BUNDLED_SDK_VERSION
 import androidx.benchmark.perfetto.PerfettoHelper.Companion.isAbiSupported
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SdkSuppress
@@ -35,7 +35,7 @@
 class PerfettoCaptureTest {
     @SdkSuppress(
         minSdkVersion = 23,
-        maxSdkVersion = LOWEST_BUNDLED_VERSION_SUPPORTED - 1
+        maxSdkVersion = MIN_BUNDLED_SDK_VERSION - 1
     )
     @SmallTest
     @Test
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoSdkHandshakeTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoSdkHandshakeTest.kt
index d088c78..f8cbebb 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoSdkHandshakeTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoSdkHandshakeTest.kt
@@ -26,6 +26,8 @@
 import androidx.benchmark.macro.perfetto.PerfettoSdkHandshakeTest.SdkDelivery.MISSING
 import androidx.benchmark.macro.perfetto.PerfettoSdkHandshakeTest.SdkDelivery.PROVIDED_BY_BENCHMARK
 import androidx.benchmark.perfetto.PerfettoCapture
+import androidx.benchmark.perfetto.PerfettoCapture.PerfettoSdkConfig
+import androidx.benchmark.perfetto.PerfettoCapture.PerfettoSdkConfig.InitialProcessState
 import androidx.benchmark.perfetto.PerfettoHelper.Companion.isAbiSupported
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.tracing.perfetto.handshake.PerfettoSdkHandshake
@@ -47,7 +49,7 @@
 import org.junit.runners.Parameterized
 import org.junit.runners.Parameterized.Parameters
 
-private const val tracingPerfettoVersion = "1.0.0-alpha17" // TODO(224510255): get by 'reflection'
+private const val tracingPerfettoVersion = "1.0.0-beta01" // TODO(224510255): get by 'reflection'
 private const val minSupportedSdk = Build.VERSION_CODES.R // TODO(234351579): Support API < 30
 
 @RunWith(Parameterized::class)
@@ -104,7 +106,9 @@
 
         // issue an 'enable' broadcast
         perfettoCapture.enableAndroidxTracingPerfetto(
-            targetPackage, shouldProvideBinaries(testConfig.sdkDelivery)
+            targetPackage,
+            shouldProvideBinaries(testConfig.sdkDelivery),
+            isColdStartupTracing = false
         ).let { response: String? ->
             when (testConfig.sdkDelivery) {
                 PROVIDED_BY_BENCHMARK -> assertThat(response).isNull()
@@ -114,7 +118,9 @@
 
         // issue an 'enable' broadcast again
         perfettoCapture.enableAndroidxTracingPerfetto(
-            targetPackage, shouldProvideBinaries(testConfig.sdkDelivery)
+            targetPackage,
+            shouldProvideBinaries(testConfig.sdkDelivery),
+            isColdStartupTracing = false
         ).let { response: String? ->
             when (testConfig.sdkDelivery) {
                 PROVIDED_BY_BENCHMARK -> assertAlreadyEnabledResponse(response)
@@ -136,7 +142,8 @@
         try {
             perfettoCapture.enableAndroidxTracingPerfetto(
                 targetPackage,
-                shouldProvideBinaries(testConfig.sdkDelivery)
+                shouldProvideBinaries(testConfig.sdkDelivery),
+                isColdStartupTracing = false
             )
         } catch (e: IllegalStateException) {
             assertThat(e.message).ignoringCase().contains("Unsupported ABI")
@@ -153,7 +160,8 @@
         val response =
             perfettoCapture.enableAndroidxTracingPerfetto(
                 targetPackage,
-                shouldProvideBinaries(testConfig.sdkDelivery)
+                shouldProvideBinaries(testConfig.sdkDelivery),
+                isColdStartupTracing = false
             )
 
         assertThat(response).ignoringCase().contains("SDK version not supported")
@@ -271,6 +279,55 @@
 
     // TODO(283953019): enable alongside StartupTracingInitializer (pending performance testing)
     @Ignore
+    /**
+     * Tests [androidx.benchmark.perfetto.PerfettoCapture.enableAndroidxTracingPerfetto] as
+     * opposed to [androidx.tracing.perfetto.handshake.PerfettoSdkHandshake.enableTracingColdStart]
+     */
+    @Test
+    fun test_handshake_cold_start() {
+        assumeTrue(isAbiSupported())
+        assumeTrue(Build.VERSION.SDK_INT >= minSupportedSdk)
+        assumeTrue(testConfig.sdkDelivery == PROVIDED_BY_BENCHMARK)
+
+        // perform a handshake setting up cold start tracing
+        killProcess()
+        assertPackageAlive(false)
+
+        perfettoCapture.enableAndroidxTracingPerfetto(
+            targetPackage,
+            shouldProvideBinaries(testConfig.sdkDelivery),
+            isColdStartupTracing = true
+        ).let {
+            assertThat(it).isNull()
+        }
+        assertPackageAlive(false)
+
+        // start the app
+        // verify that tracing was enabled at app startup (once)
+        enablePackage()
+        perfettoCapture.enableAndroidxTracingPerfetto(
+            targetPackage,
+            shouldProvideBinaries(testConfig.sdkDelivery),
+            isColdStartupTracing = false
+        ).let {
+            assertAlreadyEnabledResponse(it)
+        }
+
+        // verify that tracing was enabled at app startup (more than once)
+        killProcess()
+        enablePackage()
+        perfettoCapture.enableAndroidxTracingPerfetto(
+            targetPackage,
+            shouldProvideBinaries(testConfig.sdkDelivery),
+            isColdStartupTracing = false
+        ).let {
+            // in non-persistent mode, cold startup tracing should expire after one run
+            assertThat(it).isNull()
+        }
+    }
+
+    // TODO(283953019): enable alongside StartupTracingInitializer (pending performance testing)
+    @Ignore
     @Test
     fun test_handshake_framework_cold_start_disable_persistent() =
         test_handshake_framework_cold_start_disable(persistent = true)
@@ -326,7 +383,8 @@
 
         val response = perfettoCapture.enableAndroidxTracingPerfetto(
             "package.does.not.exist.89e51176_bc28_41f1_ac73_ca717454b517",
-            shouldProvideBinaries(testConfig.sdkDelivery)
+            shouldProvideBinaries(testConfig.sdkDelivery),
+            isColdStartupTracing = false
         )
 
         assertThat(response).ignoringCase()
@@ -493,4 +551,16 @@
         /** Remain unresolved */
         MISSING
     }
+
+    private fun PerfettoCapture.enableAndroidxTracingPerfetto(
+        targetPackage: String,
+        provideBinariesIfMissing: Boolean,
+        isColdStartupTracing: Boolean
+    ): String? = this.enableAndroidxTracingPerfetto(
+        PerfettoSdkConfig(
+            targetPackage,
+            if (isColdStartupTracing) InitialProcessState.NotAlive else InitialProcessState.Alive,
+            provideBinariesIfMissing
+        )
+    )
 }
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/CompilationMode.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/CompilationMode.kt
index a0dc54a..1b9da10 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/CompilationMode.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/CompilationMode.kt
@@ -99,8 +99,9 @@
                         val output = Shell.executeScriptCaptureStdout(
                             "cmd package compile --reset $packageName"
                         )
+
                         check(output.trim() == "Success" || output.contains("PERFORMED")) {
-                            "Unable to recompile $packageName (out=$output)"
+                            compileResetErrorString(packageName, output, DeviceInfo.isEmulator)
                         }
                     } else {
                         // User builds pre-U. Kick off a full uninstall-reinstall
@@ -434,6 +435,21 @@
                 "Failed to compile (out=$stdout)"
             }
         }
+
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // enable testing
+        fun compileResetErrorString(
+            packageName: String,
+            output: String,
+            isEmulator: Boolean
+        ): String {
+            return "Unable to reset compilation of $packageName (out=$output)." +
+                if (output.contains("could not be compiled") && isEmulator) {
+                    " Try updating your emulator -" +
+                        " see https://issuetracker.google.com/issue?id=251540646"
+                } else {
+                    ""
+                }
+        }
     }
 }
 
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
index 75d5df8..919dc78 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
@@ -33,6 +33,8 @@
 import androidx.benchmark.UserspaceTracing
 import androidx.benchmark.checkAndGetSuppressionState
 import androidx.benchmark.conditionalError
+import androidx.benchmark.perfetto.PerfettoCapture.PerfettoSdkConfig
+import androidx.benchmark.perfetto.PerfettoCapture.PerfettoSdkConfig.InitialProcessState
 import androidx.benchmark.perfetto.PerfettoCaptureWrapper
 import androidx.benchmark.perfetto.PerfettoConfig
 import androidx.benchmark.perfetto.PerfettoTrace
@@ -138,25 +140,41 @@
                 """.trimIndent()
             ),
             conditionalError(
+                hasError = DeviceInfo.misconfiguredForTracing,
+                id = "DEVICE-TRACING-MISCONFIGURED",
+                summary = "This ${DeviceInfo.typeLabel}'s OS is misconfigured for tracing",
+                message = """
+                    This ${DeviceInfo.typeLabel}'s OS image has not correctly mounted the tracing
+                    file system, which prevents macrobenchmarking, and Perfetto/atrace trace capture
+                    in general. You can try a different device, or experiment with an emulator
+                    (though that will not give timing measurements representative of real device
+                    experience).
+                    This error may not be suppressed.
+                """.trimIndent()
+            ),
+            conditionalError(
                 hasError = Arguments.methodTracingEnabled(),
                 id = "METHOD-TRACING-ENABLED",
                 summary = "Method tracing is enabled during a Macrobenchmark",
                 message = """
                     The Macrobenchmark run for $packageName has method tracing enabled.
-                    This causes the VM will run more slowly than usual, so the metrics from the
+                    This causes the VM to run more slowly than usual, so the metrics from the
                     trace files should only be considered in relative terms
                     (e.g. was run #1 faster than run #2). Also, these metrics cannot be compared
                     with benchmark runs that don't have method tracing enabled.
                 """.trimIndent()
-            )
+            ),
         ).sortedBy { it.id }
 
     // These error ids are really warnings. In that, we don't need developers to have to
     // explicitly suppress them using test instrumentation arguments.
     // TODO: Introduce a better way to surface warnings.
-    val warnings = setOf("METHOD-TRACING-ENABLED")
+    val alwaysSuppressed = setOf("METHOD-TRACING-ENABLED")
+    val neverSuppressed = setOf("DEVICE-TRACING-MISCONFIGURED")
 
-    return errors.checkAndGetSuppressionState(Arguments.suppressedErrors + warnings)
+    return errors.checkAndGetSuppressionState(
+        Arguments.suppressedErrors + alwaysSuppressed - neverSuppressed
+    )
 }
 
 /**
@@ -174,7 +192,7 @@
     iterations: Int,
     launchWithClearTask: Boolean,
     startupModeMetricHint: StartupMode?,
-    userspaceTracingPackage: String?,
+    perfettoSdkConfig: PerfettoSdkConfig?,
     setupBlock: MacrobenchmarkScope.() -> Unit,
     measureBlock: MacrobenchmarkScope.() -> Unit
 ) {
@@ -258,7 +276,7 @@
                         },
                         useStackSamplingConfig = true
                     ),
-                    userspaceTracingPackage = userspaceTracingPackage
+                    perfettoSdkConfig = perfettoSdkConfig
                 ) {
                     try {
                         trace("start metrics") {
@@ -389,13 +407,17 @@
     setupBlock: MacrobenchmarkScope.() -> Unit,
     measureBlock: MacrobenchmarkScope.() -> Unit
 ) {
-    val userspaceTracingPackage = if (Arguments.fullTracingEnable &&
-        startupMode != StartupMode.COLD // can't use with COLD, since the broadcast wakes up target
-    ) {
-        packageName
-    } else {
-        null
-    }
+    val perfettoSdkConfig =
+        if (Arguments.fullTracingEnable && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+            PerfettoSdkConfig(
+                packageName,
+                when (startupMode) {
+                    null -> InitialProcessState.Unknown
+                    StartupMode.COLD -> InitialProcessState.NotAlive
+                    StartupMode.HOT, StartupMode.WARM -> InitialProcessState.Alive
+                }
+            )
+        } else null
     macrobenchmark(
         uniqueName = uniqueName,
         className = className,
@@ -405,7 +427,7 @@
         compilationMode = compilationMode,
         iterations = iterations,
         startupModeMetricHint = startupMode,
-        userspaceTracingPackage = userspaceTracingPackage,
+        perfettoSdkConfig = perfettoSdkConfig,
         setupBlock = {
             if (startupMode == StartupMode.COLD) {
                 // Run setup before killing process
diff --git a/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoOverheadBenchmark.kt b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoOverheadBenchmark.kt
index 449e495..57f6360 100644
--- a/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoOverheadBenchmark.kt
+++ b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoOverheadBenchmark.kt
@@ -16,25 +16,25 @@
 
 package androidx.benchmark.benchmark
 
+import androidx.benchmark.ExperimentalBenchmarkConfigApi
+import androidx.benchmark.MicrobenchmarkConfig
 import androidx.benchmark.junit4.BenchmarkRule
 import androidx.benchmark.junit4.measureRepeated
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
-import androidx.test.platform.app.InstrumentationRegistry
 import androidx.tracing.Trace
 import androidx.tracing.trace
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 
+@OptIn(ExperimentalBenchmarkConfigApi::class)
 @LargeTest
 @RunWith(AndroidJUnit4::class)
 class PerfettoOverheadBenchmark {
-    private val targetPackage =
-        InstrumentationRegistry.getInstrumentation().targetContext.packageName
 
     @get:Rule
-    val benchmarkRule = BenchmarkRule(packages = listOf(targetPackage))
+    val benchmarkRule = BenchmarkRule(MicrobenchmarkConfig(shouldEnableTraceAppTag = true))
 
     /**
      * Empty baseline, no tracing. Expect similar results to [TrivialJavaBenchmark.nothing].
@@ -48,7 +48,7 @@
      */
     @Test
     fun runWithTimingDisabled() = benchmarkRule.measureRepeated {
-        runWithTimingDisabled { /* nothing*/ }
+        runWithTimingDisabled { /* nothing */ }
     }
 
     /**
diff --git a/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoSdkOverheadBenchmark.kt b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoSdkOverheadBenchmark.kt
index f0ba393..8388e3d 100644
--- a/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoSdkOverheadBenchmark.kt
+++ b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoSdkOverheadBenchmark.kt
@@ -17,9 +17,13 @@
 package androidx.benchmark.benchmark
 
 import android.os.Build
+import androidx.benchmark.ExperimentalBenchmarkConfigApi
+import androidx.benchmark.MicrobenchmarkConfig
 import androidx.benchmark.junit4.BenchmarkRule
 import androidx.benchmark.junit4.measureRepeated
 import androidx.benchmark.perfetto.PerfettoCapture
+import androidx.benchmark.perfetto.PerfettoCapture.PerfettoSdkConfig
+import androidx.benchmark.perfetto.PerfettoCapture.PerfettoSdkConfig.InitialProcessState
 import androidx.benchmark.perfetto.PerfettoHelper.Companion.isAbiSupported
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
@@ -33,6 +37,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
+@OptIn(ExperimentalBenchmarkConfigApi::class)
 @LargeTest
 @RunWith(AndroidJUnit4::class)
 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R) // TODO(234351579): Support API < 30
@@ -46,7 +51,7 @@
         InstrumentationRegistry.getInstrumentation().targetContext.packageName
 
     @get:Rule
-    val benchmarkRule = BenchmarkRule(packages = listOf(targetPackage))
+    val benchmarkRule = BenchmarkRule(MicrobenchmarkConfig(shouldEnableTraceAppTag = true))
 
     private val testData = Array(50_000) { UUID.randomUUID().toString() }
 
@@ -71,7 +76,9 @@
     /** Measuring overhead of [androidx.tracing.perfetto.PerfettoSdkTrace]. */
     @Test
     fun traceBeginEnd_perfettoSdkTrace() {
-        PerfettoCapture().enableAndroidxTracingPerfetto(targetPackage, true).let { response ->
+        PerfettoCapture().enableAndroidxTracingPerfetto(
+            PerfettoSdkConfig(targetPackage, InitialProcessState.Alive)
+        ).let { response ->
             assertTrue(
                 "Ensuring Perfetto SDK is enabled",
                 response == null || response.contains("already enabled")
diff --git a/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/SampleCustomBenchmark.kt b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/SampleCustomBenchmark.kt
new file mode 100644
index 0000000..8a2d967
--- /dev/null
+++ b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/SampleCustomBenchmark.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.benchmark.benchmark
+
+import androidx.benchmark.ExperimentalBenchmarkConfigApi
+import androidx.benchmark.MetricCapture
+import androidx.benchmark.MicrobenchmarkConfig
+import androidx.benchmark.ProfilerConfig
+import androidx.benchmark.TimeCapture
+import androidx.benchmark.junit4.BenchmarkRule
+import androidx.benchmark.junit4.measureRepeated
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalBenchmarkConfigApi::class)
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class SampleCustomBenchmark {
+    var counter: Long = 0
+
+    @get:Rule
+    val benchmarkRule = BenchmarkRule(
+        // Ideally, this config object would be specified per test, but that requires non-trivial
+        // changes to BenchmarkRule/BenchmarkState, and can be explored later
+        MicrobenchmarkConfig(
+            metrics = listOf(
+                TimeCapture(), // put TimeCapture early to prioritize it (starts last, stops first)
+                object : MetricCapture(
+                    // list of names of submetrics this capture will produce
+                    listOf("customCounter", "surpriseZero")
+                ) {
+                    var valueAtPause: Long = 0
+                    var pausedOffset: Long = 0
+
+                    override fun captureStart(timeNs: Long) {
+                        counter = 0
+                    }
+
+                    override fun captureStop(timeNs: Long, output: LongArray, offset: Int) {
+                        output[offset] = counter - pausedOffset
+
+                        // note that this number that's reported isn't the metric for one iter, but
+                        // for N iters, and is divided on your behalf. This means with a quick
+                        // benchmark like the one below, you might see results like:
+                        // SampleCustomBenchmark.sample
+                        // timeNs            min 25.1,   median 25.1,   max 25.4
+                        // customCounter     min 20.0,   median 20.0,   max 20.0
+                        // surpriseZero      min  0.0,   median  0.0,   max  0.0
+                        // allocationCount   min  0.0,   median  0.0,   max  0.0
+                        output[offset + 1] = 1000
+                    }
+
+                    override fun capturePaused() {
+                        valueAtPause = counter
+                    }
+
+                    override fun captureResumed() {
+                        pausedOffset += counter - valueAtPause
+                    }
+                }
+            ),
+            profiler = ProfilerConfig.MethodTracing()
+        )
+    )
+    @Test
+    fun sample() {
+        benchmarkRule.measureRepeated {
+            repeat(20) {
+                counter++
+            }
+            runWithTimingDisabled {
+                counter++ // this is ignored, so customCounter output is simply 20
+            }
+        }
+    }
+}
diff --git a/benchmark/gradle-plugin/src/main/kotlin/androidx/benchmark/gradle/BenchmarkPlugin.kt b/benchmark/gradle-plugin/src/main/kotlin/androidx/benchmark/gradle/BenchmarkPlugin.kt
index 5f656dc..64086f5 100644
--- a/benchmark/gradle-plugin/src/main/kotlin/androidx/benchmark/gradle/BenchmarkPlugin.kt
+++ b/benchmark/gradle-plugin/src/main/kotlin/androidx/benchmark/gradle/BenchmarkPlugin.kt
@@ -137,13 +137,18 @@
             if (!applied) {
                 applied = true
 
+                // Note, this directory is hard-coded in AGP
+                val outputDir = project.layout.buildDirectory.dir(
+                    "outputs/connected_android_test_additional_output"
+                )
                 if (!project.properties[ADDITIONAL_TEST_OUTPUT_KEY].toString().toBoolean()) {
                     // Only enable pulling benchmark data through this plugin on older versions of
                     // AGP that do not yet enable this flag.
                     project.tasks.register("benchmarkReport", BenchmarkReportTask::class.java)
-                        .configure {
-                            it.adbPath.set(adbPathProvider)
-                            it.dependsOn(project.tasks.named("connectedAndroidTest"))
+                        .configure { reportTask ->
+                            reportTask.benchmarkReportDir.set(outputDir)
+                            reportTask.adbPath.set(adbPathProvider)
+                            reportTask.dependsOn(project.tasks.named("connectedAndroidTest"))
                         }
 
                     project.tasks.named("connectedAndroidTest").configure {
@@ -153,13 +158,12 @@
                         it.finalizedBy("benchmarkReport")
                     }
                 } else {
-                    val projectBuildDir = project.buildDir.path
                     project.tasks.named("connectedAndroidTest").configure {
                         it.doLast {
                             it.logger.info(
                                 "Benchmark",
-                                "Benchmark report files generated at $projectBuildDir" +
-                                    "/outputs/connected_android_test_additional_output"
+                                "Benchmark report files generated at " +
+                                    outputDir.get().asFile.absolutePath
                             )
                         }
                     }
diff --git a/benchmark/gradle-plugin/src/main/kotlin/androidx/benchmark/gradle/BenchmarkReportTask.kt b/benchmark/gradle-plugin/src/main/kotlin/androidx/benchmark/gradle/BenchmarkReportTask.kt
index e84bb7c..482292a 100644
--- a/benchmark/gradle-plugin/src/main/kotlin/androidx/benchmark/gradle/BenchmarkReportTask.kt
+++ b/benchmark/gradle-plugin/src/main/kotlin/androidx/benchmark/gradle/BenchmarkReportTask.kt
@@ -18,8 +18,10 @@
 
 import java.io.File
 import org.gradle.api.DefaultTask
+import org.gradle.api.file.DirectoryProperty
 import org.gradle.api.provider.Property
 import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.OutputDirectory
 import org.gradle.api.tasks.StopExecutionException
 import org.gradle.api.tasks.TaskAction
 import org.gradle.work.DisableCachingByDefault
@@ -27,18 +29,12 @@
 @Suppress("UnstableApiUsage")
 @DisableCachingByDefault(because = "Benchmark measurements are performed each task execution.")
 abstract class BenchmarkReportTask : DefaultTask() {
-    private val benchmarkReportDir: File
 
     init {
         group = "Android"
         description = "Run benchmarks found in the current project and output reports to the " +
             "benchmark_reports folder under the project's build directory."
 
-        benchmarkReportDir = File(
-            "${project.buildDir}/outputs", "connected_android_test_additional_output"
-        )
-        outputs.dir(benchmarkReportDir)
-
         // This task should mirror the upToDate behavior of connectedAndroidTest as we always want
         // this task to run after connectedAndroidTest is run to pull the most recent benchmark
         // report data, even when tests are triggered multiple times in a row without source
@@ -46,7 +42,10 @@
         outputs.upToDateWhen { false }
     }
 
-    @get: Input
+    @get:OutputDirectory
+    abstract val benchmarkReportDir: DirectoryProperty
+
+    @get:Input
     abstract val adbPath: Property<String>
 
     @TaskAction
@@ -57,10 +56,11 @@
     }
 
     private fun getReportsForDevices(adb: Adb) {
-        if (benchmarkReportDir.exists()) {
-            benchmarkReportDir.deleteRecursively()
+        val reportDir = benchmarkReportDir.asFile.get()
+        if (reportDir.exists()) {
+            reportDir.deleteRecursively()
         }
-        benchmarkReportDir.mkdirs()
+        reportDir.mkdirs()
 
         val deviceIds = adb.execSync("devices -l").stdout
             .split("\n")
@@ -75,12 +75,12 @@
                 throw StopExecutionException("Failed to find benchmark report on device: $deviceId")
             }
 
-            val outDir = File(benchmarkReportDir, deviceId)
+            val outDir = File(reportDir, deviceId)
             outDir.mkdirs()
             getReportsForDevice(adb, outDir, dataDir, deviceId)
             logger.info(
                 "Benchmark",
-                "Benchmark report files generated at ${benchmarkReportDir.absolutePath}"
+                "Benchmark report files generated at ${reportDir.absolutePath}"
             )
         }
     }
diff --git a/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/CompilationModeTest.kt b/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/CompilationModeTest.kt
index a1eda85..c093990 100644
--- a/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/CompilationModeTest.kt
+++ b/benchmark/integration-tests/macrobenchmark/src/main/java/androidx/benchmark/integration/macrobenchmark/CompilationModeTest.kt
@@ -25,10 +25,12 @@
 import androidx.benchmark.macro.junit4.MacrobenchmarkRule
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
 import androidx.test.uiautomator.By
 import androidx.test.uiautomator.Until
 import androidx.testutils.getStartupMetrics
 import com.google.common.truth.Truth.assertThat
+import kotlin.test.assertEquals
 import org.junit.Assume.assumeTrue
 import org.junit.Rule
 import org.junit.Test
@@ -134,6 +136,38 @@
         return COMPILATION_PROFILE_UNKNOWN
     }
 
+    @SmallTest
+    @Test
+    fun compileResetErrorString() {
+        assertEquals(
+            expected = "Unable to reset compilation of pkg (out=out).",
+            actual = CompilationMode.compileResetErrorString(
+                packageName = "pkg",
+                output = "out",
+                isEmulator = true
+            )
+        )
+        assertEquals(
+            expected = "Unable to reset compilation of pkg (out=pkg could not be compiled).",
+            actual = CompilationMode.compileResetErrorString(
+                packageName = "pkg",
+                output = "pkg could not be compiled",
+                isEmulator = false
+            )
+        )
+        // verbose message requires emulator + specific "could not be compiled" output from --reset
+        assertEquals(
+            expected = "Unable to reset compilation of pkg (out=pkg could not be compiled)." +
+                " Try updating your emulator - see" +
+                " https://issuetracker.google.com/issue?id=251540646",
+            actual = CompilationMode.compileResetErrorString(
+                packageName = "pkg",
+                output = "pkg could not be compiled",
+                isEmulator = true
+            )
+        )
+    }
+
     companion object {
 
         // Intent
diff --git a/bluetooth/bluetooth/api/current.txt b/bluetooth/bluetooth/api/current.txt
index c549d1d..1c84661 100644
--- a/bluetooth/bluetooth/api/current.txt
+++ b/bluetooth/bluetooth/api/current.txt
@@ -55,8 +55,9 @@
   }
 
   public final class ScanFilter {
-    ctor public ScanFilter(optional androidx.bluetooth.BluetoothAddress? deviceAddress, optional int manufacturerId, optional byte[]? manufacturerData, optional byte[]? manufacturerDataMask, optional java.util.UUID? serviceDataUuid, optional byte[]? serviceData, optional byte[]? serviceDataMask, optional java.util.UUID? serviceUuid, optional java.util.UUID? serviceUuidMask);
+    ctor public ScanFilter(optional androidx.bluetooth.BluetoothAddress? deviceAddress, optional String? deviceName, optional int manufacturerId, optional byte[]? manufacturerData, optional byte[]? manufacturerDataMask, optional java.util.UUID? serviceDataUuid, optional byte[]? serviceData, optional byte[]? serviceDataMask, optional java.util.UUID? serviceUuid, optional java.util.UUID? serviceUuidMask);
     method public androidx.bluetooth.BluetoothAddress? getDeviceAddress();
+    method public String? getDeviceName();
     method public byte[]? getManufacturerData();
     method public byte[]? getManufacturerDataMask();
     method public int getManufacturerId();
@@ -66,6 +67,7 @@
     method public java.util.UUID? getServiceUuid();
     method public java.util.UUID? getServiceUuidMask();
     property public final androidx.bluetooth.BluetoothAddress? deviceAddress;
+    property public final String? deviceName;
     property public final byte[]? manufacturerData;
     property public final byte[]? manufacturerDataMask;
     property public final int manufacturerId;
diff --git a/bluetooth/bluetooth/api/restricted_current.txt b/bluetooth/bluetooth/api/restricted_current.txt
index c549d1d..1c84661 100644
--- a/bluetooth/bluetooth/api/restricted_current.txt
+++ b/bluetooth/bluetooth/api/restricted_current.txt
@@ -55,8 +55,9 @@
   }
 
   public final class ScanFilter {
-    ctor public ScanFilter(optional androidx.bluetooth.BluetoothAddress? deviceAddress, optional int manufacturerId, optional byte[]? manufacturerData, optional byte[]? manufacturerDataMask, optional java.util.UUID? serviceDataUuid, optional byte[]? serviceData, optional byte[]? serviceDataMask, optional java.util.UUID? serviceUuid, optional java.util.UUID? serviceUuidMask);
+    ctor public ScanFilter(optional androidx.bluetooth.BluetoothAddress? deviceAddress, optional String? deviceName, optional int manufacturerId, optional byte[]? manufacturerData, optional byte[]? manufacturerDataMask, optional java.util.UUID? serviceDataUuid, optional byte[]? serviceData, optional byte[]? serviceDataMask, optional java.util.UUID? serviceUuid, optional java.util.UUID? serviceUuidMask);
     method public androidx.bluetooth.BluetoothAddress? getDeviceAddress();
+    method public String? getDeviceName();
     method public byte[]? getManufacturerData();
     method public byte[]? getManufacturerDataMask();
     method public int getManufacturerId();
@@ -66,6 +67,7 @@
     method public java.util.UUID? getServiceUuid();
     method public java.util.UUID? getServiceUuidMask();
     property public final androidx.bluetooth.BluetoothAddress? deviceAddress;
+    property public final String? deviceName;
     property public final byte[]? manufacturerData;
     property public final byte[]? manufacturerDataMask;
     property public final int manufacturerId;
diff --git a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothLe.kt b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothLe.kt
index 0d99838..24b5d0d 100644
--- a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothLe.kt
+++ b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothLe.kt
@@ -141,9 +141,8 @@
         }
 
         val bleScanner = bluetoothAdapter?.bluetoothLeScanner
-        val scanSettings = ScanSettings.Builder().build()
-
         val fwkFilters = filters.map { it.fwkScanFilter }
+        val scanSettings = ScanSettings.Builder().build()
         bleScanner?.startScan(fwkFilters, scanSettings, callback)
 
         awaitClose {
diff --git a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/ScanFilter.kt b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/ScanFilter.kt
index ac11322..0111f0bd 100644
--- a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/ScanFilter.kt
+++ b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/ScanFilter.kt
@@ -27,31 +27,34 @@
  * scan results to only those that are of interest to them.
  */
 class ScanFilter(
-    /** The scan filter for the remote device address. {@code null} if filter is not set. */
+    /** The scan filter for the remote device address. `null` if filter is not set. */
     val deviceAddress: BluetoothAddress? = null,
 
+    /** The scan filter for the remote device name. `null` if filter is not set. */
+    val deviceName: String? = null,
+
     /** The scan filter for manufacturer id. [MANUFACTURER_FILTER_NONE] if filter is not set. */
     val manufacturerId: Int = MANUFACTURER_FILTER_NONE,
 
-    /** The scan filter for manufacturer data. {@code null} if filter is not set. */
+    /** The scan filter for manufacturer data. `null` if filter is not set. */
     val manufacturerData: ByteArray? = null,
 
-    /** The partial filter on manufacturerData. {@code null} if filter is not set. */
+    /** The partial filter on manufacturerData. `null` if filter is not set. */
     val manufacturerDataMask: ByteArray? = null,
 
-    /** The scan filter for service data uuid. {@code null} if filter is not set. */
+    /** The scan filter for service data uuid. `null` if filter is not set. */
     val serviceDataUuid: UUID? = null,
 
-    /** The scan filter for service data. {@code null} if filter is not set. */
+    /** The scan filter for service data. `null` if filter is not set. */
     val serviceData: ByteArray? = null,
 
-    /** The partial filter on service data. {@code null} if filter is not set. */
+    /** The partial filter on service data. `null` if filter is not set. */
     val serviceDataMask: ByteArray? = null,
 
-    /** The scan filter for service uuid. {@code null} if filter is not set. */
+    /** The scan filter for service uuid. `null` if filter is not set. */
     val serviceUuid: UUID? = null,
 
-    /** The partial filter on service uuid. {@code null} if filter is not set. */
+    /** The partial filter on service uuid. `null` if filter is not set. */
     val serviceUuidMask: UUID? = null
 ) {
     companion object {
@@ -60,35 +63,35 @@
 
     init {
         if (manufacturerId < 0 && manufacturerId != MANUFACTURER_FILTER_NONE) {
-            throw IllegalArgumentException("invalid manufacturerId")
+            throw IllegalArgumentException("Invalid manufacturerId")
         }
 
         if (manufacturerDataMask != null) {
             if (manufacturerData == null) {
                 throw IllegalArgumentException(
-                    "manufacturerData is null while manufacturerDataMask is not null")
+                    "ManufacturerData is null while manufacturerDataMask is not null")
             }
 
             if (manufacturerData.size != manufacturerDataMask.size) {
                 throw IllegalArgumentException(
-                    "size mismatch for manufacturerData and manufacturerDataMask")
+                    "Size mismatch for manufacturerData and manufacturerDataMask")
             }
         }
 
         if (serviceDataMask != null) {
             if (serviceData == null) {
                 throw IllegalArgumentException(
-                    "serviceData is null while serviceDataMask is not null")
+                    "ServiceData is null while serviceDataMask is not null")
             }
 
             if (serviceData.size != serviceDataMask.size) {
                 throw IllegalArgumentException(
-                    "size mismatch for service data and service data mask")
+                    "Size mismatch for service data and service data mask")
             }
         }
 
-        if (serviceUuidMask != null && serviceUuid == null) {
-            throw IllegalArgumentException("uuid is null while uuidMask is not null")
+        if (serviceUuid == null && serviceUuidMask != null) {
+            throw IllegalArgumentException("ServiceUuid is null while ServiceUuidMask is not null")
         }
     }
 
@@ -97,8 +100,9 @@
         FwkScanFilter.Builder().run {
             deviceAddress?.let { setDeviceAddress(it.address) }
 
-            if (manufacturerId != MANUFACTURER_FILTER_NONE &&
-                manufacturerData != null) {
+            deviceName?.let { setDeviceName(it) }
+
+            if (manufacturerId != MANUFACTURER_FILTER_NONE && manufacturerData != null) {
                 if (Build.VERSION.SDK_INT >= 33) {
                     setManufacturerData(
                         manufacturerId,
diff --git a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/ScanResult.kt b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/ScanResult.kt
index b882814..f98714b 100644
--- a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/ScanResult.kt
+++ b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/ScanResult.kt
@@ -36,6 +36,7 @@
  *
  */
 class ScanResult internal constructor(private val fwkScanResult: FwkScanResult) {
+
     /** Remote Bluetooth device found. */
     val device: BluetoothDevice
         get() = BluetoothDevice.of(fwkScanResult.device)
@@ -72,7 +73,7 @@
      * Returns the service data associated with the service UUID.
      *
      * @param serviceUuid The service UUID of the service data
-     * @return the service data associated with the specified service UUID, or {@code null}
+     * @return the service data associated with the specified service UUID, or `null`
      * if the service UUID is not found
      */
     fun getServiceData(serviceUuid: UUID): ByteArray? {
diff --git a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerFragment.kt b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerFragment.kt
index ca23455..27ffe00 100644
--- a/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerFragment.kt
+++ b/bluetooth/integration-tests/testapp/src/main/java/androidx/bluetooth/integration/testapp/ui/scanner/ScannerFragment.kt
@@ -16,7 +16,6 @@
 
 package androidx.bluetooth.integration.testapp.ui.scanner
 
-// TODO(ofy) Migrate to androidx.bluetooth.AdvertiseParams
 import android.annotation.SuppressLint
 import android.os.Bundle
 import android.util.Log
diff --git a/buildSrc-tests/build.gradle b/buildSrc-tests/build.gradle
index 5a72509..dcf2b1d 100644
--- a/buildSrc-tests/build.gradle
+++ b/buildSrc-tests/build.gradle
@@ -109,8 +109,3 @@
     t.overrideDirectory = ktlintDir
     t.overrideSubdirectories = subdirs
 })
-
-// Broken in AGP 7.0-alpha15 due to b/180408027
-tasks["lint"].configure { t ->
-    t.enabled = false
-}
diff --git a/buildSrc-tests/src/test/java/androidx/build/SdkResourceGeneratorTest.kt b/buildSrc-tests/src/test/java/androidx/build/SdkResourceGeneratorTest.kt
index fb02b79..c8a9473 100644
--- a/buildSrc-tests/src/test/java/androidx/build/SdkResourceGeneratorTest.kt
+++ b/buildSrc-tests/src/test/java/androidx/build/SdkResourceGeneratorTest.kt
@@ -31,6 +31,7 @@
     fun `All SDK properties are resolved`() {
         androidx.build.dependencies.agpVersion = "1.2.3"
         androidx.build.dependencies.kspVersion = "3.4.5"
+        androidx.build.dependencies.kotlinGradlePluginVersion = "1.7.10"
 
         val project = ProjectBuilder.builder().build()
         project.extensions.create(
diff --git a/buildSrc-tests/src/test/java/androidx/build/buildInfo/CreateLibraryBuildInfoFileTaskTest.kt b/buildSrc-tests/src/test/java/androidx/build/buildInfo/CreateLibraryBuildInfoFileTaskTest.kt
index a6a165e..0f550f0 100644
--- a/buildSrc-tests/src/test/java/androidx/build/buildInfo/CreateLibraryBuildInfoFileTaskTest.kt
+++ b/buildSrc-tests/src/test/java/androidx/build/buildInfo/CreateLibraryBuildInfoFileTaskTest.kt
@@ -19,6 +19,7 @@
 import androidx.build.buildInfo.CreateLibraryBuildInfoFileTask.Companion.asBuildInfoDependencies
 import androidx.build.jetpad.LibraryBuildInfoFile
 import androidx.testutils.gradle.ProjectSetupRule
+import com.google.common.truth.Truth.assertThat
 import com.google.gson.Gson
 import java.io.File
 import net.saff.checkmark.Checkmark.Companion.check
@@ -29,7 +30,6 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.rules.TemporaryFolder
-import com.google.common.truth.Truth.assertThat
 
 class CreateLibraryBuildInfoFileTaskTest {
     @get:Rule
@@ -78,7 +78,7 @@
         assertThat(buildInfo.groupId).isEqualTo("androidx.build_info_test")
         assertThat(buildInfo.artifactId).isEqualTo("test")
         assertThat(buildInfo.version).isEqualTo("0.0.1")
-        assertThat(buildInfo.kotlinVersion).isEqualTo(projectSetup.props.kotlinVersion)
+        assertThat(buildInfo.kotlinVersion).isEqualTo(projectSetup.props.kgpVersion)
         assertThat(buildInfo.groupIdRequiresSameVersion).isFalse()
         assertThat(buildInfo.dependencies).hasSize(1)
         assertThat(buildInfo.dependencies.single().groupId).isEqualTo("androidx.core")
diff --git a/buildSrc-tests/src/test/java/androidx/build/testConfiguration/AndroidTestConfigBuilderTest.kt b/buildSrc-tests/src/test/java/androidx/build/testConfiguration/AndroidTestConfigBuilderTest.kt
index 799ebed..e4d7736 100644
--- a/buildSrc-tests/src/test/java/androidx/build/testConfiguration/AndroidTestConfigBuilderTest.kt
+++ b/buildSrc-tests/src/test/java/androidx/build/testConfiguration/AndroidTestConfigBuilderTest.kt
@@ -39,7 +39,7 @@
     fun init() {
         builder = ConfigBuilder()
         builder.configName("placeHolderAndroidTest.xml")
-            .isBenchmark(false)
+            .isMicrobenchmark(false)
             .applicationId("com.androidx.placeholder.Placeholder")
             .isPostsubmit(true)
             .minSdk("15")
@@ -58,6 +58,15 @@
     }
 
     @Test
+    fun testXmlAgainstGoldenDefaultBenchmark() {
+        builder.isMicrobenchmark(true)
+        MatcherAssert.assertThat(
+            builder.buildXml(),
+            CoreMatchers.`is`(goldenDefaultConfigBenchmark)
+        )
+    }
+
+    @Test
     fun testJsonAgainstGoldenDefault() {
         MatcherAssert.assertThat(
             builder.buildJson(),
@@ -114,7 +123,7 @@
 
     @Test
     fun testJsonAgainstGoldenPresubmitBenchmark() {
-        builder.isBenchmark(true)
+        builder.isMicrobenchmark(true)
             .isPostsubmit(false)
         MatcherAssert.assertThat(
             builder.buildJson(),
@@ -232,7 +241,7 @@
 
     @Test
     fun testValidTestConfigXml_benchmarkTrue() {
-        builder.isBenchmark(true)
+        builder.isMicrobenchmark(true)
         validate(builder.buildXml())
     }
 
@@ -270,7 +279,7 @@
     @Test
     fun testValidTestConfigXml_presubmitBenchmark() {
         builder.isPostsubmit(false)
-            .isBenchmark(true)
+            .isMicrobenchmark(true)
         validate(builder.buildXml())
     }
 
@@ -319,3 +328,40 @@
     </test>
     </configuration>
 """.trimIndent()
+
+private val goldenDefaultConfigBenchmark = """
+    <?xml version="1.0" encoding="utf-8"?>
+    <!-- Copyright (C) 2020 The Android Open Source Project
+    Licensed under the Apache License, Version 2.0 (the "License")
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+    http://www.apache.org/licenses/LICENSE-2.0
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions
+    and limitations under the License.-->
+    <configuration description="Runs tests for the module">
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MinApiLevelModuleController">
+    <option name="min-api-level" value="15" />
+    </object>
+    <option name="test-suite-tag" value="placeholder_tag" />
+    <option name="config-descriptor:metadata" key="applicationId" value="com.androidx.placeholder.Placeholder" />
+    <option name="wifi:disable" value="true" />
+    <option name="instrumentation-arg" key="notAnnotation" value="androidx.test.filters.FlakyTest" />
+    <option name="instrumentation-arg" key="listener" value="androidx.benchmark.junit4.InstrumentationResultsRunListener" />
+    <include name="google/unbundled/common/setup" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+    <option name="cleanup-apks" value="true" />
+    <option name="install-arg" value="-t" />
+    <option name="test-file-name" value="placeholder.apk" />
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+    <option name="run-command" value="cmd package compile -f -m speed com.androidx.placeholder.Placeholder" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+    <option name="runner" value="com.example.Runner"/>
+    <option name="package" value="com.androidx.placeholder.Placeholder" />
+    </test>
+    </configuration>
+""".trimIndent()
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index ee4f326..06583d6 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -38,8 +38,8 @@
                     // Details: KT-46445 and https://github.com/gradle/gradle/issues/17052
                     "-Xsam-conversions=class"
             ]
-            languageVersion = "1.5"
-            apiVersion = "1.5"
+            languageVersion = "1.8"
+            apiVersion = "1.8"
         }
     }
 }
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeImplPlugin.kt
index 282bd32..1d045d0 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeImplPlugin.kt
@@ -23,27 +23,17 @@
 import com.android.build.gradle.LibraryExtension
 import com.android.build.gradle.LibraryPlugin
 import com.android.build.gradle.TestedExtension
-import com.android.build.gradle.internal.lint.AndroidLintAnalysisTask
-import com.android.build.gradle.internal.lint.AndroidLintTask
-import com.android.build.gradle.internal.lint.LintModelWriterTask
-import com.android.build.gradle.internal.lint.VariantInputs
 import java.io.File
 import org.gradle.api.Plugin
 import org.gradle.api.Project
 import org.gradle.api.artifacts.type.ArtifactTypeDefinition
 import org.gradle.api.attributes.Attribute
-import org.gradle.api.file.ConfigurableFileCollection
 import org.gradle.api.plugins.ExtraPropertiesExtension
 import org.gradle.api.tasks.ClasspathNormalizer
 import org.gradle.kotlin.dsl.create
-import org.gradle.kotlin.dsl.findByType
-import org.gradle.kotlin.dsl.withType
-import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
 import org.jetbrains.kotlin.gradle.plugin.KotlinBasePluginWrapper
 import org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper
-import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-import org.jetbrains.kotlin.tooling.core.withClosure
 
 const val composeSourceOption =
     "plugin:androidx.compose.compiler.plugins.kotlin:sourceInformation=true"
@@ -187,8 +177,6 @@
                 it.languageSettings.apply { optIn("kotlin.ExperimentalMultiplatform") }
             }
 
-            configureLintForMultiplatformLibrary(multiplatformExtension)
-
             afterEvaluate {
                 if (multiplatformExtension.targets.findByName("jvm") != null) {
                     tasks.named("jvmTestClasses").also(::addToBuildOnServer)
@@ -276,70 +264,3 @@
         }
     }
 }
-
-/**
- * Adds missing MPP sourcesets (such as commonMain) to the Lint tasks
- *
- * TODO: b/195329463 Lint is not aware of MPP, and MPP doesn't configure Lint. There is no built-in
- *   API to adjust the default Lint task's sources, so we use this hack to manually add sources for
- *   MPP source sets. In the future with the new Kotlin Project Model
- *   (https://youtrack.jetbrains.com/issue/KT-42572) and an AGP / MPP integration plugin this will
- *   no longer be needed.
- */
-private fun Project.configureLintForMultiplatformLibrary(
-    multiplatformExtension: KotlinMultiplatformExtension
-) {
-    afterEvaluate {
-        // This workaround only works for libraries (apps would require changes to a different
-        // task). Given that we currently do not have any MPP app projects, this should never
-        // happen.
-        project.extensions.findByType<LibraryExtension>() ?: return@afterEvaluate
-        val androidMain =
-            multiplatformExtension.sourceSets.findByName("androidMain") ?: return@afterEvaluate
-        // Get all the sourcesets androidMain transitively / directly depends on
-        val dependencies = androidMain.withClosure(KotlinSourceSet::dependsOn)
-
-        /** Helper function to add the missing sourcesets to this [VariantInputs] */
-        fun VariantInputs.addSourceSets() {
-            // Each variant has a source provider for the variant (such as debug) and the 'main'
-            // variant. The actual files that Lint will run on is both of these providers
-            // combined - so we can just add the dependencies to the first we see.
-            val sourceProvider = sourceProviders.get().firstOrNull() ?: return
-            dependencies.forEach { sourceSet ->
-                sourceProvider.javaDirectories.withChangesAllowed {
-                    from(sourceSet.kotlin.sourceDirectories)
-                }
-            }
-        }
-
-        // Lint for libraries is split into two tasks - analysis, and reporting. We need to
-        // add the new sources to both, so all parts of the pipeline are aware.
-        project.tasks.withType<AndroidLintAnalysisTask>().configureEach {
-            it.variantInputs.addSourceSets()
-        }
-
-        project.tasks.withType<AndroidLintTask>().configureEach { it.variantInputs.addSourceSets() }
-
-        // Also configure the model writing task, so that we don't run into mismatches between
-        // analyzed sources in one module and a downstream module
-        project.tasks.withType<LintModelWriterTask>().configureEach {
-            it.variantInputs.addSourceSets()
-        }
-    }
-}
-
-/**
- * Lint uses [ConfigurableFileCollection.disallowChanges] during initialization, which prevents
- * modifying the file collection separately (there is no time to configure it before AGP has
- * initialized and disallowed changes). This uses reflection to temporarily allow changes, and apply
- * [block].
- */
-private fun ConfigurableFileCollection.withChangesAllowed(
-    block: ConfigurableFileCollection.() -> Unit
-) {
-    val disallowChanges = this::class.java.getDeclaredField("disallowChanges")
-    disallowChanges.isAccessible = true
-    disallowChanges.set(this, false)
-    block()
-    disallowChanges.set(this, true)
-}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXExtension.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXExtension.kt
index cdd9add..038ad3c 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXExtension.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXExtension.kt
@@ -410,6 +410,12 @@
     override val kotlinBomVersion: Provider<String>
         get() = kotlinTarget.map { project.getVersionByName(it.catalogVersion) }
 
+    /**
+     * Whether to validate the androidx configuration block using validateProjectParser. This
+     * should always be set to true unless we are temporarily working around a bug.
+     */
+    var runProjectParser: Boolean = true
+
     companion object {
         const val DEFAULT_UNSPECIFIED_VERSION = "unspecified"
     }
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
index 9295ed7..8fdbf41 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
@@ -35,6 +35,7 @@
 import androidx.build.testConfiguration.addAppApkToTestConfigGeneration
 import androidx.build.testConfiguration.configureTestConfigGeneration
 import com.android.build.api.artifact.SingleArtifact
+import com.android.build.api.variant.AndroidComponentsExtension
 import com.android.build.api.variant.ApplicationAndroidComponentsExtension
 import com.android.build.api.variant.HasAndroidTest
 import com.android.build.api.variant.LibraryAndroidComponentsExtension
@@ -124,6 +125,7 @@
             }
         }
 
+        project.configureLint()
         project.configureKtlint()
         project.configureKotlinVersion()
 
@@ -162,6 +164,7 @@
                 project.validatePublishedMultiplatformHasDefault()
             }
         }
+        project.disallowAccidentalAndroidDependenciesInKmpProject(kmpExtension)
     }
 
     private fun Project.registerProjectOrArtifact() {
@@ -400,7 +403,6 @@
             }
             project.configureKmpTests()
             project.configureSourceJarForMultiplatform()
-            project.configureLintForMultiplatform(extension)
 
             // Disable any source JAR task(s) added by KotlinMultiplatformPlugin.
             // https://youtrack.jetbrains.com/issue/KT-55881
@@ -428,13 +430,6 @@
                 it.configureTests()
                 it.artRewritingWorkaround()
             }
-            finalizeDsl {
-                project.configureAndroidProjectForLint(
-                    it.lint,
-                    androidXExtension,
-                    isLibrary = false
-                )
-            }
         }
     }
 
@@ -536,9 +531,6 @@
                 it.configureTests()
                 it.artRewritingWorkaround()
             }
-            finalizeDsl {
-                project.configureAndroidProjectForLint(it.lint, androidXExtension, isLibrary = true)
-            }
         }
 
         project.configurePublicResourcesStub(libraryExtension)
@@ -613,10 +605,6 @@
             }
         }
 
-        // Standard lint, docs, and Metalava configuration for AndroidX projects.
-        if (project.multiplatformExtension == null) {
-            project.configureNonAndroidProjectForLint(extension)
-        }
         val apiTaskConfig =
             if (project.multiplatformExtension != null) {
                 KmpApiTaskConfig
@@ -931,13 +919,6 @@
             return
         }
         project.afterEvaluate {
-            if (project.hasKotlinNativeTarget().get()) {
-                // KMP plugin cannot handle constraints properly for native targets
-                // b/274786186, YT: KT-57531
-                // It is expected to be fixed in Kotlin 1.9 after which, we should remove this check
-                return@afterEvaluate
-            }
-
             // make sure that the project has a group
             val projectGroup = extension.mavenGroup ?: return@afterEvaluate
             // make sure that this group is configured to use a single version
@@ -1105,6 +1086,11 @@
     }
 }
 
+val Project.androidExtension: AndroidComponentsExtension<*, *, *>
+    get() = extensions.findByType<LibraryAndroidComponentsExtension>()
+        ?: extensions.findByType<ApplicationAndroidComponentsExtension>()
+        ?: throw IllegalArgumentException("Failed to find any registered Android extension")
+
 val Project.multiplatformExtension
     get() = extensions.findByType(KotlinMultiplatformExtension::class.java)
 
@@ -1221,9 +1207,38 @@
     }
 }
 
+/**
+ * Verifies we don't accidentially write "implementation" instead of "commonMainImplementation"
+ */
+fun Project.disallowAccidentalAndroidDependenciesInKmpProject(
+    kmpExtension: AndroidXMultiplatformExtension
+) {
+    project.afterEvaluate {
+        if (kmpExtension.supportedPlatforms.isNotEmpty()) {
+            val androidConfiguration = project.configurations.findByName("implementation")
+            if (androidConfiguration != null) {
+               if (
+                   androidConfiguration.dependencies.isNotEmpty() ||
+                   androidConfiguration.dependencyConstraints.isNotEmpty()
+               ) {
+                   throw GradleException(
+                       "The 'implementation' Configuration should not be used in a " +
+                       "multiplatform project: this Configuration is declared by the " +
+                       "Android plugin rather than the kmp plugin. Did you mean " +
+                       "'commonMainImplementation'?")
+                }
+            }
+        }
+    }
+}
+
 /** Verifies that ProjectParser computes the correct values for this project */
 fun Project.validateProjectParser(extension: AndroidXExtension) {
-    project.afterEvaluate {
+    // If configuration fails, we don't want to validate the ProjectParser
+    // (otherwise it could report a confusing, unnecessary error)
+    project.gradle.taskGraph.whenReady {
+        if (!extension.runProjectParser) return@whenReady
+
         val parsed = project.parse()
         check(extension.type == parsed.libraryType) {
             "ProjectParser incorrectly computed libraryType = ${parsed.libraryType} " +
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
index b036cde..a3fd34e 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXMultiplatformExtension.kt
@@ -50,16 +50,14 @@
     private val kotlinExtension: KotlinMultiplatformExtension by kotlinExtensionDelegate
 
     /**
-     * The list of platforms that have been requested in the build configuration.
+     * The list of platforms that have been declared as supported in the build configuration.
      *
-     * The list of enabled platforms in [targetPlatforms] will vary based on the build environment.
-     * For example, a project's build configuration may have requested `mac()` but this is not
-     * available when building on Linux.
+     * This may be a superset of the currently enabled platforms in [targetPlatforms].
      */
-    val requestedPlatforms: MutableSet<PlatformIdentifier> = mutableSetOf()
+    val supportedPlatforms: MutableSet<PlatformIdentifier> = mutableSetOf()
 
     /**
-     * The list of platforms that are enabled.
+     * The list of platforms that are currently enabled.
      *
      * This will vary across build environments. For example, a project's build configuration may
      * have requested `mac()` but this is not available when building on Linux.
@@ -91,14 +89,14 @@
      * identifier for that platform.
      */
     var defaultPlatform: String? = null
-        get() = field ?: requestedPlatforms.singleOrNull()?.id
+        get() = field ?: supportedPlatforms.singleOrNull()?.id
         set(value) {
             if (value != null) {
-                if (requestedPlatforms.none { it.id == value }) {
+                if (supportedPlatforms.none { it.id == value }) {
                     throw GradleException(
                         "Platform $value has not been requested as a target. " +
                             "Available platforms are: " +
-                            requestedPlatforms.joinToString(", ") { it.id }
+                            supportedPlatforms.joinToString(", ") { it.id }
                     )
                 }
                 if (targetPlatforms.none { it == value }) {
@@ -145,7 +143,7 @@
 
     @JvmOverloads
     fun jvm(block: Action<KotlinJvmTarget>? = null): KotlinJvmTarget? {
-        requestedPlatforms.add(PlatformIdentifier.JVM)
+        supportedPlatforms.add(PlatformIdentifier.JVM)
         return if (project.enableJvm()) {
             kotlinExtension.jvm {
                 block?.execute(this)
@@ -163,9 +161,9 @@
 
     @JvmOverloads
     fun android(block: Action<KotlinAndroidTarget>? = null): KotlinAndroidTarget? {
-        requestedPlatforms.add(PlatformIdentifier.ANDROID)
+        supportedPlatforms.add(PlatformIdentifier.ANDROID)
         return if (project.enableJvm()) {
-            kotlinExtension.android { block?.execute(this) }
+            kotlinExtension.androidTarget { block?.execute(this) }
         } else {
             null
         }
@@ -173,7 +171,7 @@
 
     @JvmOverloads
     fun desktop(block: Action<KotlinJvmTarget>? = null): KotlinJvmTarget? {
-        requestedPlatforms.add(PlatformIdentifier.DESKTOP)
+        supportedPlatforms.add(PlatformIdentifier.DESKTOP)
         return if (project.enableDesktop()) {
             kotlinExtension.jvm("desktop") { block?.execute(this) }
         } else {
@@ -189,7 +187,7 @@
 
     @JvmOverloads
     fun macosX64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTargetWithHostTests? {
-        requestedPlatforms.add(PlatformIdentifier.MAC_OSX_64)
+        supportedPlatforms.add(PlatformIdentifier.MAC_OSX_64)
         return if (project.enableMac()) {
             kotlinExtension.macosX64().also { block?.execute(it) }
         } else {
@@ -199,7 +197,7 @@
 
     @JvmOverloads
     fun macosArm64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTargetWithHostTests? {
-        requestedPlatforms.add(PlatformIdentifier.MAC_ARM_64)
+        supportedPlatforms.add(PlatformIdentifier.MAC_ARM_64)
         return if (project.enableMac()) {
             kotlinExtension.macosArm64().also { block?.execute(it) }
         } else {
@@ -209,7 +207,7 @@
 
     @JvmOverloads
     fun iosArm64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTarget? {
-        requestedPlatforms.add(PlatformIdentifier.IOS_ARM_64)
+        supportedPlatforms.add(PlatformIdentifier.IOS_ARM_64)
         return if (project.enableMac()) {
             kotlinExtension.iosArm64().also { block?.execute(it) }
         } else {
@@ -225,7 +223,7 @@
 
     @JvmOverloads
     fun iosX64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTarget? {
-        requestedPlatforms.add(PlatformIdentifier.IOS_X_64)
+        supportedPlatforms.add(PlatformIdentifier.IOS_X_64)
         return if (project.enableMac()) {
             kotlinExtension.iosX64().also { block?.execute(it) }
         } else {
@@ -235,7 +233,7 @@
 
     @JvmOverloads
     fun iosSimulatorArm64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTarget? {
-        requestedPlatforms.add(PlatformIdentifier.IOS_SIMULATOR_ARM_64)
+        supportedPlatforms.add(PlatformIdentifier.IOS_SIMULATOR_ARM_64)
         return if (project.enableMac()) {
             kotlinExtension.iosSimulatorArm64().also { block?.execute(it) }
         } else {
@@ -252,7 +250,7 @@
 
     @JvmOverloads
     fun linuxX64(block: Action<KotlinNativeTarget>? = null): KotlinNativeTargetWithHostTests? {
-        requestedPlatforms.add(PlatformIdentifier.LINUX_64)
+        supportedPlatforms.add(PlatformIdentifier.LINUX_64)
         return if (project.enableLinux()) {
             kotlinExtension.linuxX64().also { block?.execute(it) }
         } else {
@@ -262,7 +260,7 @@
 
     @JvmOverloads
     fun js(block: Action<KotlinJsTargetDsl>? = null): KotlinJsTargetDsl? {
-        requestedPlatforms.add(PlatformIdentifier.JS)
+        supportedPlatforms.add(PlatformIdentifier.JS)
         return if (project.enableJs()) {
             kotlinExtension.js().also { block?.execute(it) }
         } else {
@@ -286,7 +284,7 @@
 
 fun Project.validatePublishedMultiplatformHasDefault() {
     val extension = project.extensions.getByType(AndroidXMultiplatformExtension::class.java)
-    if (extension.defaultPlatform == null && extension.requestedPlatforms.isNotEmpty()) {
+    if (extension.defaultPlatform == null && extension.supportedPlatforms.isNotEmpty()) {
         throw GradleException(
             "Project is published and multiple platforms are requested. You " +
                 "must explicitly specify androidXMultiplatform.defaultPlatform as one of: " +
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt
index 506a116..fc33853 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt
@@ -202,16 +202,10 @@
             task.setOutput(File(project.getDistributionDirectory(), "task_outputs.txt"))
             task.removePrefix(project.getCheckoutRoot().path)
         }
-        tasks
-            .matching { it.name == "commonizeNativeDistribution" }
-            .configureEach {
-                it.notCompatibleWithConfigurationCache(
-                    "https://youtrack.jetbrains.com/issue/KT-54627"
-                )
-            }
     }
 
     private fun Project.setDependencyVersions() {
+        androidx.build.dependencies.kotlinGradlePluginVersion = getVersionByName("kotlin")
         androidx.build.dependencies.kotlinNativeVersion = getVersionByName("kotlinNative")
         androidx.build.dependencies.kspVersion = getVersionByName("ksp")
         androidx.build.dependencies.agpVersion = getVersionByName("androidGradlePlugin")
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/ErrorProneConfiguration.kt b/buildSrc/private/src/main/kotlin/androidx/build/ErrorProneConfiguration.kt
index 8c43945..8a3fbfd 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/ErrorProneConfiguration.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/ErrorProneConfiguration.kt
@@ -310,7 +310,7 @@
                 val compileTask = compileTaskProvider.get()
                 it.classpath = compileTask.classpath
                 it.source = compileTask.source
-                it.destinationDirectory.set(file(buildDir.resolve("errorProne")))
+                it.destinationDirectory.set(layout.buildDirectory.dir("errorProne"))
                 it.options.compilerArgs = compileTask.options.compilerArgs.toMutableList()
                 it.options.annotationProcessorPath = compileTask.options.annotationProcessorPath
                 it.options.bootstrapClasspath = compileTask.options.bootstrapClasspath
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/Ktlint.kt b/buildSrc/private/src/main/kotlin/androidx/build/Ktlint.kt
index c6a4d3a..fffddc1 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/Ktlint.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/Ktlint.kt
@@ -26,6 +26,7 @@
 import org.gradle.api.attributes.Attribute
 import org.gradle.api.file.ConfigurableFileCollection
 import org.gradle.api.file.FileTree
+import org.gradle.api.file.RegularFileProperty
 import org.gradle.api.model.ObjectFactory
 import org.gradle.api.tasks.CacheableTask
 import org.gradle.api.tasks.Classpath
@@ -102,14 +103,13 @@
 private const val IncludedFiles = "**/*.kt"
 
 fun Project.configureKtlint() {
-    val outputDir = "${buildDir.relativeTo(projectDir)}/reports/ktlint/"
     val lintProvider =
         tasks.register("ktlint", KtlintCheckTask::class.java) { task ->
-            task.report = File("${outputDir}ktlint-checkstyle-report.xml")
+            task.report.set(layout.buildDirectory.file("reports/ktlint/report.xml"))
             task.ktlintClasspath.from(getKtlintConfiguration())
         }
     tasks.register("ktlintFormat", KtlintFormatTask::class.java) { task ->
-        task.report = File("${outputDir}ktlint-format-checkstyle-report.xml")
+        task.report.set(layout.buildDirectory.file("reports/ktlint/format-report.xml"))
         task.ktlintClasspath.from(getKtlintConfiguration())
     }
     // afterEvaluate because Gradle's default "check" task doesn't exist yet
@@ -156,7 +156,7 @@
      */
     @get:Internal var overrideSubdirectories: List<String>? = null
 
-    @get:OutputFile lateinit var report: File
+    @get:OutputFile abstract val report: RegularFileProperty
 
     protected fun getArgsList(shouldFormat: Boolean): List<String> {
         val arguments = mutableListOf("--code-style=android_studio")
@@ -165,7 +165,7 @@
         arguments.add("--disabled_rules")
         arguments.add(DisabledRules)
         arguments.add("--reporter=plain")
-        arguments.add("--reporter=checkstyle,output=$report")
+        arguments.add("--reporter=checkstyle,output=${report.get().asFile.absolutePath}")
 
         overrideDirectory?.let {
             val subdirectories = overrideSubdirectories
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/LintConfiguration.kt b/buildSrc/private/src/main/kotlin/androidx/build/LintConfiguration.kt
index 8d1ccc2..2552af0 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/LintConfiguration.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/LintConfiguration.kt
@@ -17,123 +17,278 @@
 package androidx.build
 
 import com.android.build.api.dsl.Lint
+import com.android.build.gradle.AppPlugin
 import com.android.build.gradle.BaseExtension
+import com.android.build.gradle.LibraryPlugin
 import com.android.build.gradle.internal.lint.AndroidLintAnalysisTask
 import com.android.build.gradle.internal.lint.AndroidLintTask
 import com.android.build.gradle.internal.lint.LintModelWriterTask
 import com.android.build.gradle.internal.lint.VariantInputs
 import java.io.File
-import java.util.Locale
 import org.gradle.api.GradleException
 import org.gradle.api.Project
 import org.gradle.api.file.ConfigurableFileCollection
 import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.plugins.JavaPlugin
 import org.gradle.api.plugins.JavaPluginExtension
 import org.gradle.kotlin.dsl.findByType
 import org.gradle.kotlin.dsl.getByType
 import org.gradle.kotlin.dsl.withType
+import org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin
+import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
+import org.jetbrains.kotlin.tooling.core.withClosure
 
-fun Project.configureNonAndroidProjectForLint(extension: AndroidXExtension) {
-    apply(mapOf("plugin" to "com.android.lint"))
-
-    // Create fake variant tasks since that is what is invoked by developers.
-    val lintTask = tasks.named("lint")
-    tasks.register("lintDebug") {
-        it.dependsOn(lintTask)
-        it.enabled = false
-    }
-    tasks.register("lintAnalyzeDebug") { it.enabled = false }
-    tasks.register("lintRelease") {
-        it.dependsOn(lintTask)
-        it.enabled = false
-    }
-    addToBuildOnServer(lintTask)
-
-    val lint = extensions.getByType<Lint>()
-    // Support the lint standalone plugin case which, as yet, lacks AndroidComponents finalizeDsl
-    afterEvaluate { configureLint(lint, extension, true) }
-}
-
-fun Project.configureAndroidProjectForLint(
-    lint: Lint,
-    extension: AndroidXExtension,
-    isLibrary: Boolean
-) {
-    project.afterEvaluate {
-        // makes sure that the lintDebug task will exist, so we can find it by name
-        setUpLintDebugIfNeeded()
-    }
-    tasks.register("lintAnalyze") { it.enabled = false }
-    configureLint(lint, extension, isLibrary)
-    tasks.named("lint").configure { task ->
-        // We already run lintDebug, we don't need to run lint which lints the release variant
-        task.enabled = false
-    }
-}
-
-private fun Project.setUpLintDebugIfNeeded() {
-    val variants = project.agpVariants
-    val variantNames = variants.map { v -> v.name }
-    if (!variantNames.contains("debug")) {
-        tasks.register("lintDebug") {
-            for (variantName in variantNames) {
-                if (variantName.lowercase(Locale.US).contains("debug")) {
-                    it.dependsOn(
-                        tasks.named(
-                            "lint${variantName.replaceFirstChar {
-                                if (it.isLowerCase()) it.titlecase(Locale.US) else it.toString()
-                            }}"
-                        )
-                    )
-                }
+/**
+ * Single entry point to Android Lint configuration.
+ */
+fun Project.configureLint() {
+    project.plugins.all { plugin ->
+        when (plugin) {
+            is AppPlugin -> configureAndroidProjectForLint(isLibrary = false)
+            is LibraryPlugin -> configureAndroidProjectForLint(isLibrary = true)
+            // Only configure non-multiplatform Java projects via JavaPlugin. Multiplatform
+            // projects targeting Java (e.g. `jvm { withJava() }`) are configured via
+            // KotlinBasePlugin.
+            is JavaPlugin -> if (project.multiplatformExtension == null) {
+                configureNonAndroidProjectForLint()
+            }
+            // Only configure non-Android multiplatform projects via KotlinBasePlugin.
+            // Multiplatform projects targeting Android (e.g. `id("com.android.library")`) are
+            // configured via AppPlugin or LibraryPlugin.
+            is KotlinBasePlugin -> if (
+                project.multiplatformExtension != null &&
+                !project.plugins.hasPlugin(AppPlugin::class.java) &&
+                !project.plugins.hasPlugin(LibraryPlugin::class.java)
+            ) {
+                configureNonAndroidProjectForLint()
             }
         }
     }
 }
 
 /**
- * Installs AIDL source directories on lint tasks. Adapted from AndroidXComposeImplPlugin's
- * `configureLintForMultiplatformLibrary` extension function. See b/189250111 for feature request.
+ * Android Lint configuration entry point for Android projects.
+ */
+private fun Project.configureAndroidProjectForLint(
+    isLibrary: Boolean
+) = androidExtension.finalizeDsl { extension ->
+    // The lintAnalyze task is used by `androidx-studio-integration-lint.sh`.
+    tasks.register("lintAnalyze") { task -> task.enabled = false }
+
+    configureLint(extension.lint, isLibrary)
+
+    // We already run lintDebug, we don't need to run lint on the release variant.
+    tasks.named("lint").configure { task -> task.enabled = false }
+
+    afterEvaluate {
+        registerLintDebugIfNeededAfterEvaluate()
+
+        if (extension.buildFeatures.aidl == true) {
+            configureLintForAidlAfterEvaluate()
+        }
+    }
+}
+
+/**
+ * Android Lint configuration entry point for non-Android projects.
+ */
+private fun Project.configureNonAndroidProjectForLint() = afterEvaluate {
+    // The lint plugin expects certain configurations and source sets which are only added by
+    // the Java and Android plugins. If this is a multiplatform project targeting JVM, we'll
+    // need to manually create these configurations and source sets based on their multiplatform
+    // JVM equivalents.
+    addSourceSetsForMultiplatformAfterEvaluate()
+
+    // For Android projects, the Android Gradle Plugin is responsible for applying the lint plugin;
+    // however, we need to apply it ourselves for non-Android projects.
+    apply(mapOf("plugin" to "com.android.lint"))
+
+    // Create task aliases matching those creates by AGP for Android projects, since those are what
+    // developers expect to invoke. Redirect them to the "real" lint task.
+    val lintTask = tasks.named("lint")
+    tasks.register("lintDebug") {
+        it.dependsOn(lintTask)
+        it.enabled = false
+    }
+    tasks.register("lintRelease") {
+        it.dependsOn(lintTask)
+        it.enabled = false
+    }
+
+    // The lintAnalyzeDebug task is used by `androidx-studio-integration-lint.sh`.
+    tasks.register("lintAnalyzeDebug") { it.enabled = false }
+
+    addToBuildOnServer(lintTask)
+
+    // For Android projects, we can run lint configuration last using `DslLifecycle.finalizeDsl`;
+    // however, we need to run it using `Project.afterEvaluate` for non-Android projects.
+    configureLint(project.extensions.getByType(), isLibrary = true)
+}
+
+/**
+ * Registers the `lintDebug` task if there are debug variants present.
+ *
+ * This method *must* run after evaluation.
+ */
+private fun Project.registerLintDebugIfNeededAfterEvaluate() {
+    val variantNames = project.agpVariants.map { it.name }
+    if (!variantNames.contains("debug")) {
+        tasks.register("lintDebug") { task ->
+            // The lintDebug tasks depends on lint tasks for all debug variants.
+            variantNames
+                .filter { it.contains("debug", ignoreCase = true) }
+                .map { tasks.named("lint${it.camelCase()}") }
+                .forEach { task.dependsOn(it) }
+        }
+    }
+}
+
+private fun String.camelCase() =
+    replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() }
+
+/**
+ * If the project is targeting Android and using the AIDL build feature, installs AIDL source
+ * directories on lint tasks.
+ *
+ * Adapted from AndroidXComposeImplPlugin's `configureLintForMultiplatformLibrary` extension
+ * function. See b/189250111 for AGP feature request.
  *
  * The `UnstableAidlAnnotationDetector` check from `lint-checks` requires that _only_ unstable AIDL
  * files are passed to Lint, e.g. files in the AGP-defined `aidl` source set but not files in the
  * Stable AIDL plugin-defined `stableAidl` source set. If we decide to lint Stable AIDL files, we'll
  * need some other way to distinguish stable from unstable AIDL.
+ *
+ * This method *must* run after evaluation.
  */
-fun Project.configureLintForAidl() {
-    afterEvaluate {
-        val extension = project.extensions.findByType<BaseExtension>() ?: return@afterEvaluate
-        if (extension.buildFeatures.aidl != true) return@afterEvaluate
+private fun Project.configureLintForAidlAfterEvaluate() {
+    // BaseExtension needed to access resolved source files on `aidl`.
+    val extension = project.extensions.findByType<BaseExtension>() ?: return
+    val mainAidl = extension.sourceSets.getByName("main").aidl.getSourceFiles()
 
-        val mainAidl = extension.sourceSets.getByName("main").aidl.getSourceFiles()
+    /** Helper function to add the missing sourcesets to this [VariantInputs] */
+    fun VariantInputs.addSourceSets() {
+        // Each variant has a source provider for the variant (such as debug) and the 'main'
+        // variant. The actual files that Lint will run on is both of these providers
+        // combined - so we can just add the dependencies to the first we see.
+        val variantAidl = extension.sourceSets.getByName(name.get()).aidl.getSourceFiles()
+        val sourceProvider = sourceProviders.get().firstOrNull() ?: return
+        sourceProvider.javaDirectories.withChangesAllowed { from(mainAidl, variantAidl) }
+    }
 
-        /** Helper function to add the missing sourcesets to this [VariantInputs] */
-        fun VariantInputs.addSourceSets() {
-            // Each variant has a source provider for the variant (such as debug) and the 'main'
-            // variant. The actual files that Lint will run on is both of these providers
-            // combined - so we can just add the dependencies to the first we see.
-            val variantAidl = extension.sourceSets.getByName(name.get()).aidl.getSourceFiles()
-            val sourceProvider = sourceProviders.get().firstOrNull() ?: return
-            sourceProvider.javaDirectories.withChangesAllowed { from(mainAidl, variantAidl) }
+    // Lint for libraries is split into two tasks - analysis, and reporting. We need to
+    // add the new sources to both, so all parts of the pipeline are aware.
+    project.tasks.withType<AndroidLintAnalysisTask>().configureEach {
+        it.variantInputs.addSourceSets()
+    }
+
+    project.tasks.withType<AndroidLintTask>().configureEach {
+        it.variantInputs.addSourceSets()
+    }
+
+    // Also configure the model writing task, so that we don't run into mismatches between
+    // analyzed sources in one module and a downstream module
+    project.tasks.withType<LintModelWriterTask>().configureEach {
+        it.variantInputs.addSourceSets()
+    }
+}
+
+/**
+ * If the project is using multiplatform, adds configurations and source sets expected by the lint
+ * plugin, which allows it to configure itself when running against a non-Android multiplatform
+ * project.
+ *
+ * The version of lint that we're using does not directly support Kotlin multiplatform, but we can
+ * synthesize the necessary configurations and source sets from existing `jvm` configurations and
+ * `kotlinSourceSets`, respectively.
+ *
+ * This method *must* run after evaluation.
+ */
+private fun Project.addSourceSetsForMultiplatformAfterEvaluate() {
+    val kmpTargets = project.multiplatformExtension?.targets ?: return
+
+    // Synthesize target configurations based on multiplatform configurations.
+    val kmpApiElements = kmpTargets.map { it.apiElementsConfigurationName }
+    val kmpRuntimeElements = kmpTargets.map { it.runtimeElementsConfigurationName }
+    listOf(
+        kmpRuntimeElements to "runtimeElements",
+        kmpApiElements to "apiElements"
+    ).forEach { (kmpConfigNames, targetConfigName) ->
+        project.configurations.maybeCreate(targetConfigName).apply {
+            kmpConfigNames
+                .mapNotNull { configName -> project.configurations.findByName(configName) }
+                .forEach { config -> extendsFrom(config) }
         }
+    }
 
-        // Lint for libraries is split into two tasks - analysis, and reporting. We need to
-        // add the new sources to both, so all parts of the pipeline are aware.
-        project.tasks.withType<AndroidLintAnalysisTask>().configureEach {
-            it.variantInputs.addSourceSets()
-        }
-
-        project.tasks.withType<AndroidLintTask>().configureEach { it.variantInputs.addSourceSets() }
-
-        // Also configure the model writing task, so that we don't run into mismatches between
-        // analyzed sources in one module and a downstream module
-        project.tasks.withType<LintModelWriterTask>().configureEach {
-            it.variantInputs.addSourceSets()
+    // Synthesize source sets based on multiplatform source sets.
+    val javaExtension = project.extensions.findByType(JavaPluginExtension::class.java)
+        ?: throw GradleException("Failed to find extension of type 'JavaPluginExtension'")
+    listOf(
+        "main" to "main",
+        "test" to "test"
+    ).forEach { (kmpCompilationName, targetSourceSetName) ->
+        javaExtension.sourceSets.maybeCreate(targetSourceSetName).apply {
+            kmpTargets
+                .mapNotNull { target -> target.compilations.findByName(kmpCompilationName) }
+                .flatMap { compilation -> compilation.kotlinSourceSets }
+                .flatMap { sourceSet -> sourceSet.kotlin.srcDirs }
+                .forEach { srcDirs -> java.srcDirs += srcDirs }
         }
     }
 }
 
-fun Project.configureLint(lint: Lint, extension: AndroidXExtension, isLibrary: Boolean) {
+/**
+ * If the project is using multiplatform targeted to Android, adds source sets directly to lint
+ * tasks, which allows it to run against Android multiplatform projects.
+ *
+ * Lint is not aware of MPP, and MPP doesn't configure Lint. There is no built-in API to adjust the
+ * default Lint task's sources, so we use this hack to manually add sources for MPP source sets. In
+ * the future, with the new Kotlin Project Model (https://youtrack.jetbrains.com/issue/KT-42572) and
+ * an AGP / MPP integration plugin, this will no longer be needed. See also b/195329463.
+ */
+private fun Project.addSourceSetsForAndroidMultiplatformAfterEvaluate() {
+    val multiplatformExtension = project.multiplatformExtension ?: return
+    multiplatformExtension.targets.findByName("android") ?: return
+
+    val androidMain = multiplatformExtension.sourceSets.findByName("androidMain")
+        ?: throw GradleException("Failed to find source set with name 'androidMain'")
+
+    // Get all the source sets androidMain transitively / directly depends on.
+    val dependencySourceSets = androidMain.withClosure(KotlinSourceSet::dependsOn)
+
+    /**
+     * Helper function to add the missing sourcesets to this [VariantInputs]
+     */
+    fun VariantInputs.addSourceSets() {
+        // Each variant has a source provider for the variant (such as debug) and the 'main'
+        // variant. The actual files that Lint will run on is both of these providers
+        // combined - so we can just add the dependencies to the first we see.
+        val sourceProvider = sourceProviders.get().firstOrNull() ?: return
+        dependencySourceSets.forEach { sourceSet ->
+            sourceProvider.javaDirectories.withChangesAllowed {
+                from(sourceSet.kotlin.sourceDirectories)
+            }
+        }
+    }
+
+    // Lint for libraries is split into two tasks - analysis, and reporting. We need to
+    // add the new sources to both, so all parts of the pipeline are aware.
+    project.tasks.withType<AndroidLintAnalysisTask>().configureEach {
+        it.variantInputs.addSourceSets()
+    }
+
+    project.tasks.withType<AndroidLintTask>().configureEach { it.variantInputs.addSourceSets() }
+
+    // Also configure the model writing task, so that we don't run into mismatches between
+    // analyzed sources in one module and a downstream module
+    project.tasks.withType<LintModelWriterTask>().configureEach {
+        it.variantInputs.addSourceSets()
+    }
+}
+
+private fun Project.configureLint(lint: Lint, isLibrary: Boolean) {
+    val extension = project.androidXExtension
+    val isMultiplatform = project.multiplatformExtension != null
     val lintChecksProject =
         project.rootProject.findProject(":lint-checks")
             ?: if (allowMissingLintProject()) {
@@ -144,7 +299,9 @@
 
     project.dependencies.add("lintChecks", lintChecksProject)
 
-    project.configureLintForAidl()
+    afterEvaluate {
+        addSourceSetsForAndroidMultiplatformAfterEvaluate()
+    }
 
     // The purpose of this specific project is to test that lint is running, so
     // it contains expected violations that we do not want to trigger a build failure
@@ -198,6 +355,13 @@
             fatal.add("VisibleForTests")
         }
 
+        if (isMultiplatform) {
+            // Disable classfile-based checks because lint cannot find the class files for
+            // multiplatform projects and `SourceSet.java.classesDirectory` is not configurable.
+            // This is not ideal, but it's better than having no lint checks at all.
+            disable.add("LintError")
+        }
+
         // Reenable after b/238892319 is resolved
         disable.add("NotificationPermission")
 
@@ -291,46 +455,6 @@
 }
 
 /**
- * Lint on multiplatform projects is only applied to Java code and android source sets. To force it
- * to run on JVM code, we add the java source sets that lint looks for, but use the sources
- * directories of the JVM source sets if they exist.
- */
-fun Project.configureLintForMultiplatform(extension: AndroidXExtension) = afterEvaluate {
-    // if lint has been applied through some other mechanism, this step is unnecessary
-    runCatching { project.tasks.named("lint") }
-        .onSuccess {
-            return@afterEvaluate
-        }
-    val jvmTarget =
-        project.multiplatformExtension?.targets?.findByName("jvm") ?: return@afterEvaluate
-    val runtimeConfiguration =
-        project.configurations.findByName("jvmRuntimeElements") ?: return@afterEvaluate
-    val apiConfiguration =
-        project.configurations.findByName("jvmApiElements") ?: return@afterEvaluate
-    val javaExtension =
-        project.extensions.findByType(JavaPluginExtension::class.java) ?: return@afterEvaluate
-    project.configurations.maybeCreate("runtimeElements").apply {
-        extendsFrom(runtimeConfiguration)
-    }
-    project.configurations.maybeCreate("apiElements").apply { extendsFrom(apiConfiguration) }
-    val mainSourceSets = jvmTarget.compilations.getByName("main").kotlinSourceSets
-    val testSourceSets = jvmTarget.compilations.getByName("test").kotlinSourceSets
-    javaExtension.sourceSets.maybeCreate("main").apply {
-        java.setSrcDirs(mainSourceSets.flatMap { it.kotlin.srcDirs })
-        java.classesDirectory
-    }
-    javaExtension.sourceSets.maybeCreate("test").apply {
-        java.srcDirs.addAll(testSourceSets.flatMap { it.kotlin.srcDirs })
-    }
-    project.configureNonAndroidProjectForLint(extension)
-
-    // Disable classfile based checks because lint cannot find the classfiles for multiplatform
-    // projects, and SourceSet.java.classesDirectory is not configurable. This is not ideal, but
-    // better than having no lint checks at all.
-    extensions.getByType<Lint>().disable.add("LintError")
-}
-
-/**
  * Lint uses [ConfigurableFileCollection.disallowChanges] during initialization, which prevents
  * modifying the file collection separately (there is no time to configure it before AGP has
  * initialized and disallowed changes). This uses reflection to temporarily allow changes, and apply
@@ -346,5 +470,5 @@
     disallowChanges.set(this, true)
 }
 
-val Project.lintBaseline: RegularFileProperty
-    get() = project.objects.fileProperty().fileValue(File(projectDir, "/lint-baseline.xml"))
+private val Project.lintBaseline: RegularFileProperty
+    get() = project.objects.fileProperty().fileValue(File(projectDir, "lint-baseline.xml"))
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/Release.kt b/buildSrc/private/src/main/kotlin/androidx/build/Release.kt
index 8aab95b..76a41ec 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/Release.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/Release.kt
@@ -259,7 +259,9 @@
                 onConfigure = { archiveTask: VerifyVersionFilesTask ->
                     archiveTask.group = "Distribution"
                     archiveTask.description = "Builds all archives for publishing"
-                    archiveTask.repositoryDirectory = project.rootProject.getRepositoryDirectory()
+                    archiveTask.repositoryDirectory.set(
+                        project.rootProject.getRepositoryDirectory()
+                    )
                 },
                 onRegister = {}
             )
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/SourceJarTaskHelper.kt b/buildSrc/private/src/main/kotlin/androidx/build/SourceJarTaskHelper.kt
index 718ea413..e98648a 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/SourceJarTaskHelper.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/SourceJarTaskHelper.kt
@@ -20,7 +20,6 @@
 import androidx.build.dackka.docsPlatform
 import com.android.build.gradle.LibraryExtension
 import com.google.gson.GsonBuilder
-import java.io.File
 import java.util.Locale
 import org.gradle.api.DefaultTask
 import org.gradle.api.GradleException
@@ -30,6 +29,7 @@
 import org.gradle.api.attributes.DocsType
 import org.gradle.api.attributes.Usage
 import org.gradle.api.file.DuplicatesStrategy
+import org.gradle.api.file.RegularFileProperty
 import org.gradle.api.plugins.JavaPluginExtension
 import org.gradle.api.tasks.CacheableTask
 import org.gradle.api.tasks.Input
@@ -136,10 +136,10 @@
             ?: throw GradleException(
                 "Unable to find multiplatform extension while configuring multiplatform source JAR"
             )
-    val metadataFile = buildDir.resolve(PROJECT_STRUCTURE_METADATA_FILEPATH)
+    val metadataFile = layout.buildDirectory.file(PROJECT_STRUCTURE_METADATA_FILEPATH)
     val multiplatformMetadataTask =
         tasks.register("createMultiplatformMetadata", CreateMultiplatformMetadata::class.java) {
-            it.metadataFile = metadataFile
+            it.metadataFile.set(metadataFile)
             it.sourceSetJson = createSourceSetMetadata(extension)
         }
     val sourceJar =
@@ -217,11 +217,11 @@
 abstract class CreateMultiplatformMetadata : DefaultTask() {
     @Input lateinit var sourceSetJson: String
 
-    @OutputFile lateinit var metadataFile: File
+    @get:OutputFile abstract val metadataFile: RegularFileProperty
 
     @TaskAction
     fun execute() {
-        metadataFile.apply {
+        metadataFile.get().asFile.apply {
             parentFile.mkdirs()
             createNewFile()
             writeText(sourceSetJson)
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/VerifyVersionFilesTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/VerifyVersionFilesTask.kt
index f2f6a46..56fd8f0 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/VerifyVersionFilesTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/VerifyVersionFilesTask.kt
@@ -16,11 +16,11 @@
 
 package androidx.build
 
-import java.io.File
 import java.io.FileInputStream
 import java.util.zip.ZipEntry
 import java.util.zip.ZipInputStream
 import org.gradle.api.DefaultTask
+import org.gradle.api.file.DirectoryProperty
 import org.gradle.api.tasks.CacheableTask
 import org.gradle.api.tasks.InputDirectory
 import org.gradle.api.tasks.PathSensitive
@@ -29,13 +29,13 @@
 
 /** Task for verifying version files in Androidx artifacts */
 @CacheableTask
-open class VerifyVersionFilesTask : DefaultTask() {
-
-    @InputDirectory @PathSensitive(PathSensitivity.RELATIVE) lateinit var repositoryDirectory: File
+abstract class VerifyVersionFilesTask : DefaultTask() {
+    @get:[InputDirectory PathSensitive(PathSensitivity.RELATIVE)]
+    abstract val repositoryDirectory: DirectoryProperty
 
     @TaskAction
     fun verifyVersionFilesPresent() {
-        repositoryDirectory.walk().forEach { file ->
+        repositoryDirectory.asFile.get().walk().forEach { file ->
             var expectedPrefix = "androidx"
             if (file.path.contains("/libyuv/"))
                 expectedPrefix = "libyuv_libyuv" // external library that we don't publish
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/VersionFileWriterTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/VersionFileWriterTask.kt
index 708dc21..82b0dea 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/VersionFileWriterTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/VersionFileWriterTask.kt
@@ -71,7 +71,7 @@
 
             it.version.set(version)
             it.relativePath.set(String.format("META-INF/%s_%s.version", group, artifactId))
-            it.outputDir.set(File(buildDir, "generatedVersionFile"))
+            it.outputDir.set(layout.buildDirectory.dir("generatedVersionFile"))
 
             // We only add version file if is a library that is publishing.
             it.enabled = androidXExtension.shouldPublish()
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/dackka/DackkaTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/dackka/DackkaTask.kt
index 5806ed1..59b5d8f 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/dackka/DackkaTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/dackka/DackkaTask.kt
@@ -22,6 +22,7 @@
 import javax.inject.Inject
 import org.gradle.api.DefaultTask
 import org.gradle.api.file.ConfigurableFileCollection
+import org.gradle.api.file.DirectoryProperty
 import org.gradle.api.file.FileCollection
 import org.gradle.api.file.RegularFileProperty
 import org.gradle.api.model.ObjectFactory
@@ -49,7 +50,7 @@
     DefaultTask() {
 
     @get:[InputFiles PathSensitive(PathSensitivity.RELATIVE)]
-    lateinit var projectStructureMetadataFile: File
+    abstract val projectStructureMetadataFile: RegularFileProperty
 
     // Classpath containing Dackka
     @get:Classpath abstract val dackkaClasspath: ConfigurableFileCollection
@@ -64,22 +65,22 @@
 
     // Directory containing the code samples
     @get:[InputFiles PathSensitive(PathSensitivity.RELATIVE)]
-    lateinit var samplesDir: File
+    abstract val samplesDir: DirectoryProperty
 
     // Directory containing the JVM source code for Dackka to process
     @get:[InputFiles PathSensitive(PathSensitivity.RELATIVE)]
-    lateinit var jvmSourcesDir: File
+    abstract val jvmSourcesDir: DirectoryProperty
 
     // Directory containing the multiplatform source code for Dackka to process
     @get:[InputFiles PathSensitive(PathSensitivity.RELATIVE)]
-    lateinit var multiplatformSourcesDir: File
+    abstract val multiplatformSourcesDir: DirectoryProperty
 
     // Directory containing the docs project and package-lists
     @get:[InputFiles PathSensitive(PathSensitivity.RELATIVE)]
     lateinit var docsProjectDir: File
 
     // Location of generated reference docs
-    @get:OutputDirectory lateinit var destinationDir: File
+    @get:OutputDirectory abstract val destinationDir: DirectoryProperty
 
     // Set of packages to exclude for refdoc generation for all languages
     @Input lateinit var excludedPackages: Set<String>
@@ -120,6 +121,8 @@
         val gson = GsonBuilder().create()
         val multiplatformSourceSets =
             projectStructureMetadataFile
+                .get()
+                .asFile
                 .takeIf { it.exists() }
                 ?.let { metadataFile ->
                     val metadata =
@@ -127,7 +130,7 @@
                     metadata.sourceSets.map { sourceSet ->
                         val analysisPlatform =
                             DokkaAnalysisPlatform.valueOf(sourceSet.analysisPlatform.uppercase())
-                        val sourceDir = multiplatformSourcesDir.resolve(sourceSet.name)
+                        val sourceDir = multiplatformSourcesDir.get().asFile.resolve(sourceSet.name)
                         DokkaInputModels.SourceSet(
                             id = sourceSetIdForSourceSet(sourceSet.name),
                             displayName = sourceSet.name,
@@ -155,7 +158,7 @@
                 analysisPlatform = "jvm",
                 sourceRoots = objects.fileCollection().from(jvmSourcesDir),
                 samples = objects.fileCollection().from(samplesDir, frameworkSamplesDir),
-                includes = objects.fileCollection().from(includesFiles(jvmSourcesDir)),
+                includes = objects.fileCollection().from(includesFiles(jvmSourcesDir.get().asFile)),
                 classpath = dependenciesClasspath,
                 externalDocumentationLinks = externalDocs,
                 dependentSourceSets = emptyList(),
@@ -178,7 +181,7 @@
         val jsonMap =
             mapOf(
                 "moduleName" to "",
-                "outputDir" to destinationDir.path,
+                "outputDir" to destinationDir.get().asFile.path,
                 "globalLinks" to linksConfiguration,
                 "sourceSets" to sourceSets(),
                 "offlineMode" to "true",
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt b/buildSrc/private/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
index a5a0384..724504f 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
@@ -566,10 +566,6 @@
                     ":media2:media2-session:version-compat-tests:service",
                     ":media2:media2-session:version-compat-tests:client-previous",
                     ":media2:media2-session:version-compat-tests:service-previous"
-                ), // Link graphics and material to always run @Large in presubmit per b/160624022
-                setOf(
-                    ":compose:ui:ui-graphics",
-                    ":compose:material:material"
                 ), // Link material and material-ripple
                 setOf(":compose:material:material-ripple", ":compose:material:material"),
                 setOf(
@@ -631,6 +627,10 @@
                     ":profileinstaller:integration-tests:" +
                         "profile-verification-sample-no-initializer",
                     ":benchmark:integration-tests:baselineprofile-consumer",
+                ),
+                setOf(
+                    ":window:integration-tests:macrobenchmark",
+                    ":window:integration-tests:macrobenchmark-target",
                 )
             )
 
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXDocsImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXDocsImplPlugin.kt
index 606f0ad5..5b46f6a 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXDocsImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXDocsImplPlugin.kt
@@ -52,10 +52,13 @@
 import org.gradle.api.attributes.LibraryElements
 import org.gradle.api.attributes.Usage
 import org.gradle.api.file.ArchiveOperations
+import org.gradle.api.file.Directory
+import org.gradle.api.file.DirectoryProperty
 import org.gradle.api.file.DuplicatesStrategy
 import org.gradle.api.file.FileCollection
 import org.gradle.api.file.FileSystemOperations
 import org.gradle.api.file.RegularFile
+import org.gradle.api.file.RegularFileProperty
 import org.gradle.api.model.ObjectFactory
 import org.gradle.api.plugins.JavaBasePlugin
 import org.gradle.api.provider.Property
@@ -121,7 +124,7 @@
                 distributionDirectory = project.getDistributionDirectory()
             }
 
-        val unzippedSamplesSources = File(project.buildDir, "unzippedSampleSources")
+        val unzippedSamplesSources = project.layout.buildDirectory.dir("unzippedSampleSources")
         val unzipSamplesTask =
             configureUnzipTask(
                 project,
@@ -130,11 +133,13 @@
                 samplesSourcesConfiguration
             )
 
-        val unzippedJvmSourcesDirectory = File(project.buildDir, "unzippedJvmSources")
+        val unzippedJvmSourcesDirectory = project.layout.buildDirectory.dir("unzippedJvmSources")
         val unzippedMultiplatformSourcesDirectory =
-            File(project.buildDir, "unzippedMultiplatformSources")
+            project.layout.buildDirectory.dir("unzippedMultiplatformSources")
         val mergedProjectMetadata =
-            File(project.buildDir, "project_metadata/$PROJECT_STRUCTURE_METADATA_FILENAME")
+            project.layout.buildDirectory.file(
+                "project_metadata/$PROJECT_STRUCTURE_METADATA_FILENAME"
+            )
         val unzipJvmSourcesTask =
             configureUnzipJvmSourcesTasks(
                 project,
@@ -171,7 +176,7 @@
     private fun configureUnzipTask(
         project: Project,
         taskName: String,
-        destinationDirectory: File,
+        destinationDirectory: Provider<Directory>,
         docsConfiguration: Configuration
     ): TaskProvider<Sync> {
         return project.tasks.register(taskName, Sync::class.java) { task ->
@@ -210,7 +215,7 @@
      */
     private fun configureUnzipJvmSourcesTasks(
         project: Project,
-        destinationDirectory: File,
+        destinationDirectory: Provider<Directory>,
         docsConfiguration: Configuration
     ): TaskProvider<Sync> {
         return project.tasks.register("unzipJvmSources", Sync::class.java) { task ->
@@ -239,12 +244,12 @@
      */
     private fun configureMultiplatformInputsTasks(
         project: Project,
-        unzippedMultiplatformSourcesDirectory: File,
+        unzippedMultiplatformSourcesDirectory: Provider<Directory>,
         multiplatformDocsSourcesConfiguration: Configuration,
-        mergedProjectMetadata: File
+        mergedProjectMetadata: Provider<RegularFile>
     ): TaskProvider<MergeMultiplatformMetadataTask> {
         val tempMultiplatformMetadataDirectory =
-            File(project.buildDir, "tmp/multiplatformMetadataFiles")
+            project.layout.buildDirectory.dir("tmp/multiplatformMetadataFiles")
         // unzip the sources into source folder and metadata files into folders per project
         val unzipMultiplatformSources =
             project.tasks.register(
@@ -254,8 +259,8 @@
                 it.inputJars.set(
                     multiplatformDocsSourcesConfiguration.incoming.artifactView {}.files
                 )
-                it.metadataOutput = tempMultiplatformMetadataDirectory
-                it.sourceOutput = unzippedMultiplatformSourcesDirectory
+                it.metadataOutput.set(tempMultiplatformMetadataDirectory)
+                it.sourceOutput.set(unzippedMultiplatformSourcesDirectory)
             }
         // merge all the metadata files from the individual project dirs
         return project.tasks.register(
@@ -263,8 +268,8 @@
             MergeMultiplatformMetadataTask::class.java
         ) {
             it.dependsOn(unzipMultiplatformSources)
-            it.mergedProjectMetadata = mergedProjectMetadata
-            it.inputDirectory = tempMultiplatformMetadataDirectory
+            it.mergedProjectMetadata.set(mergedProjectMetadata)
+            it.inputDirectory.set(tempMultiplatformMetadataDirectory)
         }
     }
 
@@ -439,18 +444,18 @@
 
     private fun configureDackka(
         project: Project,
-        unzippedJvmSourcesDirectory: File,
-        unzippedMultiplatformSourcesDirectory: File,
+        unzippedJvmSourcesDirectory: Provider<Directory>,
+        unzippedMultiplatformSourcesDirectory: Provider<Directory>,
         unzipJvmSourcesTask: TaskProvider<Sync>,
         configureMultiplatformSourcesTask: TaskProvider<MergeMultiplatformMetadataTask>,
-        unzippedSamplesSources: File,
+        unzippedSamplesSources: Provider<Directory>,
         unzipSamplesTask: TaskProvider<Sync>,
         dependencyClasspath: FileCollection,
         buildOnServer: TaskProvider<*>,
         docsConfiguration: Configuration,
-        mergedProjectMetadata: File
+        mergedProjectMetadata: Provider<RegularFile>
     ) {
-        val generatedDocsDir = project.file("${project.buildDir}/docs")
+        val generatedDocsDir = project.layout.buildDirectory.dir("docs")
 
         val dackkaConfiguration =
             project.configurations.create("dackka").apply {
@@ -465,8 +470,7 @@
                 task.destinationFile.set(getMetadataRegularFile(project))
             }
 
-        val metricsDirectory = project.buildDir
-        val metricsFile = File(metricsDirectory, "build-metrics.json")
+        val metricsFile = project.layout.buildDirectory.file("build-metrics.json")
         val projectName = project.name
 
         val dackkaTask =
@@ -484,11 +488,11 @@
                     group = JavaBasePlugin.DOCUMENTATION_GROUP
 
                     dackkaClasspath.from(project.files(dackkaConfiguration))
-                    destinationDir = generatedDocsDir
+                    destinationDir.set(generatedDocsDir)
                     frameworkSamplesDir = File(project.rootDir, "samples")
-                    samplesDir = unzippedSamplesSources
-                    jvmSourcesDir = unzippedJvmSourcesDirectory
-                    multiplatformSourcesDir = unzippedMultiplatformSourcesDirectory
+                    samplesDir.set(unzippedSamplesSources)
+                    jvmSourcesDir.set(unzippedJvmSourcesDirectory)
+                    multiplatformSourcesDir.set(unzippedMultiplatformSourcesDirectory)
                     docsProjectDir = File(project.rootDir, "docs-public")
                     dependenciesClasspath =
                         dependencyClasspath +
@@ -498,7 +502,7 @@
                     excludedPackagesForJava = hiddenPackagesJava
                     excludedPackagesForKotlin = emptySet()
                     libraryMetadataFile.set(getMetadataRegularFile(project))
-                    projectStructureMetadataFile = mergedProjectMetadata
+                    projectStructureMetadataFile.set(mergedProjectMetadata)
                     // See go/dackka-source-link for details on this link.
                     baseSourceLink = "https://cs.android.com/search?" + "q=file:%s+class:%s"
                     annotationsNotToDisplay = hiddenAnnotations
@@ -512,10 +516,10 @@
                     task.doLast {
                         val taskEndTime = LocalDateTime.now()
                         val duration = Duration.between(taskStartTime, taskEndTime).toMillis()
-                        metricsDirectory.mkdirs()
-                        metricsFile.writeText(
-                            "{ \"${projectName}_docs_execution_duration\": $duration }"
-                        )
+                        metricsFile
+                            .get()
+                            .asFile
+                            .writeText("{ \"${projectName}_docs_execution_duration\": $duration }")
                     }
                 }
             }
@@ -699,9 +703,9 @@
 
     @get:Classpath abstract val inputJars: Property<FileCollection>
 
-    @OutputDirectory lateinit var metadataOutput: File
+    @get:OutputDirectory abstract val metadataOutput: DirectoryProperty
 
-    @OutputDirectory lateinit var sourceOutput: File
+    @get:OutputDirectory abstract val sourceOutput: DirectoryProperty
 
     @get:Inject abstract val fileSystemOperations: FileSystemOperations
     @get:Inject abstract val archiveOperations: ArchiveOperations
@@ -718,7 +722,7 @@
         sources.forEach { (name, fileTree) ->
             fileSystemOperations.sync {
                 it.from(fileTree)
-                it.into(metadataOutput.resolve(name))
+                it.into(metadataOutput.file(name))
                 it.include("META-INF/*")
             }
         }
@@ -727,15 +731,19 @@
 
 /** Merges multiplatform metadata files created by [CreateMultiplatformMetadata] */
 @CacheableTask
-abstract class MergeMultiplatformMetadataTask() : DefaultTask() {
+abstract class MergeMultiplatformMetadataTask : DefaultTask() {
 
-    @get:InputFiles @get:PathSensitive(PathSensitivity.RELATIVE) lateinit var inputDirectory: File
-    @OutputFile lateinit var mergedProjectMetadata: File
+    @get:InputFiles
+    @get:PathSensitive(PathSensitivity.RELATIVE)
+    abstract val inputDirectory: DirectoryProperty
+    @get:OutputFile abstract val mergedProjectMetadata: RegularFileProperty
 
     @TaskAction
     fun execute() {
         val mergedMetadata = ProjectStructureMetadata(sourceSets = listOf())
         inputDirectory
+            .get()
+            .asFile
             .walkTopDown()
             .filter { file -> file.name == PROJECT_STRUCTURE_METADATA_FILENAME }
             .forEach { metaFile ->
@@ -746,7 +754,7 @@
             }
         val gson = GsonBuilder().setPrettyPrinting().create()
         val json = gson.toJson(mergedMetadata)
-        mergedProjectMetadata.apply {
+        mergedProjectMetadata.get().asFile.apply {
             parentFile.mkdirs()
             createNewFile()
             writeText(json)
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/libabigail/GenerateNativeApiTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/libabigail/GenerateNativeApiTask.kt
index 8cfcb2e..b91eadd 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/libabigail/GenerateNativeApiTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/libabigail/GenerateNativeApiTask.kt
@@ -22,6 +22,7 @@
 import javax.inject.Inject
 import org.gradle.api.DefaultTask
 import org.gradle.api.GradleException
+import org.gradle.api.file.DirectoryProperty
 import org.gradle.api.provider.ListProperty
 import org.gradle.api.provider.Property
 import org.gradle.api.tasks.CacheableTask
@@ -49,7 +50,7 @@
     @get:Inject abstract val workerExecutor: WorkerExecutor
 
     @get:[InputDirectory PathSensitive(PathSensitivity.RELATIVE)]
-    abstract val prefabDirectory: Property<File>
+    abstract val prefabDirectory: DirectoryProperty
 
     @get:Internal abstract val projectRootDir: Property<File>
 
@@ -75,7 +76,7 @@
             destinationDir.deleteRecursively()
             destinationDir.mkdirs()
         }
-        val prefabDir = prefabDirectory.get()
+        val prefabDir = prefabDirectory.get().asFile
         val workQueue = workerExecutor.processIsolation()
         artifactNames.get().forEach { moduleName ->
             val module = prefabDir.resolve("modules/$moduleName/libs")
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/libabigail/NativeApiTasks.kt b/buildSrc/private/src/main/kotlin/androidx/build/libabigail/NativeApiTasks.kt
index 24ebc81..c483660 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/libabigail/NativeApiTasks.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/libabigail/NativeApiTasks.kt
@@ -47,7 +47,7 @@
                 task.description = "Generates API files from native source"
                 task.projectRootDir.set(project.rootDir)
                 task.prefabDirectory.set(
-                    project.buildDir.resolve("intermediates/prefab_package/release/prefab")
+                    project.layout.buildDirectory.dir("intermediates/prefab_package/release/prefab")
                 )
                 task.artifactNames.set(artifactNames)
                 task.apiLocation.set(builtApiLocation)
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/paparazzi/AndroidXPaparazziImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/paparazzi/AndroidXPaparazziImplPlugin.kt
index 3d09a823a..ec08a58 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/paparazzi/AndroidXPaparazziImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/paparazzi/AndroidXPaparazziImplPlugin.kt
@@ -28,8 +28,10 @@
 import org.gradle.api.Project
 import org.gradle.api.artifacts.type.ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE
 import org.gradle.api.artifacts.type.ArtifactTypeDefinition.JAR_TYPE
+import org.gradle.api.file.Directory
 import org.gradle.api.file.FileCollection
 import org.gradle.api.file.FileSystemOperations
+import org.gradle.api.provider.Provider
 import org.gradle.api.tasks.Copy
 import org.gradle.api.tasks.PathSensitivity
 import org.gradle.api.tasks.testing.Test
@@ -84,7 +86,11 @@
         outputs.dir(reportDirectory).withPropertyName("paparazziReportDir")
 
         // Clean the contents of the report directory before each test run
-        doFirst { fileSystemOperations.delete { it.delete(cachedReportDirectory.listFiles()) } }
+        doFirst {
+            fileSystemOperations.delete {
+                it.delete(cachedReportDirectory.get().asFile.listFiles())
+            }
+        }
 
         // Set non-path system properties at configuration time, so that changes invalidate caching
         prefixedSystemProperties(
@@ -103,7 +109,7 @@
                 "platformDir" to platformDirectory.canonicalPath,
                 "assetsDir" to ".", // TODO: Merged assets dirs? (needed for compose?)
                 "resDir" to ".", // TODO: Merged resource dirs? (needed for compose?)
-                "reportDir" to cachedReportDirectory.canonicalPath,
+                "reportDir" to cachedReportDirectory.get().asFile.canonicalPath,
                 "goldenRootDir" to cachedGoldenRootDirectory.canonicalPath,
             )
         }
@@ -174,8 +180,8 @@
         get() = path.replace(':', '/').trim('/')
 
     /** Output directory for storing reports and images. */
-    private val Test.reportDirectory
-        get() = project.buildDir.resolve("paparazzi").resolve(name)
+    private val Test.reportDirectory: Provider<Directory>
+        get() = project.layout.buildDirectory.dir("paparazzi/$name")
 
     /** Add a testImplementation dependency on the wrapper test utils library. */
     private fun Project.addTestUtilsDependency() {
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/resources/CheckResourceApiReleaseTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/resources/CheckResourceApiReleaseTask.kt
index 99897c9..683e597 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/resources/CheckResourceApiReleaseTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/resources/CheckResourceApiReleaseTask.kt
@@ -20,9 +20,9 @@
 import java.io.File
 import org.gradle.api.DefaultTask
 import org.gradle.api.GradleException
+import org.gradle.api.file.RegularFileProperty
 import org.gradle.api.provider.Property
 import org.gradle.api.tasks.CacheableTask
-import org.gradle.api.tasks.InputFile
 import org.gradle.api.tasks.InputFiles
 import org.gradle.api.tasks.Internal
 import org.gradle.api.tasks.PathSensitive
@@ -33,9 +33,9 @@
 @CacheableTask
 abstract class CheckResourceApiReleaseTask : DefaultTask() {
     /** Reference resource API file (in source control). */
-    @get:InputFile
+    @get:InputFiles // InputFiles allows non-existent files, whereas InputFile does not.
     @get:PathSensitive(PathSensitivity.RELATIVE)
-    abstract val referenceApiFile: Property<File>
+    abstract val referenceApiFile: RegularFileProperty
 
     /** Generated resource API file (in build output). */
     @get:Internal abstract val apiLocation: Property<ApiLocation>
@@ -48,7 +48,7 @@
 
     @TaskAction
     fun checkResourceApiRelease() {
-        val referenceApiFile = referenceApiFile.get()
+        val referenceApiFile = referenceApiFile.get().asFile
         val apiFile = apiLocation.get().resourceFile
 
         // Read the current API surface, if any, into memory.
@@ -60,7 +60,12 @@
             }
 
         // Read the reference API surface into memory.
-        val referenceApiSet = referenceApiFile.readLines().toSet()
+        val referenceApiSet =
+            if (referenceApiFile.exists()) {
+                referenceApiFile.readLines().toSet()
+            } else {
+                emptySet()
+            }
 
         // POLICY: Ensure that no resources are removed from the last released version.
         val removedApiSet = referenceApiSet - newApiSet
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/resources/PublicResourcesStubHelper.kt b/buildSrc/private/src/main/kotlin/androidx/build/resources/PublicResourcesStubHelper.kt
index fcedb43..a21e6ea 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/resources/PublicResourcesStubHelper.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/resources/PublicResourcesStubHelper.kt
@@ -23,17 +23,17 @@
 import org.gradle.api.tasks.Copy
 
 fun Project.configurePublicResourcesStub(extension: LibraryExtension) {
-    val targetResFolder = File(project.buildDir, "generated/res/public-stub")
+    val targetRes = project.layout.buildDirectory.dir("generated/res/public-stub")
 
     val generatePublicResourcesTask =
         tasks.register("generatePublicResourcesStub", Copy::class.java) { task ->
             task.from(File(project.getSupportRootFolder(), "buildSrc/res"))
-            task.into(targetResFolder)
+            task.into(targetRes)
         }
 
     extension.libraryVariants.all { variant ->
         variant.registerGeneratedResFolders(
-            project.files(targetResFolder).builtBy(generatePublicResourcesTask)
+            project.files(targetRes).builtBy(generatePublicResourcesTask)
         )
     }
 }
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/AndroidTestConfigBuilder.kt b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/AndroidTestConfigBuilder.kt
index 24d77e9..657f702 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/AndroidTestConfigBuilder.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/AndroidTestConfigBuilder.kt
@@ -23,7 +23,7 @@
     var appApkName: String? = null
     var appApkSha256: String? = null
     lateinit var applicationId: String
-    var isBenchmark: Boolean = false
+    var isMicrobenchmark: Boolean = false
     var isPostsubmit: Boolean = true
     lateinit var minSdk: String
     val tags = mutableListOf<String>()
@@ -40,7 +40,8 @@
 
     fun applicationId(applicationId: String) = apply { this.applicationId = applicationId }
 
-    fun isBenchmark(isBenchmark: Boolean) = apply { this.isBenchmark = isBenchmark }
+    fun isMicrobenchmark(isMicrobenchmark: Boolean) =
+        apply { this.isMicrobenchmark = isMicrobenchmark }
 
     fun isPostsubmit(isPostsubmit: Boolean) = apply { this.isPostsubmit = isPostsubmit }
 
@@ -59,7 +60,7 @@
     fun buildJson(): String {
         val gson = GsonBuilder().setPrettyPrinting().create()
         val instrumentationArgs =
-            if (isBenchmark && !isPostsubmit) {
+            if (isMicrobenchmark && !isPostsubmit) {
                 listOf(
                     InstrumentationArg("notAnnotation", "androidx.test.filters.FlakyTest"),
                     InstrumentationArg("androidx.benchmark.dryRunMode.enable", "true"),
@@ -91,11 +92,11 @@
         sb.append(MODULE_METADATA_TAG_OPTION.replace("APPLICATION_ID", applicationId))
             .append(WIFI_DISABLE_OPTION)
             .append(FLAKY_TEST_OPTION)
-        if (isBenchmark) {
+        if (isMicrobenchmark) {
             if (isPostsubmit) {
-                sb.append(BENCHMARK_POSTSUBMIT_OPTIONS)
+                sb.append(MICROBENCHMARK_POSTSUBMIT_OPTIONS)
             } else {
-                sb.append(BENCHMARK_PRESUBMIT_OPTION)
+                sb.append(MICROBENCHMARK_PRESUBMIT_OPTION)
             }
         }
         sb.append(SETUP_INCLUDE)
@@ -104,7 +105,11 @@
         if (!appApkName.isNullOrEmpty())
             sb.append(APK_INSTALL_OPTION.replace("APK_NAME", appApkName!!))
         sb.append(TARGET_PREPARER_CLOSE)
-            .append(TEST_BLOCK_OPEN)
+        // Post install commands after SuiteApkInstaller is declared
+        if (isMicrobenchmark) {
+            sb.append(benchmarkPostInstallCommandOption(applicationId))
+        }
+        sb.append(TEST_BLOCK_OPEN)
             .append(RUNNER_OPTION.replace("TEST_RUNNER", testRunner))
             .append(PACKAGE_OPTION.replace("APPLICATION_ID", applicationId))
             .append(TEST_BLOCK_CLOSE)
@@ -233,6 +238,18 @@
 """
         .trimIndent()
 
+private fun benchmarkPostInstallCommandOption(packageName: String) =
+    """
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+    <option name="run-command" value="${benchmarkPostInstallCommand(packageName)}" />
+    </target_preparer>
+
+""".trimIndent()
+
+private fun benchmarkPostInstallCommand(packageName: String): String {
+    return "cmd package compile -f -m speed $packageName"
+}
+
 private val SETUP_INCLUDE =
     """
     <include name="google/unbundled/common/setup" />
@@ -296,16 +313,15 @@
 """
         .trimIndent()
 
-private val BENCHMARK_PRESUBMIT_OPTION =
+private val MICROBENCHMARK_PRESUBMIT_OPTION =
     """
     <option name="instrumentation-arg" key="androidx.benchmark.dryRunMode.enable" value="true" />
 
 """
         .trimIndent()
 
-private val BENCHMARK_POSTSUBMIT_OPTIONS =
+private val MICROBENCHMARK_POSTSUBMIT_OPTIONS =
     """
-    <option name="instrumentation-arg" key="androidx.benchmark.output.enable" value="true" />
     <option name="instrumentation-arg" key="listener" value="androidx.benchmark.junit4.InstrumentationResultsRunListener" />
 
 """
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt
index 87b7114..d428c0f 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/GenerateTestConfigurationTask.kt
@@ -134,10 +134,14 @@
         configBuilder.isPostsubmit(!isPresubmit)
         // This section adds metadata tags that will help filter runners to specific modules.
         if (hasBenchmarkPlugin.get()) {
-            configBuilder.isBenchmark(true)
-            if (configBuilder.isPostsubmit) {
-                configBuilder.tag("microbenchmarks")
-            } else {
+            configBuilder.isMicrobenchmark(true)
+
+            // tag microbenchmarks as "microbenchmarks" in either build config, so that benchmark
+            // test configs will always have something to run, regardless of build (though presubmit
+            // builds will still set dry run, and not output metrics)
+            configBuilder.tag("microbenchmarks")
+
+            if (isPresubmit) {
                 // in presubmit, we treat micro benchmarks as regular correctness tests as
                 // they run with dryRunMode to check crashes don't happen, without measurement
                 configBuilder.tag("androidx_unit_tests")
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/OwnersService.kt b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/OwnersService.kt
index 56384dd..10a9cc1 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/OwnersService.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/OwnersService.kt
@@ -38,7 +38,13 @@
     @Input
     fun getSerialized(): String {
         val gson = GsonBuilder().setPrettyPrinting().create()
-        return gson.toJson(testModules.associateBy { it.name })
+        // media service/client tests are created from multiple projects, so we get multiple
+        // entries with the same TestModule.name. This code merges all the TestModule.path entries
+        // across the test modules with the same name.
+        val data = testModules.groupBy { it.name }.map {
+            TestModule(name = it.key, path = it.value.flatMap { module -> module.path })
+        }.associateBy { it.name }
+        return gson.toJson(data)
     }
 
     @TaskAction
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/TestSuiteConfiguration.kt b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/TestSuiteConfiguration.kt
index 1ef27c2..ce67a56 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/TestSuiteConfiguration.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/TestSuiteConfiguration.kt
@@ -21,8 +21,8 @@
 import androidx.build.AndroidXImplPlugin.Companion.ZIP_TEST_CONFIGS_WITH_APKS_TASK
 import androidx.build.asFilenamePrefix
 import androidx.build.dependencyTracker.AffectedModuleDetector
+import androidx.build.getFileInTestConfigDirectory
 import androidx.build.getSupportRootFolder
-import androidx.build.getTestConfigDirectory
 import androidx.build.hasAndroidTestSourceCode
 import androidx.build.hasBenchmarkPlugin
 import androidx.build.isPresubmitBuild
@@ -39,9 +39,10 @@
 import com.android.build.gradle.internal.attributes.VariantAttr
 import com.android.build.gradle.internal.publishing.AndroidArtifacts
 import com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactType
-import java.io.File
 import org.gradle.api.Project
 import org.gradle.api.attributes.Usage
+import org.gradle.api.file.RegularFile
+import org.gradle.api.provider.Provider
 import org.gradle.api.tasks.TaskProvider
 import org.gradle.kotlin.dsl.getByType
 import org.gradle.kotlin.dsl.named
@@ -59,8 +60,7 @@
 ) {
     val xmlName = "${path.asFilenamePrefix()}$variantName.xml"
     val jsonName = "_${path.asFilenamePrefix()}$variantName.json"
-    rootProject.tasks.named("createModuleInfo").configure {
-        it as ModuleInfoGenerator
+    rootProject.tasks.named<ModuleInfoGenerator>("createModuleInfo").configure {
         it.testModules.add(
             TestModule(
                 name = xmlName,
@@ -78,12 +78,12 @@
             task.testFolder.set(artifacts.get(SingleArtifact.APK))
             task.testLoader.set(artifacts.getBuiltArtifactsLoader())
             task.outputTestApk.set(
-                File(getTestConfigDirectory(), "${path.asFilenamePrefix()}-$variantName.apk")
+                getFileInTestConfigDirectory("${path.asFilenamePrefix()}-$variantName.apk")
             )
             task.additionalApkKeys.set(androidXExtension.additionalDeviceTestApkKeys)
             task.additionalTags.set(androidXExtension.additionalDeviceTestTags)
-            task.outputXml.fileValue(File(getTestConfigDirectory(), xmlName))
-            task.outputJson.fileValue(File(getTestConfigDirectory(), jsonName))
+            task.outputXml.set(getFileInTestConfigDirectory(xmlName))
+            task.outputJson.set(getFileInTestConfigDirectory(jsonName))
             task.presubmit.set(isPresubmitBuild())
             // Disable work tests on < API 18: b/178127496
             if (path.startsWith(":work:")) {
@@ -121,13 +121,13 @@
         variant: Variant,
         appProjectPath: String,
         instrumentationProjectPath: String?
-    ): File {
+    ): Provider<RegularFile> {
         var filename = appProjectPath.asFilenamePrefix()
         if (instrumentationProjectPath != null) {
             filename += "_for_${instrumentationProjectPath.asFilenamePrefix()}"
         }
         filename += "-${variant.name}.apk"
-        return File(getTestConfigDirectory(), filename)
+        return getFileInTestConfigDirectory(filename)
     }
 
     // For application modules, the instrumentation apk is generated in the module itself
@@ -283,10 +283,57 @@
 ) {
     val mediaPrefix = getMediaConfigTaskPrefix(isMedia2)
     val mediaTask = getOrCreateMediaTestConfigTask(this, isMedia2)
+
+    fun getJsonName(clientToT: Boolean, serviceToT: Boolean, clientTests: Boolean): String {
+        return "_${mediaPrefix}Client${
+            if (clientToT) "ToT" else "Previous"
+        }Service${
+            if (serviceToT) "ToT" else "Previous"
+        }${
+            if (clientTests) "Client" else "Service"
+        }Tests$variantName.json"
+    }
+
+    fun ModuleInfoGenerator.addTestModule(clientToT: Boolean, serviceToT: Boolean) {
+        // We don't test the combination of previous versions of service and client as that is not
+        // useful data. We always want at least one tip of tree project.
+        if (!clientToT && !serviceToT) return
+        testModules.add(
+            TestModule(
+                name = getJsonName(
+                    clientToT = clientToT,
+                    serviceToT = serviceToT,
+                    clientTests = true
+                ),
+                path = listOf(projectDir.toRelativeString(getSupportRootFolder()))
+            )
+        )
+        testModules.add(
+            TestModule(
+                name = getJsonName(
+                    clientToT = clientToT,
+                    serviceToT = serviceToT,
+                    clientTests = false
+                ),
+                path = listOf(projectDir.toRelativeString(getSupportRootFolder()))
+            )
+        )
+    }
+    val isClient = this.name.contains("client")
+    val isPrevious = this.name.contains("previous")
+
+    rootProject.tasks.named<ModuleInfoGenerator>("createModuleInfo").configure {
+        if (isClient) {
+            it.addTestModule(clientToT = !isPrevious, serviceToT = false)
+            it.addTestModule(clientToT = !isPrevious, serviceToT = true)
+        } else {
+            it.addTestModule(clientToT = true, serviceToT = !isPrevious)
+            it.addTestModule(clientToT = false, serviceToT = !isPrevious)
+        }
+    }
     mediaTask.configure {
-        it as GenerateMediaTestConfigurationTask
-        if (this.name.contains("client")) {
-            if (this.name.contains("previous")) {
+        if (isClient) {
+            if (isPrevious) {
                 it.clientPreviousFolder.set(artifacts.get(SingleArtifact.APK))
                 it.clientPreviousLoader.set(artifacts.getBuiltArtifactsLoader())
             } else {
@@ -294,7 +341,7 @@
                 it.clientToTLoader.set(artifacts.getBuiltArtifactsLoader())
             }
         } else {
-            if (this.name.contains("previous")) {
+            if (isPrevious) {
                 it.servicePreviousFolder.set(artifacts.get(SingleArtifact.APK))
                 it.servicePreviousLoader.set(artifacts.getBuiltArtifactsLoader())
             } else {
@@ -302,53 +349,45 @@
                 it.serviceToTLoader.set(artifacts.getBuiltArtifactsLoader())
             }
         }
-        it.jsonClientPreviousServiceToTClientTests.fileValue(
-            File(
-                this.getTestConfigDirectory(),
-                "_${mediaPrefix}ClientPreviousServiceToTClientTests$variantName.json"
+        it.jsonClientPreviousServiceToTClientTests.set(
+            getFileInTestConfigDirectory(
+                getJsonName(clientToT = false, serviceToT = true, clientTests = true)
             )
         )
-        it.jsonClientPreviousServiceToTServiceTests.fileValue(
-            File(
-                this.getTestConfigDirectory(),
-                "_${mediaPrefix}ClientPreviousServiceToTServiceTests$variantName.json"
+        it.jsonClientPreviousServiceToTServiceTests.set(
+            getFileInTestConfigDirectory(
+                getJsonName(clientToT = false, serviceToT = true, clientTests = false)
             )
         )
-        it.jsonClientToTServicePreviousClientTests.fileValue(
-            File(
-                this.getTestConfigDirectory(),
-                "_${mediaPrefix}ClientToTServicePreviousClientTests$variantName.json"
+        it.jsonClientToTServicePreviousClientTests.set(
+            getFileInTestConfigDirectory(
+                getJsonName(clientToT = true, serviceToT = false, clientTests = true)
             )
         )
-        it.jsonClientToTServicePreviousServiceTests.fileValue(
-            File(
-                this.getTestConfigDirectory(),
-                "_${mediaPrefix}ClientToTServicePreviousServiceTests$variantName.json"
+        it.jsonClientToTServicePreviousServiceTests.set(
+            getFileInTestConfigDirectory(
+                getJsonName(clientToT = true, serviceToT = false, clientTests = false)
             )
         )
-        it.jsonClientToTServiceToTClientTests.fileValue(
-            File(
-                this.getTestConfigDirectory(),
-                "_${mediaPrefix}ClientToTServiceToTClientTests$variantName.json"
+        it.jsonClientToTServiceToTClientTests.set(
+            getFileInTestConfigDirectory(
+                getJsonName(clientToT = true, serviceToT = true, clientTests = true)
             )
         )
-        it.jsonClientToTServiceToTServiceTests.fileValue(
-            File(
-                this.getTestConfigDirectory(),
-                "_${mediaPrefix}ClientToTServiceToTServiceTests$variantName.json"
+        it.jsonClientToTServiceToTServiceTests.set(
+            getFileInTestConfigDirectory(
+                getJsonName(clientToT = true, serviceToT = true, clientTests = false)
             )
         )
-        it.totClientApk.fileValue(
-            File(getTestConfigDirectory(), "${mediaPrefix}ClientToT$variantName.apk")
+        it.totClientApk.set(getFileInTestConfigDirectory("${mediaPrefix}ClientToT$variantName.apk"))
+        it.previousClientApk.set(
+            getFileInTestConfigDirectory("${mediaPrefix}ClientPrevious$variantName.apk")
         )
-        it.previousClientApk.fileValue(
-            File(getTestConfigDirectory(), "${mediaPrefix}ClientPrevious$variantName.apk")
+        it.totServiceApk.set(
+            getFileInTestConfigDirectory("${mediaPrefix}ServiceToT$variantName.apk")
         )
-        it.totServiceApk.fileValue(
-            File(getTestConfigDirectory(), "${mediaPrefix}ServiceToT$variantName.apk")
-        )
-        it.previousServiceApk.fileValue(
-            File(getTestConfigDirectory(), "${mediaPrefix}ServicePrevious$variantName.apk")
+        it.previousServiceApk.set(
+            getFileInTestConfigDirectory("${mediaPrefix}ServicePrevious$variantName.apk")
         )
         it.minSdk.set(minSdk)
         it.testRunner.set(testRunner)
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/uptodatedness/EnableCaching.kt b/buildSrc/private/src/main/kotlin/androidx/build/uptodatedness/EnableCaching.kt
index 94855ce..d6f1557 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/uptodatedness/EnableCaching.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/uptodatedness/EnableCaching.kt
@@ -16,8 +16,9 @@
 
 package androidx.build.uptodatedness
 
-import java.io.File
 import org.gradle.api.Task
+import org.gradle.api.file.RegularFile
+import org.gradle.api.provider.Provider
 
 // Tells Gradle to skip running this task, even if this task declares no output files
 fun Task.cacheEvenIfNoOutputs() {
@@ -27,6 +28,6 @@
 // Returns a dummy/unused output path that we can pass to Gradle to prevent Gradle from thinking
 // that we forgot to declare outputs of this task, and instead to skip this task if its inputs
 // are unchanged
-fun Task.getDummyOutput(): File {
-    return File(this.project.buildDir, "dummyOutput/" + this.name.replace(":", "-"))
+private fun Task.getDummyOutput(): Provider<RegularFile> {
+    return project.layout.buildDirectory.file("dummyOutput/" + this.name.replace(":", "-"))
 }
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/BuildServerConfiguration.kt b/buildSrc/public/src/main/kotlin/androidx/build/BuildServerConfiguration.kt
index 89c77da..fece4cf 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/BuildServerConfiguration.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/BuildServerConfiguration.kt
@@ -19,6 +19,9 @@
 import androidx.build.gradle.isRoot
 import java.io.File
 import org.gradle.api.Project
+import org.gradle.api.file.Directory
+import org.gradle.api.file.RegularFile
+import org.gradle.api.provider.Provider
 
 /**
  * @return build id string for current build
@@ -74,7 +77,12 @@
  * Directory for android test configuration files that get consumed by Tradefed in CI. These configs
  * cause all the tests to be run, except in cases where buildSrc changes.
  */
-fun Project.getTestConfigDirectory(): File = File(rootProject.buildDir, "test-xml-configs")
+fun Project.getTestConfigDirectory(): Provider<Directory> =
+    rootProject.layout.buildDirectory.dir("test-xml-configs")
+
+/** A file within [getTestConfigDirectory] */
+fun Project.getFileInTestConfigDirectory(name: String): Provider<RegularFile> =
+    getTestConfigDirectory().map { it.file(name) }
 
 /** Directory to put release note files for generate release note tasks. */
 fun Project.getReleaseNotesDirectory(): File = File(getDistributionDirectory(), "release-notes")
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/BundleInsideHelper.kt b/buildSrc/public/src/main/kotlin/androidx/build/BundleInsideHelper.kt
index a17221e..e928e54 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/BundleInsideHelper.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/BundleInsideHelper.kt
@@ -17,7 +17,6 @@
 package androidx.build
 
 import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
-import java.io.File
 import org.gradle.api.Project
 import org.gradle.api.Task
 import org.gradle.api.artifacts.Configuration
@@ -221,7 +220,7 @@
                 }
                 archiveBaseName.set("repackaged")
                 archiveVersion.set("")
-                destinationDirectory.set(File(buildDir, "repackaged"))
+                destinationDirectory.set(layout.buildDirectory.dir("repackaged"))
             }
         }
     }
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/SdkResourceGenerator.kt b/buildSrc/public/src/main/kotlin/androidx/build/SdkResourceGenerator.kt
index bfb5645..3578baf 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/SdkResourceGenerator.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/SdkResourceGenerator.kt
@@ -17,6 +17,7 @@
 package androidx.build
 
 import androidx.build.dependencies.AGP_LATEST
+import androidx.build.dependencies.KOTLIN_GRADLE_PLUGIN_VERSION
 import androidx.build.dependencies.KSP_VERSION
 import com.google.common.annotations.VisibleForTesting
 import java.io.File
@@ -63,7 +64,7 @@
 
     @get:Input abstract val kotlinStdlib: Property<String>
 
-    @get:Input abstract val kotlinVersion: Property<String>
+    @get:Input abstract val kgpVersion: Property<String>
 
     @get:Input val kspVersion: String = KSP_VERSION
 
@@ -99,7 +100,7 @@
             writer.write("compileSdkVersion=${compileSdkVersion.get()}\n")
             writer.write("buildToolsVersion=${buildToolsVersion.get()}\n")
             writer.write("minSdkVersion=${minSdkVersion.get()}\n")
-            writer.write("kotlinVersion=${kotlinVersion.get()}\n")
+            writer.write("kgpVersion=${kgpVersion.get()}\n")
             writer.write("kspVersion=$kspVersion\n")
             writer.write("buildSrcOutRelativePath=$buildSrcOutRelativePath\n")
         }
@@ -118,7 +119,7 @@
 
         @VisibleForTesting
         fun registerSdkResourceGeneratorTask(project: Project): TaskProvider<SdkResourceGenerator> {
-            val generatedDirectory = File(project.buildDir, "generated/resources")
+            val generatedDirectory = project.layout.buildDirectory.dir("generated/resources")
             return project.tasks.register(TASK_NAME, SdkResourceGenerator::class.java) {
                 it.tipOfTreeMavenRepoRelativePath =
                     project.getRepositoryDirectory().toRelativeString(project.projectDir)
@@ -134,7 +135,7 @@
                         "org.jetbrains.kotlin:kotlin-stdlib:$version"
                     }
                 )
-                it.kotlinVersion.set(project.androidXConfiguration.kotlinBomVersion)
+                it.kgpVersion.set(KOTLIN_GRADLE_PLUGIN_VERSION)
                 it.buildSrcOutRelativePath =
                     (project.properties["buildSrcOut"] as File).toRelativeString(project.projectDir)
                 // Copy repositories used for the library project so that it can replicate the same
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/dependencies/Dependencies.kt b/buildSrc/public/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
index 9e5e84a..cb94008 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
@@ -28,6 +28,10 @@
 val KOTLIN_NATIVE_VERSION
     get() = kotlinNativeVersion
 
+lateinit var kotlinGradlePluginVersion: String
+val KOTLIN_GRADLE_PLUGIN_VERSION
+    get() = kotlinGradlePluginVersion
+
 lateinit var agpVersion: String
 val AGP_LATEST
     get() = "com.android.tools.build:gradle:$agpVersion"
diff --git a/buildSrc/repos.gradle b/buildSrc/repos.gradle
index 4ae93fa..9b5077d 100644
--- a/buildSrc/repos.gradle
+++ b/buildSrc/repos.gradle
@@ -65,13 +65,6 @@
                url("https://maven.pkg.jetbrains.space/public/p/compose/dev")
         }
         handler.mavenLocal()
-        // TODO(b/280646217): Remove after official release to gmaven.
-        handler.maven {
-            url("https://storage.googleapis.com/r8-releases/raw")
-            content {
-                includeModule("com.android.tools", "r8")
-            }
-        }
     }
     // Ordering appears to be important: b/229733266
     def androidPluginRepoOverride = System.getenv("GRADLE_PLUGIN_REPO")
diff --git a/buildSrc/shared-dependencies.gradle b/buildSrc/shared-dependencies.gradle
index 56c89bd..96189f6 100644
--- a/buildSrc/shared-dependencies.gradle
+++ b/buildSrc/shared-dependencies.gradle
@@ -37,7 +37,7 @@
     implementation(libs.apacheCommonIo) // used in CheckApiEquivalenceTask.kt
     implementation(libs.dexMemberList) // used in ReportLibraryMetricsTask.kt
 
-    implementation(libs.protobufGradlePluginz) // needed to compile inspection plugin
+    implementation(libs.protobufGradlePlugin) // needed to compile inspection plugin
     implementation(libs.kotlinPoet) // needed to compile material-icon-generator
     implementation(libs.xmlpull) // needed to compile material-icon-generator
 
diff --git a/busytown/androidx_incremental.sh b/busytown/androidx_incremental.sh
index 08e4653..481f9d4 100755
--- a/busytown/androidx_incremental.sh
+++ b/busytown/androidx_incremental.sh
@@ -46,7 +46,13 @@
 # reproducible
 DIAGNOSE_ARG=""
 if [ "$PRESUBMIT" == "false" ]; then
-  DIAGNOSE_ARG="--diagnose"
+  if [ "$BUILD_NUMBER" == "" ]; then
+    # This is a local build so we can diagnose without a timeout. The user can cancel it when they're satisfied.
+    DIAGNOSE_ARG="--diagnose"
+  else
+    # This is running on the build server so we should not spend long trying to diagnose it
+    DIAGNOSE_ARG="--diagnose --diagnose-timeout 600"
+  fi
 fi
 
 EXIT_VALUE=0
@@ -58,9 +64,7 @@
 else
     # Run Gradle
     if impl/build.sh $DIAGNOSE_ARG buildOnServer checkExternalLicenses listTaskOutputs \
-        --profile \
-        -Pkotlin.incremental=false \
-        "$@"; then
+        --profile "$@"; then
     echo build succeeded
     EXIT_VALUE=0
     else
diff --git a/busytown/androidx_multiplatform_linux.sh b/busytown/androidx_multiplatform_linux.sh
index 34e9b84..28589241 100755
--- a/busytown/androidx_multiplatform_linux.sh
+++ b/busytown/androidx_multiplatform_linux.sh
@@ -14,4 +14,6 @@
 export USE_ANDROIDX_REMOTE_BUILD_CACHE=false
 
 # run build in a sandbox
-../development/sandbox/run-without-network.sh impl/build.sh buildOnServer allTests --no-configuration-cache --no-daemon -Pandroidx.displayTestOutput=false
+../development/sandbox/run-without-network.sh impl/build.sh buildOnServer \
+    allTests \
+    -Pandroidx.displayTestOutput=false
diff --git a/busytown/impl/build.sh b/busytown/impl/build.sh
index 0683335..3b10921 100755
--- a/busytown/impl/build.sh
+++ b/busytown/impl/build.sh
@@ -32,6 +32,13 @@
 else
   DIAGNOSE=false
 fi
+if [ "$1" == "--diagnose-timeout" ]; then
+  shift
+  DIAGNOSE_TIMEOUT_ARG="--timeout $1"
+  shift
+else
+  DIAGNOSE_TIMEOUT_ARG=""
+fi
 
 # record the build start time
 BUILD_START_MARKER="$OUT_DIR/build.sh.start"
@@ -105,6 +112,7 @@
   echo build passed
 else
   if grep "has several compatible actual declarations in modules" "$DIST_DIR/logs/gradle.log" >/dev/null 2>/dev/null; then
+    run ./gradlew --stop || true
     # try to copy the OUT_DIR into DIST where we can find it
     cd "$OUT_DIR"
     echo "zipping out into $DIST_DIR/out.zip"
@@ -112,13 +120,13 @@
     cd -
   else
     if [ "$DIAGNOSE" == "true" ]; then
-     # see if diagnose-build-failure.sh can identify the root cauase
+      # see if diagnose-build-failure.sh can identify the root cauase
       echo "running diagnose-build-failure.sh, see build.log" >&2
       # Specify a short timeout in case we're running on a remote server, so we don't take too long.
       # We probably won't have enough time to fully diagnose the problem given this timeout, but
       # we might be able to determine whether this problem is reproducible enough for a developer to
       # more easily investigate further
-      ./development/diagnose-build-failure/diagnose-build-failure.sh --timeout 600 "--ci $*"
+      ./development/diagnose-build-failure/diagnose-build-failure.sh $DIAGNOSE_TIMEOUT_ARG "--ci $*"
     fi
   fi
   BUILD_STATUS=1 # failure
diff --git a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/TestUseCaseCamera.kt b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/TestUseCaseCamera.kt
index 4acf60b..3a304c6 100644
--- a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/TestUseCaseCamera.kt
+++ b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/TestUseCaseCamera.kt
@@ -26,6 +26,7 @@
 import androidx.camera.camera2.pipe.CameraGraph
 import androidx.camera.camera2.pipe.CameraId
 import androidx.camera.camera2.pipe.CameraPipe
+import androidx.camera.camera2.pipe.CameraStream
 import androidx.camera.camera2.pipe.Request
 import androidx.camera.camera2.pipe.RequestTemplate
 import androidx.camera.camera2.pipe.integration.adapter.CameraStateAdapter
@@ -37,6 +38,8 @@
 import androidx.camera.camera2.pipe.integration.compat.workaround.OutputSizesCorrector
 import androidx.camera.camera2.pipe.integration.config.CameraConfig
 import androidx.camera.camera2.pipe.integration.config.UseCaseCameraConfig
+import androidx.camera.camera2.pipe.integration.config.UseCaseGraphConfig
+import androidx.camera.camera2.pipe.integration.impl.Camera2ImplConfig
 import androidx.camera.camera2.pipe.integration.impl.CameraCallbackMap
 import androidx.camera.camera2.pipe.integration.impl.CameraInteropStateCallbackRepository
 import androidx.camera.camera2.pipe.integration.impl.CameraPipeCameraProperties
@@ -46,11 +49,14 @@
 import androidx.camera.camera2.pipe.integration.impl.UseCaseCameraRequestControl
 import androidx.camera.camera2.pipe.integration.impl.UseCaseCameraRequestControlImpl
 import androidx.camera.camera2.pipe.integration.impl.UseCaseCameraState
+import androidx.camera.camera2.pipe.integration.impl.UseCaseManager.Companion.createCameraGraphConfig
 import androidx.camera.camera2.pipe.integration.impl.UseCaseSurfaceManager
 import androidx.camera.camera2.pipe.integration.impl.UseCaseThreads
 import androidx.camera.camera2.pipe.integration.impl.toMap
 import androidx.camera.core.UseCase
 import androidx.camera.core.impl.Config
+import androidx.camera.core.impl.DeferrableSurface
+import androidx.camera.core.impl.SessionConfig
 import kotlinx.coroutines.Deferred
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.launch
@@ -65,7 +71,6 @@
     private val useCases: List<UseCase>,
     private val cameraConfig: CameraConfig = CameraConfig(CameraId(cameraId)),
     val cameraPipe: CameraPipe = CameraPipe(CameraPipe.Config(context)),
-    val callbackMap: CameraCallbackMap = CameraCallbackMap(),
     val useCaseSurfaceManager: UseCaseSurfaceManager = UseCaseSurfaceManager(
         threads,
         cameraPipe,
@@ -83,24 +88,32 @@
             OutputSizesCorrector(cameraMetadata, streamConfigurationMap)
         )
     )
-    val useCaseCameraGraphConfig =
-        UseCaseCameraConfig(
+    val sessionConfigAdapter = SessionConfigAdapter(useCases)
+    val useCaseCameraGraphConfig: UseCaseGraphConfig
+
+    init {
+        val streamConfigMap = mutableMapOf<CameraStream.Config, DeferrableSurface>()
+        val callbackMap = CameraCallbackMap()
+        val requestListener = ComboRequestListener()
+        val cameraGraphConfig = createCameraGraphConfig(sessionConfigAdapter, streamConfigMap,
+            callbackMap, requestListener, cameraConfig, cameraQuirks, null)
+        val cameraGraph = cameraPipe.create(cameraGraphConfig)
+
+        useCaseCameraGraphConfig = UseCaseCameraConfig(
             useCases,
+            sessionConfigAdapter,
             CameraStateAdapter(),
-            cameraQuirks,
-            CameraGraph.Flags()
+            cameraGraph,
+            streamConfigMap
         ).provideUseCaseGraphConfig(
-            callbackMap = callbackMap,
-            cameraConfig = cameraConfig,
-            cameraPipe = cameraPipe,
-            requestListener = ComboRequestListener(),
             useCaseSurfaceManager = useCaseSurfaceManager,
             cameraInteropStateCallbackRepository = CameraInteropStateCallbackRepository()
         )
+    }
 
     override val requestControl: UseCaseCameraRequestControl = UseCaseCameraRequestControlImpl(
         configAdapter = CaptureConfigAdapter(
-            CameraPipeCameraProperties(cameraPipe, cameraConfig),
+            CameraPipeCameraProperties(cameraConfig, cameraMetadata),
             useCaseCameraGraphConfig,
             threads
         ),
@@ -139,6 +152,7 @@
     }
 
     override var runningUseCases = useCases.toSet()
+
     override fun <T> setParameterAsync(
         key: CaptureRequest.Key<T>,
         value: T,
@@ -160,4 +174,10 @@
             useCaseSurfaceManager.stopAsync().await()
         }
     }
+
+    companion object {
+        fun SessionConfig.toCamera2ImplConfig(): Camera2ImplConfig {
+            return Camera2ImplConfig(implementationOptions)
+        }
+    }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/VerifyResultListener.kt b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/VerifyResultListener.kt
index 46a90fc..87327a7 100644
--- a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/VerifyResultListener.kt
+++ b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/VerifyResultListener.kt
@@ -16,11 +16,11 @@
 
 package androidx.camera.camera2.pipe.testing
 
-import android.hardware.camera2.CaptureFailure
 import androidx.annotation.RequiresApi
 import androidx.camera.camera2.pipe.FrameInfo
 import androidx.camera.camera2.pipe.FrameNumber
 import androidx.camera.camera2.pipe.Request
+import androidx.camera.camera2.pipe.RequestFailure
 import androidx.camera.camera2.pipe.RequestMetadata
 import java.util.concurrent.TimeUnit
 import java.util.concurrent.TimeoutException
@@ -36,8 +36,10 @@
     private val waitingCount = atomic(capturesCount)
     private val failureException =
         TimeoutException("Test doesn't complete after waiting for $capturesCount frames.")
+
     @Volatile
     private var startReceiving = false
+
     @Volatile
     private var _verifyBlock: (
         captureRequest: RequestMetadata,
@@ -79,14 +81,10 @@
         }
     }
 
-    @Deprecated(
-        message = "Migrating to using RequestFailureWrapper instead of CaptureFailure",
-        level = DeprecationLevel.WARNING
-    )
     override fun onFailed(
         requestMetadata: RequestMetadata,
         frameNumber: FrameNumber,
-        captureFailure: CaptureFailure
+        requestFailure: RequestFailure
     ) {
         if (!startReceiving) {
             return
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapter.kt
new file mode 100644
index 0000000..c8276d6
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapter.kt
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.integration.adapter
+
+import androidx.annotation.RequiresApi
+import androidx.annotation.VisibleForTesting
+import androidx.camera.camera2.pipe.CameraDevices
+import androidx.camera.camera2.pipe.CameraId
+import androidx.camera.camera2.pipe.integration.internal.CameraGraphCreator
+import androidx.camera.camera2.pipe.integration.interop.Camera2CameraInfo
+import androidx.camera.camera2.pipe.integration.interop.ExperimentalCamera2Interop
+import androidx.camera.core.CameraInfo
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.concurrent.CameraCoordinator
+import androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_UNSPECIFIED
+import androidx.camera.core.concurrent.CameraCoordinator.CameraOperatingMode
+import androidx.camera.core.impl.CameraInternal
+
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+class CameraCoordinatorAdapter(
+    cameraDevices: CameraDevices,
+    private val cameraGraphCreator: CameraGraphCreator
+) : CameraCoordinator {
+    @VisibleForTesting val cameraInternalMap = mutableMapOf<CameraId, CameraInternalAdapter>()
+    @VisibleForTesting var concurrentCameraIdsSet = mutableSetOf<Set<CameraId>>()
+    @VisibleForTesting var concurrentCameraIdMap = mutableMapOf<String, MutableList<String>>()
+    @VisibleForTesting var activeConcurrentCameraInfosList = mutableListOf<CameraInfo>()
+    @VisibleForTesting var concurrentMode: Int = CAMERA_OPERATING_MODE_UNSPECIFIED
+    @VisibleForTesting var concurrentModeOn = false
+
+    init {
+        concurrentCameraIdsSet = cameraDevices.awaitConcurrentCameraIds()!!.toMutableSet()
+        for (cameraIdSet in concurrentCameraIdsSet) {
+            val cameraIdsList = cameraIdSet.toList()
+            if (cameraIdsList.size >= 2) {
+                val cameraId1: String = cameraIdsList[0].value
+                val cameraId2: String = cameraIdsList[1].value
+                if (!concurrentCameraIdMap.containsKey(cameraId1)) {
+                    concurrentCameraIdMap[cameraId1] = mutableListOf()
+                }
+                if (!concurrentCameraIdMap.containsKey(cameraId2)) {
+                    concurrentCameraIdMap[cameraId2] = mutableListOf()
+                }
+                concurrentCameraIdMap[cameraId1]?.add(cameraId2)
+                concurrentCameraIdMap[cameraId2]?.add(cameraId1)
+            }
+        }
+    }
+
+    fun registerCamera(cameraId: String, cameraInternal: CameraInternal) {
+        cameraInternalMap[CameraId.fromCamera2Id(cameraId)] =
+            cameraInternal as CameraInternalAdapter
+    }
+
+    @OptIn(ExperimentalCamera2Interop::class)
+    override fun getConcurrentCameraSelectors(): MutableList<MutableList<CameraSelector>> {
+        return concurrentCameraIdsSet.map { concurrentCameraIds ->
+            concurrentCameraIds.map { cameraId ->
+                CameraSelector.Builder().addCameraFilter { cameraInfos ->
+                    cameraInfos.filter {
+                        cameraId.value == Camera2CameraInfo.from(it).getCameraId()
+                    }
+                }.build()
+            }.toMutableList()
+        }.toMutableList()
+    }
+
+    override fun getActiveConcurrentCameraInfos(): MutableList<CameraInfo> {
+        return activeConcurrentCameraInfosList
+    }
+
+    override fun setActiveConcurrentCameraInfos(cameraInfos: MutableList<CameraInfo>) {
+        activeConcurrentCameraInfosList = cameraInfos
+        for (cameraInternalAdapter in cameraInternalMap.values) {
+            cameraInternalAdapter.resumeRefresh()
+        }
+    }
+
+    @OptIn(ExperimentalCamera2Interop::class)
+    override fun getPairedConcurrentCameraId(cameraId: String): String? {
+        if (!concurrentCameraIdMap.containsKey(cameraId)) {
+            return null
+        }
+
+        for (pairedCameraId in concurrentCameraIdMap[cameraId]!!) {
+            for (cameraInfo in activeConcurrentCameraInfos) {
+                if (pairedCameraId == Camera2CameraInfo.from(cameraInfo).getCameraId()) {
+                    return pairedCameraId
+                }
+            }
+        }
+        return null
+    }
+
+    @CameraOperatingMode
+    override fun getCameraOperatingMode(): Int {
+        return concurrentMode
+    }
+
+    override fun setCameraOperatingMode(@CameraOperatingMode cameraOperatingMode: Int) {
+        concurrentMode = cameraOperatingMode
+        concurrentModeOn = cameraOperatingMode == CameraCoordinator.CAMERA_OPERATING_MODE_CONCURRENT
+        cameraGraphCreator.setConcurrentModeOn(concurrentModeOn)
+        for (cameraInternalAdapter in cameraInternalMap.values) {
+            if (concurrentModeOn) {
+                cameraInternalAdapter.pauseRefresh()
+            } else {
+                cameraInternalAdapter.resumeRefresh()
+            }
+        }
+    }
+
+    override fun addListener(listener: CameraCoordinator.ConcurrentCameraModeListener) {
+    }
+
+    override fun removeListener(listener: CameraCoordinator.ConcurrentCameraModeListener) {
+    }
+
+    override fun shutdown() {
+        cameraInternalMap.clear()
+        concurrentCameraIdsSet.clear()
+        concurrentCameraIdMap.clear()
+        activeConcurrentCameraInfosList.clear()
+        concurrentMode = CAMERA_OPERATING_MODE_UNSPECIFIED
+        concurrentModeOn = false
+    }
+}
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraFactoryAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraFactoryAdapter.kt
index eb8dcb1..1c3dc2c 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraFactoryAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraFactoryAdapter.kt
@@ -32,10 +32,8 @@
 import androidx.camera.camera2.pipe.integration.impl.CameraInteropStateCallbackRepository
 import androidx.camera.camera2.pipe.integration.internal.CameraCompatibilityFilter
 import androidx.camera.camera2.pipe.integration.internal.CameraSelectionOptimizer
-import androidx.camera.core.CameraInfo
 import androidx.camera.core.CameraSelector
 import androidx.camera.core.concurrent.CameraCoordinator
-import androidx.camera.core.concurrent.CameraCoordinator.ConcurrentCameraModeListener
 import androidx.camera.core.impl.CameraFactory
 import androidx.camera.core.impl.CameraInternal
 import androidx.camera.core.impl.CameraThreadConfig
@@ -73,6 +71,8 @@
     }
     private var mAvailableCamerasSelector: CameraSelector? = availableCamerasSelector
     private var mAvailableCameraIds: List<String>
+    private val cameraCoordinator: CameraCoordinatorAdapter = CameraCoordinatorAdapter(
+        appComponent.getCameraDevices(), appComponent.getCameraGraphCreator())
 
     init {
         debug { "Created CameraFactoryAdapter" }
@@ -91,47 +91,21 @@
      * The [getCamera] method is responsible for providing CameraInternal object based on cameraID.
      * Use cameraId from set of cameraIds provided by [getAvailableCameraIds] method.
      */
-    override fun getCamera(cameraId: String): CameraInternal =
-        appComponent.cameraBuilder()
+    override fun getCamera(cameraId: String): CameraInternal {
+        val cameraInternal = appComponent.cameraBuilder()
             .config(CameraConfig(CameraId(cameraId)))
             .build()
             .getCameraInternal()
+        cameraCoordinator.registerCamera(cameraId, cameraInternal)
+        return cameraInternal
+    }
 
     override fun getAvailableCameraIds(): Set<String> =
         // Use a LinkedHashSet to preserve order
         LinkedHashSet(mAvailableCameraIds)
 
     override fun getCameraCoordinator(): CameraCoordinator {
-        // TODO(b/262772650): camera-pipe support for concurrent camera.
-        return object : CameraCoordinator {
-            override fun getConcurrentCameraSelectors(): MutableList<MutableList<CameraSelector>> {
-                return mutableListOf()
-            }
-
-            override fun getActiveConcurrentCameraInfos(): MutableList<CameraInfo> {
-                return mutableListOf()
-            }
-
-            override fun setActiveConcurrentCameraInfos(cameraInfos: MutableList<CameraInfo>) {
-            }
-
-            override fun getPairedConcurrentCameraId(cameraId: String): String? {
-                return null
-            }
-
-            override fun getCameraOperatingMode(): Int {
-                return CameraCoordinator.CAMERA_OPERATING_MODE_UNSPECIFIED
-            }
-
-            override fun setCameraOperatingMode(cameraOperatingMode: Int) {
-            }
-
-            override fun addListener(listener: ConcurrentCameraModeListener) {
-            }
-
-            override fun removeListener(listener: ConcurrentCameraModeListener) {
-            }
-        }
+        return cameraCoordinator
     }
 
     override fun getCameraManager(): Any = appComponent
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInternalAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInternalAdapter.kt
index 12acfb6..5b39179 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInternalAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInternalAdapter.kt
@@ -60,6 +60,13 @@
         // TODO: Consider preloading the list of camera ids and metadata.
     }
 
+    fun pauseRefresh() = threads.scope.launch(threads.backgroundDispatcher) {
+        useCaseManager.pauseRefresh()
+    }
+    fun resumeRefresh() = threads.scope.launch(threads.backgroundDispatcher) {
+        useCaseManager.resumeRefresh()
+    }
+
     // Load / unload methods
     override fun open() {
         debug { "$this#open" }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/CameraAppConfig.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/CameraAppConfig.kt
index 2ba8e67..31d67ce 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/CameraAppConfig.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/CameraAppConfig.kt
@@ -23,6 +23,7 @@
 import androidx.camera.camera2.pipe.CameraDevices
 import androidx.camera.camera2.pipe.CameraPipe
 import androidx.camera.camera2.pipe.integration.impl.CameraInteropStateCallbackRepository
+import androidx.camera.camera2.pipe.integration.internal.CameraGraphCreator
 import androidx.camera.core.impl.CameraFactory
 import androidx.camera.core.impl.CameraThreadConfig
 import dagger.Component
@@ -78,6 +79,8 @@
     fun getCameraPipe(): CameraPipe
     fun getCameraDevices(): CameraDevices
 
+    fun getCameraGraphCreator(): CameraGraphCreator
+
     @Component.Builder
     interface Builder {
         fun config(config: CameraAppConfig): Builder
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/UseCaseCameraConfig.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/UseCaseCameraConfig.kt
index 566b875..b60e5a3c 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/UseCaseCameraConfig.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/UseCaseCameraConfig.kt
@@ -18,30 +18,17 @@
 
 package androidx.camera.camera2.pipe.integration.config
 
-import android.media.MediaCodec
 import androidx.annotation.RequiresApi
 import androidx.camera.camera2.pipe.CameraGraph
-import androidx.camera.camera2.pipe.CameraId
-import androidx.camera.camera2.pipe.CameraPipe
 import androidx.camera.camera2.pipe.CameraStream
-import androidx.camera.camera2.pipe.OutputStream
-import androidx.camera.camera2.pipe.StreamFormat
 import androidx.camera.camera2.pipe.StreamId
 import androidx.camera.camera2.pipe.core.Log
 import androidx.camera.camera2.pipe.integration.adapter.CameraStateAdapter
 import androidx.camera.camera2.pipe.integration.adapter.SessionConfigAdapter
-import androidx.camera.camera2.pipe.integration.adapter.SessionConfigAdapter.Companion.toCamera2ImplConfig
-import androidx.camera.camera2.pipe.integration.compat.quirk.CameraQuirks
-import androidx.camera.camera2.pipe.integration.compat.quirk.CloseCameraDeviceOnCameraGraphCloseQuirk
-import androidx.camera.camera2.pipe.integration.compat.quirk.CloseCaptureSessionOnDisconnectQuirk
-import androidx.camera.camera2.pipe.integration.compat.quirk.CloseCaptureSessionOnVideoQuirk
-import androidx.camera.camera2.pipe.integration.compat.quirk.DeviceQuirks
 import androidx.camera.camera2.pipe.integration.compat.workaround.CapturePipelineTorchCorrection
-import androidx.camera.camera2.pipe.integration.impl.CameraCallbackMap
 import androidx.camera.camera2.pipe.integration.impl.CameraInteropStateCallbackRepository
 import androidx.camera.camera2.pipe.integration.impl.CapturePipeline
 import androidx.camera.camera2.pipe.integration.impl.CapturePipelineImpl
-import androidx.camera.camera2.pipe.integration.impl.ComboRequestListener
 import androidx.camera.camera2.pipe.integration.impl.UseCaseCamera
 import androidx.camera.camera2.pipe.integration.impl.UseCaseCameraImpl
 import androidx.camera.camera2.pipe.integration.impl.UseCaseCameraRequestControlImpl
@@ -87,9 +74,10 @@
 @Module
 class UseCaseCameraConfig(
     private val useCases: List<UseCase>,
+    private val sessionConfigAdapter: SessionConfigAdapter,
     private val cameraStateAdapter: CameraStateAdapter,
-    private val cameraQuirks: CameraQuirks,
-    private val cameraGraphFlags: CameraGraph.Flags,
+    private val cameraGraph: CameraGraph,
+    private val streamConfigMap: Map<CameraStream.Config, DeferrableSurface>
 ) {
     @UseCaseCameraScope
     @Provides
@@ -104,95 +92,26 @@
     @UseCaseCameraScope
     @Provides
     fun provideUseCaseGraphConfig(
-        callbackMap: CameraCallbackMap,
-        cameraConfig: CameraConfig,
-        cameraPipe: CameraPipe,
-        requestListener: ComboRequestListener,
         useCaseSurfaceManager: UseCaseSurfaceManager,
         cameraInteropStateCallbackRepository: CameraInteropStateCallbackRepository
     ): UseCaseGraphConfig {
-        val streamConfigMap = mutableMapOf<CameraStream.Config, DeferrableSurface>()
-
-        var containsVideo = false
-        // TODO: This may need to combine outputs that are (or will) share the same output
-        //  imageReader or surface.
-        val sessionConfigAdapter = SessionConfigAdapter(useCases)
         sessionConfigAdapter.getValidSessionConfigOrNull()?.let { sessionConfig ->
             cameraInteropStateCallbackRepository.updateCallbacks(sessionConfig)
-            sessionConfig.surfaces.forEach { deferrableSurface ->
-                val outputConfig = CameraStream.Config.create(
-                    streamUseCase = getStreamUseCase(
-                        deferrableSurface,
-                        sessionConfigAdapter.surfaceToStreamUseCaseMap
-                    ),
-                    streamUseHint = getStreamUseHint(
-                        deferrableSurface,
-                        sessionConfigAdapter.surfaceToStreamUseHintMap
-                    ),
-                    size = deferrableSurface.prescribedSize,
-                    format = StreamFormat(deferrableSurface.prescribedStreamFormat),
-                    camera = CameraId(
-                        sessionConfig.toCamera2ImplConfig().getPhysicalCameraId(
-                            cameraConfig.cameraId.value
-                        )!!
-                    )
-                )
-                streamConfigMap[outputConfig] = deferrableSurface
-                Log.debug {
-                    "Prepare config for: $deferrableSurface (${deferrableSurface.prescribedSize}," +
-                        " ${deferrableSurface.prescribedStreamFormat})"
-                }
-                if (deferrableSurface.containerClass == MediaCodec::class.java) {
-                    containsVideo = true
-                }
-            }
         }
 
-        val shouldCloseCaptureSessionOnDisconnect =
-            if (CameraQuirks.isImmediateSurfaceReleaseAllowed()) {
-                // If we can release Surfaces immediately, we'll finalize the session when the
-                // camera graph is closed (through FinalizeSessionOnCloseQuirk), and thus we won't
-                // need to explicitly close the capture session.
-                false
-            } else {
-                if (cameraQuirks.quirks.contains(CloseCaptureSessionOnVideoQuirk::class.java) &&
-                    containsVideo
-                ) {
-                    true
-                } else {
-                    DeviceQuirks[CloseCaptureSessionOnDisconnectQuirk::class.java] != null
-                }
-            }
-        val shouldCloseCameraDeviceOnClose =
-            DeviceQuirks[CloseCameraDeviceOnCameraGraphCloseQuirk::class.java] != null
-        val combinedFlags = cameraGraphFlags.copy(
-            quirkCloseCaptureSessionOnDisconnect = shouldCloseCaptureSessionOnDisconnect,
-            quirkCloseCameraDeviceOnClose = shouldCloseCameraDeviceOnClose,
-        )
-
-        // Build up a config (using TEMPLATE_PREVIEW by default)
-        val graph = cameraPipe.create(
-            CameraGraph.Config(
-                camera = cameraConfig.cameraId,
-                streams = streamConfigMap.keys.toList(),
-                defaultListeners = listOf(callbackMap, requestListener),
-                flags = combinedFlags,
-            )
-        )
-
         val surfaceToStreamMap = mutableMapOf<DeferrableSurface, StreamId>()
         streamConfigMap.forEach { (streamConfig, deferrableSurface) ->
-            graph.streams[streamConfig]?.let {
+            cameraGraph.streams[streamConfig]?.let {
                 surfaceToStreamMap[deferrableSurface] = it.id
             }
         }
 
         Log.debug {
-            "Prepare UseCaseCameraGraphConfig: $graph "
+            "Prepare UseCaseCameraGraphConfig: $cameraGraph "
         }
 
         if (sessionConfigAdapter.isSessionConfigValid()) {
-            useCaseSurfaceManager.setupAsync(graph, sessionConfigAdapter, surfaceToStreamMap)
+            useCaseSurfaceManager.setupAsync(cameraGraph, sessionConfigAdapter, surfaceToStreamMap)
                 .invokeOnCompletion { throwable ->
                     // Only show logs for error cases, ignore CancellationException since the task
                     // could be cancelled by UseCaseSurfaceManager#stopAsync().
@@ -206,28 +125,14 @@
             }
         }
 
-        graph.start()
+        cameraGraph.start()
 
         return UseCaseGraphConfig(
-            graph = graph,
+            graph = cameraGraph,
             surfaceToStreamMap = surfaceToStreamMap,
             cameraStateAdapter = cameraStateAdapter,
         )
     }
-
-    private fun getStreamUseCase(
-        deferrableSurface: DeferrableSurface,
-        mapping: Map<DeferrableSurface, Long>
-    ): OutputStream.StreamUseCase? {
-        return mapping[deferrableSurface]?.let { OutputStream.StreamUseCase(it) }
-    }
-
-    private fun getStreamUseHint(
-        deferrableSurface: DeferrableSurface,
-        mapping: Map<DeferrableSurface, Long>
-    ): OutputStream.StreamUseHint? {
-        return mapping[deferrableSurface]?.let { OutputStream.StreamUseHint(it) }
-    }
 }
 
 data class UseCaseGraphConfig(
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/AndroidCaptureFailure.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/AndroidCaptureFailure.kt
new file mode 100644
index 0000000..083412f
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/AndroidCaptureFailure.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.integration.impl
+
+import android.hardware.camera2.CaptureFailure
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.FrameNumber
+import androidx.camera.camera2.pipe.RequestFailure
+import androidx.camera.camera2.pipe.RequestMetadata
+import androidx.camera.camera2.pipe.UnsafeWrapper
+import kotlin.reflect.KClass
+
+/**
+ * This class implements the [RequestFailure] interface by passing the package-private
+ * [CaptureFailure] object.
+ */
+@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+internal class AndroidCaptureFailure(
+    override val requestMetadata: RequestMetadata,
+    override val captureFailure: CaptureFailure
+) : RequestFailure, UnsafeWrapper {
+    override val frameNumber: FrameNumber = FrameNumber(captureFailure.frameNumber)
+    override val reason: Int = captureFailure.reason
+    override val wasImageCaptured: Boolean = captureFailure.wasImageCaptured()
+
+    @Suppress("UNCHECKED_CAST")
+    override fun <T : Any> unwrapAs(type: KClass<T>): T? =
+        when (type) {
+            CaptureFailure::class -> captureFailure as T?
+            else -> null
+        }
+}
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraCallbackMap.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraCallbackMap.kt
index 315e653..228ca50 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraCallbackMap.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraCallbackMap.kt
@@ -17,7 +17,6 @@
 package androidx.camera.camera2.pipe.integration.impl
 
 import android.hardware.camera2.CameraCaptureSession
-import android.hardware.camera2.CaptureFailure
 import android.hardware.camera2.CaptureRequest
 import android.hardware.camera2.CaptureResult
 import android.hardware.camera2.TotalCaptureResult
@@ -30,6 +29,7 @@
 import androidx.camera.camera2.pipe.FrameMetadata
 import androidx.camera.camera2.pipe.FrameNumber
 import androidx.camera.camera2.pipe.Request
+import androidx.camera.camera2.pipe.RequestFailure
 import androidx.camera.camera2.pipe.RequestMetadata
 import androidx.camera.camera2.pipe.StreamId
 import androidx.camera.camera2.pipe.integration.adapter.CameraUseCaseAdapter
@@ -118,21 +118,18 @@
         }
     }
 
-    @Deprecated(
-        message = "Migrating to using RequestFailureWrapper instead of CaptureFailure",
-        level = DeprecationLevel.WARNING
-    )
     override fun onFailed(
         requestMetadata: RequestMetadata,
         frameNumber: FrameNumber,
-        captureFailure: CaptureFailure
+        requestFailure: RequestFailure
     ) {
         for ((callback, executor) in callbacks) {
             if (callback is CameraUseCaseAdapter.CaptureCallbackContainer) {
                 val session: CameraCaptureSession? =
                     requestMetadata.unwrapAs(CameraCaptureSession::class)
                 val request: CaptureRequest? = requestMetadata.unwrapAs(CaptureRequest::class)
-                if (session != null && request != null) {
+                val captureFailure = requestFailure.captureFailure
+                if (session != null && request != null && captureFailure != null) {
                     executor.execute {
                         callback.captureCallback.onCaptureFailed(
                             session, request,
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraProperties.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraProperties.kt
index 4da3b4b..d524290 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraProperties.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraProperties.kt
@@ -21,7 +21,6 @@
 import androidx.annotation.RequiresApi
 import androidx.camera.camera2.pipe.CameraId
 import androidx.camera.camera2.pipe.CameraMetadata
-import androidx.camera.camera2.pipe.CameraPipe
 import androidx.camera.camera2.pipe.integration.config.CameraConfig
 import androidx.camera.camera2.pipe.integration.config.CameraScope
 import javax.inject.Inject
@@ -36,12 +35,14 @@
 
 @CameraScope
 class CameraPipeCameraProperties @Inject constructor(
-    private val cameraPipe: CameraPipe,
-    private val cameraConfig: CameraConfig
+    private val cameraConfig: CameraConfig,
+    private val cameraMetadata: CameraMetadata?,
 ) : CameraProperties {
     override val cameraId: CameraId
         get() = cameraConfig.cameraId
-    override val metadata: CameraMetadata by lazy {
-        checkNotNull(cameraPipe.cameras().awaitCameraMetadata(cameraId))
-    }
+
+    // TODO(b/270615090): Here it's safe to use the !! operator because all consumers of the
+    //  metadata don't read it during CameraX initialization. Long term however, CameraProperties
+    //  can probably be removed entirely.
+    override val metadata: CameraMetadata = cameraMetadata!!
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CapturePipeline.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CapturePipeline.kt
index d696fb1..5469642 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CapturePipeline.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CapturePipeline.kt
@@ -37,7 +37,6 @@
 import android.annotation.SuppressLint
 import android.hardware.camera2.CameraCharacteristics.CONTROL_AE_STATE_FLASH_REQUIRED
 import android.hardware.camera2.CameraDevice
-import android.hardware.camera2.CaptureFailure
 import android.hardware.camera2.CaptureResult
 import androidx.annotation.RequiresApi
 import androidx.camera.camera2.pipe.CameraGraph
@@ -45,6 +44,7 @@
 import androidx.camera.camera2.pipe.FrameNumber
 import androidx.camera.camera2.pipe.Lock3ABehavior
 import androidx.camera.camera2.pipe.Request
+import androidx.camera.camera2.pipe.RequestFailure
 import androidx.camera.camera2.pipe.RequestMetadata
 import androidx.camera.camera2.pipe.Result3A
 import androidx.camera.camera2.pipe.core.Log.debug
@@ -296,22 +296,17 @@
                             completeSignal.complete(null)
                         }
 
-                        @Deprecated(
-                            message = "Migrating to using RequestFailureWrapper instead of " +
-                                "CaptureFailure",
-                            level = DeprecationLevel.WARNING,
-                            replaceWith = ReplaceWith("onFailed")
-                        )
                         @SuppressLint("ClassVerificationFailure")
                         override fun onFailed(
                             requestMetadata: RequestMetadata,
                             frameNumber: FrameNumber,
-                            captureFailure: CaptureFailure
+                            requestFailure: RequestFailure
                         ) {
                             completeSignal.completeExceptionally(
                                 ImageCaptureException(
                                     ERROR_CAPTURE_FAILED,
-                                    "Capture request failed with reason " + captureFailure.reason,
+                                    "Capture request failed with reason " +
+                                        requestFailure.reason,
                                     null
                                 )
                             )
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/ComboRequestListener.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/ComboRequestListener.kt
index 6f98ef1..c29b7d6 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/ComboRequestListener.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/ComboRequestListener.kt
@@ -16,13 +16,13 @@
 
 package androidx.camera.camera2.pipe.integration.impl
 
-import android.hardware.camera2.CaptureFailure
 import androidx.annotation.RequiresApi
 import androidx.camera.camera2.pipe.CameraTimestamp
 import androidx.camera.camera2.pipe.FrameInfo
 import androidx.camera.camera2.pipe.FrameMetadata
 import androidx.camera.camera2.pipe.FrameNumber
 import androidx.camera.camera2.pipe.Request
+import androidx.camera.camera2.pipe.RequestFailure
 import androidx.camera.camera2.pipe.RequestMetadata
 import androidx.camera.camera2.pipe.StreamId
 import androidx.camera.camera2.pipe.integration.config.CameraScope
@@ -84,18 +84,19 @@
         }
     }
 
-    @Deprecated(
-        message = "Migrating to using RequestFailureWrapper instead of CaptureFailure",
-        level = DeprecationLevel.WARNING
-    )
     override fun onFailed(
         requestMetadata: RequestMetadata,
         frameNumber: FrameNumber,
-        captureFailure: CaptureFailure
+        requestFailure: RequestFailure
     ) {
-        @Suppress("DEPRECATION")
         listeners.forEach { (listener, executor) ->
-            executor.execute { listener.onFailed(requestMetadata, frameNumber, captureFailure) }
+            executor.execute {
+                listener.onFailed(
+                    requestMetadata,
+                    frameNumber,
+                    requestFailure
+                )
+            }
         }
     }
 
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FakeCaptureFailure.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FakeCaptureFailure.kt
new file mode 100644
index 0000000..d7d309b6
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FakeCaptureFailure.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.integration.impl
+
+import android.hardware.camera2.CaptureFailure
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.FrameNumber
+import androidx.camera.camera2.pipe.RequestFailure
+import androidx.camera.camera2.pipe.RequestMetadata
+
+/**
+ * This class implements the [RequestFailure] interface by extracting the fields of
+ * the package-private [CaptureFailure] object.
+ */
+@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+internal data class FakeCaptureFailure(
+    override val requestMetadata: RequestMetadata,
+    override val wasImageCaptured: Boolean,
+    override val frameNumber: FrameNumber,
+    override val reason: Int,
+    override val captureFailure: CaptureFailure?
+) : RequestFailure
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt
index b67a339..5199d9f 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt
@@ -18,7 +18,6 @@
 
 package androidx.camera.camera2.pipe.integration.impl
 
-import android.hardware.camera2.CaptureFailure
 import android.hardware.camera2.CaptureRequest
 import androidx.annotation.GuardedBy
 import androidx.annotation.RequiresApi
@@ -30,6 +29,7 @@
 import androidx.camera.camera2.pipe.FrameNumber
 import androidx.camera.camera2.pipe.Metadata
 import androidx.camera.camera2.pipe.Request
+import androidx.camera.camera2.pipe.RequestFailure
 import androidx.camera.camera2.pipe.RequestMetadata
 import androidx.camera.camera2.pipe.RequestTemplate
 import androidx.camera.camera2.pipe.StreamId
@@ -333,23 +333,19 @@
             }
         }
 
-        @Deprecated(
-            message = "Migrating to using RequestFailureWrapper instead of CaptureFailure",
-            level = DeprecationLevel.WARNING
-        )
         override fun onFailed(
             requestMetadata: RequestMetadata,
             frameNumber: FrameNumber,
-            captureFailure: CaptureFailure,
+            requestFailure: RequestFailure,
         ) {
             @Suppress("DEPRECATION")
-            super.onFailed(requestMetadata, frameNumber, captureFailure)
-            completeExceptionally(requestMetadata, captureFailure)
+            super.onFailed(requestMetadata, frameNumber, requestFailure)
+            completeExceptionally(requestMetadata, requestFailure)
         }
 
         private fun completeExceptionally(
             requestMetadata: RequestMetadata,
-            captureFailure: CaptureFailure? = null
+            requestFailure: RequestFailure? = null
         ) {
             threads.scope.launch(start = CoroutineStart.UNDISPATCHED) {
                 requestMetadata[USE_CASE_CAMERA_STATE_CUSTOM_TAG]?.let { requestNo ->
@@ -357,7 +353,7 @@
                         updateSignals.completeExceptionally(
                             requestNo,
                             Throwable(
-                                "Failed in framework level" + (captureFailure?.reason?.let {
+                                "Failed in framework level" + (requestFailure?.reason?.let {
                                     " with CaptureFailure.reason = $it"
                                 } ?: "")
                             )
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
index a8f3c76..aa17277 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
 
 package androidx.camera.camera2.pipe.integration.impl
 
@@ -22,26 +23,38 @@
 import androidx.annotation.GuardedBy
 import androidx.annotation.RequiresApi
 import androidx.camera.camera2.pipe.CameraGraph
+import androidx.camera.camera2.pipe.CameraId
+import androidx.camera.camera2.pipe.CameraPipe
+import androidx.camera.camera2.pipe.CameraStream
+import androidx.camera.camera2.pipe.OutputStream
+import androidx.camera.camera2.pipe.StreamFormat
 import androidx.camera.camera2.pipe.core.Log
 import androidx.camera.camera2.pipe.integration.adapter.CameraStateAdapter
 import androidx.camera.camera2.pipe.integration.adapter.EncoderProfilesProviderAdapter
+import androidx.camera.camera2.pipe.integration.adapter.SessionConfigAdapter
 import androidx.camera.camera2.pipe.integration.adapter.SupportedSurfaceCombination
 import androidx.camera.camera2.pipe.integration.compat.quirk.CameraQuirks
+import androidx.camera.camera2.pipe.integration.compat.quirk.CloseCameraDeviceOnCameraGraphCloseQuirk
+import androidx.camera.camera2.pipe.integration.compat.quirk.CloseCaptureSessionOnVideoQuirk
+import androidx.camera.camera2.pipe.integration.compat.quirk.DeviceQuirks
 import androidx.camera.camera2.pipe.integration.config.CameraConfig
 import androidx.camera.camera2.pipe.integration.config.CameraScope
 import androidx.camera.camera2.pipe.integration.config.UseCaseCameraComponent
 import androidx.camera.camera2.pipe.integration.config.UseCaseCameraConfig
+import androidx.camera.camera2.pipe.integration.internal.CameraGraphCreator
 import androidx.camera.camera2.pipe.integration.interop.Camera2CameraControl
 import androidx.camera.camera2.pipe.integration.interop.ExperimentalCamera2Interop
 import androidx.camera.core.UseCase
 import androidx.camera.core.impl.CameraInternal
 import androidx.camera.core.impl.CameraMode
 import androidx.camera.core.impl.DeferrableSurface
+import androidx.camera.core.impl.SessionConfig
 import androidx.camera.core.impl.SessionConfig.ValidatingBuilder
 import javax.inject.Inject
 import javax.inject.Provider
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.joinAll
+import kotlinx.coroutines.runBlocking
 
 /**
  * This class keeps track of the currently attached and active [UseCase]'s for a specific camera.
@@ -73,6 +86,10 @@
 @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
 @CameraScope
 class UseCaseManager @Inject constructor(
+    private val cameraPipe: CameraPipe,
+    private val cameraGraphCreator: CameraGraphCreator,
+    private val callbackMap: CameraCallbackMap,
+    private val requestListener: ComboRequestListener,
     private val cameraConfig: CameraConfig,
     private val builder: UseCaseCameraComponent.Builder,
     @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") // Java version required for Dagger
@@ -97,6 +114,9 @@
     @GuardedBy("lock")
     private var activeResumeEnabled = false
 
+    @GuardedBy("lock")
+    private var refreshAttached = true
+
     private val meteringRepeating by lazy {
         MeteringRepeating.Builder(
             cameraProperties,
@@ -121,6 +141,15 @@
 
     private val allControls = controls.toMutableSet().apply { add(camera2CameraControl) }
 
+    fun pauseRefresh() = synchronized(lock) {
+        refreshAttached = false
+    }
+
+    fun resumeRefresh() = synchronized(lock) {
+        refreshAttached = true
+        refreshAttachedUseCases(attachedUseCases)
+    }
+
     /**
      * This attaches the specified [useCases] to the current set of attached use cases. When any
      * changes are identified (i.e., a new use case is added), the subsequent actions would trigger
@@ -254,6 +283,9 @@
 
     @GuardedBy("lock")
     private fun refreshAttachedUseCases(newUseCases: Set<UseCase>) {
+        if (!refreshAttached) {
+            return
+        }
         val useCases = newUseCases.toList()
 
         // Close prior camera graph
@@ -278,14 +310,24 @@
             return
         }
 
+        val sessionConfigAdapter = SessionConfigAdapter(useCases)
+        val streamConfigMap = mutableMapOf<CameraStream.Config, DeferrableSurface>()
+
+        val graphConfig = createCameraGraphConfig(
+            sessionConfigAdapter, streamConfigMap, callbackMap,
+            requestListener, cameraConfig, cameraQuirks, cameraGraphFlags)
+        val cameraGraph =
+            runBlocking { cameraGraphCreator.createCameraGraph(cameraPipe, graphConfig) }
+
         // Create and configure the new camera component.
         _activeComponent =
             builder.config(
                 UseCaseCameraConfig(
                     useCases,
+                    sessionConfigAdapter,
                     cameraStateAdapter,
-                    cameraQuirks,
-                    cameraGraphFlags
+                    cameraGraph,
+                    streamConfigMap
                 )
             )
                 .build()
@@ -420,4 +462,106 @@
         val captureConfig = sessionConfig.repeatingCaptureConfig
         return predicate(captureConfig.surfaces, sessionConfig.surfaces)
     }
+
+    companion object {
+        fun SessionConfig.toCamera2ImplConfig(): Camera2ImplConfig {
+            return Camera2ImplConfig(implementationOptions)
+        }
+
+        fun createCameraGraphConfig(
+            sessionConfigAdapter: SessionConfigAdapter,
+            streamConfigMap: MutableMap<CameraStream.Config, DeferrableSurface>,
+            callbackMap: CameraCallbackMap,
+            requestListener: ComboRequestListener,
+            cameraConfig: CameraConfig,
+            cameraQuirks: CameraQuirks,
+            cameraGraphFlags: CameraGraph.Flags?,
+        ): CameraGraph.Config {
+            var containsVideo = false
+            // TODO: This may need to combine outputs that are (or will) share the same output
+            //  imageReader or surface.
+            sessionConfigAdapter.getValidSessionConfigOrNull()?.let { sessionConfig ->
+                sessionConfig.surfaces.forEach { deferrableSurface ->
+                    val outputConfig = CameraStream.Config.create(
+                        streamUseCase = getStreamUseCase(
+                            deferrableSurface,
+                            sessionConfigAdapter.surfaceToStreamUseCaseMap
+                        ),
+                        streamUseHint = getStreamUseHint(
+                            deferrableSurface,
+                            sessionConfigAdapter.surfaceToStreamUseHintMap
+                        ),
+                        size = deferrableSurface.prescribedSize,
+                        format = StreamFormat(deferrableSurface.prescribedStreamFormat),
+                        camera = CameraId(
+                            sessionConfig.toCamera2ImplConfig().getPhysicalCameraId(
+                                cameraConfig.cameraId.value
+                            )!!
+                        )
+                    )
+                    streamConfigMap[outputConfig] = deferrableSurface
+                    Log.debug {
+                        "Prepare config for: $deferrableSurface (" +
+                            "${deferrableSurface.prescribedSize}," +
+                            " ${deferrableSurface.prescribedStreamFormat})"
+                    }
+                    if (deferrableSurface.containerClass == MediaCodec::class.java) {
+                        containsVideo = true
+                    }
+                }
+            }
+            val shouldCloseCaptureSessionOnDisconnect =
+                if (CameraQuirks.isImmediateSurfaceReleaseAllowed()) {
+                    // If we can release Surfaces immediately, we'll finalize the session when the
+                    // camera graph is closed (through FinalizeSessionOnCloseQuirk), and thus we won't
+                    // need to explicitly close the capture session.
+                    false
+                } else {
+                    if (cameraQuirks.quirks.contains(CloseCaptureSessionOnVideoQuirk::class.java) &&
+                        containsVideo
+                    ) {
+                        true
+                    } else
+                    // TODO(b/277675483): From the current test results, older devices (Android
+                    //  version <= 8.1.0) seem to have a higher chance of encountering an issue where
+                    //  not closing the capture session would lead to CameraDevice.close stalling
+                    //  indefinitely. This version check might need to be further fine-turned down the
+                    //  line.
+                        Build.VERSION.SDK_INT <= Build.VERSION_CODES.O_MR1
+                }
+            val shouldCloseCameraDeviceOnClose =
+                DeviceQuirks[CloseCameraDeviceOnCameraGraphCloseQuirk::class.java] != null
+
+            val combinedFlags = cameraGraphFlags?.copy(
+                quirkCloseCaptureSessionOnDisconnect = shouldCloseCaptureSessionOnDisconnect,
+                quirkCloseCameraDeviceOnClose = shouldCloseCameraDeviceOnClose,
+            )
+                ?: CameraGraph.Flags(
+                    quirkCloseCaptureSessionOnDisconnect = shouldCloseCaptureSessionOnDisconnect,
+                    quirkCloseCameraDeviceOnClose = shouldCloseCameraDeviceOnClose,
+                )
+
+            // Build up a config (using TEMPLATE_PREVIEW by default)
+            return CameraGraph.Config(
+                camera = cameraConfig.cameraId,
+                streams = streamConfigMap.keys.toList(),
+                defaultListeners = listOf(callbackMap, requestListener),
+                flags = combinedFlags,
+            )
+        }
+
+        private fun getStreamUseCase(
+            deferrableSurface: DeferrableSurface,
+            mapping: Map<DeferrableSurface, Long>
+        ): OutputStream.StreamUseCase? {
+            return mapping[deferrableSurface]?.let { OutputStream.StreamUseCase(it) }
+        }
+
+        private fun getStreamUseHint(
+            deferrableSurface: DeferrableSurface,
+            mapping: Map<DeferrableSurface, Long>
+        ): OutputStream.StreamUseHint? {
+            return mapping[deferrableSurface]?.let { OutputStream.StreamUseHint(it) }
+        }
+    }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/CameraGraphCreator.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/CameraGraphCreator.kt
new file mode 100644
index 0000000..f0aabe8
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/CameraGraphCreator.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.integration.internal
+
+import androidx.annotation.GuardedBy
+import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.CameraGraph
+import androidx.camera.camera2.pipe.CameraPipe
+import javax.inject.Inject
+import javax.inject.Singleton
+import kotlinx.coroutines.CompletableDeferred
+
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+@Singleton
+class CameraGraphCreator @Inject constructor() {
+    private val lock = Any()
+
+    @GuardedBy("lock")
+    var currentExpectedConfigs = 1
+
+    @GuardedBy("lock")
+    val currentConfigs = mutableListOf<CameraGraph.Config>()
+
+    private var pendingDeferred: CompletableDeferred<CameraGraph>? = null
+
+    fun setConcurrentModeOn(on: Boolean) = synchronized(lock) {
+        currentExpectedConfigs = if (on) {
+            2
+        } else {
+            1
+        }
+    }
+
+    suspend fun createCameraGraph(cameraPipe: CameraPipe, config: CameraGraph.Config): CameraGraph {
+        var deferred: CompletableDeferred<CameraGraph>? = null
+        synchronized(lock) {
+            currentConfigs.add(config)
+            if (currentConfigs.size != currentExpectedConfigs) {
+                deferred = CompletableDeferred()
+                pendingDeferred = deferred
+            }
+        }
+        if (deferred != null) {
+            return deferred!!.await()
+        }
+        synchronized(lock) {
+            if (currentExpectedConfigs == 1) {
+                val cameraGraph = cameraPipe.create(config)
+                currentConfigs.clear()
+                return cameraGraph
+            } else {
+                val cameraGraphs = cameraPipe.createCameraGraphs(currentConfigs)
+                pendingDeferred?.complete(cameraGraphs.first())
+                currentConfigs.clear()
+                return cameraGraphs[1]
+            }
+        }
+    }
+}
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapterTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapterTest.kt
new file mode 100644
index 0000000..12c69f8
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapterTest.kt
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.integration.adapter
+
+import android.os.Build
+import androidx.camera.camera2.pipe.CameraBackendId
+import androidx.camera.camera2.pipe.CameraId
+import androidx.camera.camera2.pipe.integration.internal.CameraGraphCreator
+import androidx.camera.camera2.pipe.integration.testing.FakeCameraInfoAdapterCreator
+import androidx.camera.camera2.pipe.testing.FakeCameraDevices
+import androidx.camera.camera2.pipe.testing.FakeCameraMetadata
+import androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_CONCURRENT
+import androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_SINGLE
+import androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_UNSPECIFIED
+import androidx.camera.core.impl.CameraInfoInternal
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+
+@RunWith(RobolectricCameraPipeTestRunner::class)
+@DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class CameraCoordinatorAdapterTest {
+
+    private val cameraMetadata = FakeCameraMetadata()
+
+    private val cameraDevices = FakeCameraDevices(
+        defaultCameraBackendId = CameraBackendId("0"),
+        concurrentCameraBackendIds = setOf(
+            setOf(CameraBackendId("0"), CameraBackendId("1")),
+            setOf(CameraBackendId("0"), CameraBackendId("2"))
+        ),
+        cameraMetadataMap = mapOf(CameraBackendId("0") to listOf(cameraMetadata))
+    )
+
+    private val mockCameraGraphCreator: CameraGraphCreator = mock()
+    private val mockCameraInternalAdapter0: CameraInternalAdapter = mock()
+    private val mockCameraInternalAdapter1: CameraInternalAdapter = mock()
+    private val mockCameraInternalAdapter2: CameraInternalAdapter = mock()
+
+    private val cameraCoordinatorAdapter = CameraCoordinatorAdapter(
+        cameraDevices, mockCameraGraphCreator)
+
+    @Before
+    fun setUp() {
+        cameraCoordinatorAdapter.registerCamera("0", mockCameraInternalAdapter0)
+        cameraCoordinatorAdapter.registerCamera("1", mockCameraInternalAdapter1)
+        cameraCoordinatorAdapter.registerCamera("2", mockCameraInternalAdapter2)
+    }
+
+    @Test
+    fun getConcurrentCameraSelectors() {
+        val cameraSelectors = cameraCoordinatorAdapter.concurrentCameraSelectors
+        assertThat(cameraSelectors.size).isEqualTo(2)
+        assertThat(cameraSelectors[0].size).isEqualTo(2)
+        assertThat(cameraSelectors[1].size).isEqualTo(2)
+    }
+
+    @Test
+    fun setAndGetActiveConcurrentCameraInfos() {
+        cameraCoordinatorAdapter.activeConcurrentCameraInfos = mutableListOf(
+            FakeCameraInfoAdapterCreator.createCameraInfoAdapter(cameraId = CameraId("0")),
+            FakeCameraInfoAdapterCreator.createCameraInfoAdapter(cameraId = CameraId("1")))
+
+        assertThat(cameraCoordinatorAdapter.activeConcurrentCameraInfos.size).isEqualTo(2)
+        val cameraInfo0 = cameraCoordinatorAdapter.activeConcurrentCameraInfos[0]
+            as CameraInfoInternal
+        assertThat(cameraInfo0.cameraId).isEqualTo("0")
+        val cameraInfo1 = cameraCoordinatorAdapter.activeConcurrentCameraInfos[1]
+            as CameraInfoInternal
+        assertThat(cameraInfo1.cameraId).isEqualTo("1")
+        verify(mockCameraInternalAdapter0).resumeRefresh()
+        verify(mockCameraInternalAdapter1).resumeRefresh()
+    }
+
+    @Test
+    fun getPairedConcurrentCameraId() {
+        assertThat(cameraCoordinatorAdapter.getPairedConcurrentCameraId("0")).isNull()
+
+        cameraCoordinatorAdapter.activeConcurrentCameraInfos = mutableListOf(
+            FakeCameraInfoAdapterCreator.createCameraInfoAdapter(cameraId = CameraId("0")),
+            FakeCameraInfoAdapterCreator.createCameraInfoAdapter(cameraId = CameraId("1")))
+
+        assertThat(cameraCoordinatorAdapter.getPairedConcurrentCameraId("0")).isEqualTo("1")
+    }
+
+    @Test
+    fun setAndGetCameraOperatingMode() {
+        cameraCoordinatorAdapter.cameraOperatingMode = CAMERA_OPERATING_MODE_CONCURRENT
+
+        verify(mockCameraInternalAdapter0).pauseRefresh()
+        verify(mockCameraInternalAdapter0, never()).resumeRefresh()
+        verify(mockCameraInternalAdapter1).pauseRefresh()
+        verify(mockCameraInternalAdapter1, never()).resumeRefresh()
+        verify(mockCameraGraphCreator).setConcurrentModeOn(true)
+        assertThat(cameraCoordinatorAdapter.cameraOperatingMode)
+            .isEqualTo(CAMERA_OPERATING_MODE_CONCURRENT)
+
+        cameraCoordinatorAdapter.cameraOperatingMode = CAMERA_OPERATING_MODE_SINGLE
+
+        verify(mockCameraInternalAdapter0).resumeRefresh()
+        verify(mockCameraInternalAdapter1).resumeRefresh()
+        verify(mockCameraGraphCreator).setConcurrentModeOn(false)
+        assertThat(cameraCoordinatorAdapter.cameraOperatingMode)
+            .isEqualTo(CAMERA_OPERATING_MODE_SINGLE)
+    }
+
+    @Test
+    fun shutdown() {
+        cameraCoordinatorAdapter.cameraOperatingMode = CAMERA_OPERATING_MODE_CONCURRENT
+        cameraCoordinatorAdapter.activeConcurrentCameraInfos = mutableListOf(
+            FakeCameraInfoAdapterCreator.createCameraInfoAdapter(cameraId = CameraId("0")),
+            FakeCameraInfoAdapterCreator.createCameraInfoAdapter(cameraId = CameraId("1")))
+
+        cameraCoordinatorAdapter.shutdown()
+
+        assertThat(cameraCoordinatorAdapter.cameraInternalMap).isEmpty()
+        assertThat(cameraCoordinatorAdapter.activeConcurrentCameraInfos).isEmpty()
+        assertThat(cameraCoordinatorAdapter.concurrentCameraIdMap).isEmpty()
+        assertThat(cameraCoordinatorAdapter.concurrentCameraIdsSet).isEmpty()
+        assertThat(cameraCoordinatorAdapter.cameraOperatingMode).isEqualTo(
+            CAMERA_OPERATING_MODE_UNSPECIFIED)
+        assertThat(cameraCoordinatorAdapter.concurrentModeOn).isFalse()
+    }
+}
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombinationTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombinationTest.kt
index 8066f64..8074388 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombinationTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombinationTest.kt
@@ -32,6 +32,7 @@
 import android.util.Range
 import android.util.Size
 import android.view.WindowManager
+import androidx.camera.camera2.pipe.CameraBackendId
 import androidx.camera.camera2.pipe.CameraId
 import androidx.camera.camera2.pipe.integration.CameraPipeConfig
 import androidx.camera.camera2.pipe.integration.adapter.GuaranteedConfigurationsUtil.getConcurrentSupportedCombinationList
@@ -1949,6 +1950,10 @@
         val fakeCameraDevices =
             FakeCameraDevices(
                 defaultCameraBackendId = FakeCameraBackend.FAKE_CAMERA_BACKEND_ID,
+                concurrentCameraBackendIds = setOf(
+                    setOf(CameraBackendId("0"), CameraBackendId("1")),
+                    setOf(CameraBackendId("0"), CameraBackendId("2"))
+                ),
                 cameraMetadataMap =
                 mapOf(FakeCameraBackend.FAKE_CAMERA_BACKEND_ID to listOf(fakeCameraMetadata))
             )
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/CapturePipelineTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/CapturePipelineTest.kt
index a98c7d5..500139f 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/CapturePipelineTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/CapturePipelineTest.kt
@@ -612,12 +612,15 @@
         fakeCameraGraphSession.requestHandler = { requests ->
             requests.forEach { request ->
                 // Callback capture fail immediately.
-                @Suppress("DEPRECATION")
                 request.listeners.forEach {
+                    val requestMetadata = FakeRequestMetadata()
                     it.onFailed(
-                        requestMetadata = FakeRequestMetadata(),
+                        requestMetadata = requestMetadata,
                         frameNumber = FrameNumber(100L),
-                        captureFailure = mock(CaptureFailure::class.java),
+                        requestFailure = AndroidCaptureFailure(
+                            requestMetadata,
+                            mock(CaptureFailure::class.java)
+                        )
                     )
                 }
             }
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/StillCaptureRequestTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/StillCaptureRequestTest.kt
index 945507d..dd51c0d 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/StillCaptureRequestTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/StillCaptureRequestTest.kt
@@ -201,6 +201,8 @@
     @Test
     fun captureRequestsFailWithCaptureFailedError_onFailed(): Unit = runTest(testDispatcher) {
         val requestFuture = stillCaptureRequestControl.issueCaptureRequests()
+        val fakeRequestMetadata = FakeRequestMetadata()
+        val frameNumber = FrameNumber(0)
 
         advanceUntilIdle()
         assumeTrue(fakeCameraGraphSession.submittedRequests.size == captureConfigList.size)
@@ -209,9 +211,15 @@
             request.listeners.forEach { listener ->
                 @Suppress("DEPRECATION")
                 listener.onFailed(
-                    FakeRequestMetadata(),
-                    FrameNumber(0),
-                    createCaptureFailure()
+                    fakeRequestMetadata,
+                    frameNumber,
+                    FakeCaptureFailure(
+                        fakeRequestMetadata,
+                        false,
+                        frameNumber,
+                        CaptureFailure.REASON_ERROR,
+                        null
+                    )
                 )
             }
         }
@@ -414,13 +422,6 @@
         }
     }
 
-    private fun createCaptureFailure(): CaptureFailure {
-        val c = Class.forName("android.hardware.camera2.CaptureFailure")
-        val constructor = c.getDeclaredConstructor()
-        constructor.isAccessible = true
-        return constructor.newInstance() as CaptureFailure
-    }
-
     private fun initUseCaseCameraScopeObjects() {
         fakeCameraGraphSession = FakeCameraGraphSession()
         fakeCameraGraph = FakeCameraGraph(
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManagerTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManagerTest.kt
index b155784..e59f361 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManagerTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManagerTest.kt
@@ -16,11 +16,13 @@
 
 package androidx.camera.camera2.pipe.integration.impl
 
+import android.content.Context
 import android.hardware.camera2.CameraCharacteristics
 import android.os.Build
 import android.util.Size
 import androidx.camera.camera2.pipe.CameraGraph
 import androidx.camera.camera2.pipe.CameraId
+import androidx.camera.camera2.pipe.CameraPipe
 import androidx.camera.camera2.pipe.integration.adapter.CameraStateAdapter
 import androidx.camera.camera2.pipe.integration.adapter.RobolectricCameraPipeTestRunner
 import androidx.camera.camera2.pipe.integration.compat.StreamConfigurationMapCompat
@@ -28,6 +30,7 @@
 import androidx.camera.camera2.pipe.integration.compat.workaround.OutputSizesCorrector
 import androidx.camera.camera2.pipe.integration.config.CameraConfig
 import androidx.camera.camera2.pipe.integration.impl.UseCaseCamera.RunningUseCasesChangeListener
+import androidx.camera.camera2.pipe.integration.internal.CameraGraphCreator
 import androidx.camera.camera2.pipe.integration.interop.Camera2CameraControl
 import androidx.camera.camera2.pipe.integration.interop.ExperimentalCamera2Interop
 import androidx.camera.camera2.pipe.integration.testing.FakeCamera2CameraControlCompat
@@ -53,6 +56,9 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.robolectric.annotation.Config
+import org.robolectric.shadow.api.Shadow
+import org.robolectric.shadows.ShadowCameraCharacteristics
+import org.robolectric.shadows.ShadowCameraManager
 import org.robolectric.shadows.StreamConfigurationMapBuilder
 
 @RunWith(RobolectricCameraPipeTestRunner::class)
@@ -325,13 +331,24 @@
         val characteristicsMap: Map<CameraCharacteristics.Key<*>, Any?> = mapOf(
             CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP to streamConfigurationMap,
         )
+
+        val characteristics = ShadowCameraCharacteristics.newCameraCharacteristics()
+        (Shadow.extract<Any>(
+            ApplicationProvider.getApplicationContext<Context>()
+                .getSystemService(Context.CAMERA_SERVICE)
+        ) as ShadowCameraManager).addCamera("0", characteristics)
+
         val fakeCameraMetadata = FakeCameraMetadata(
             cameraId = cameraId,
             characteristics = characteristicsMap
         )
         val fakeCamera = FakeCamera()
         return UseCaseManager(
+            cameraPipe = CameraPipe(CameraPipe.Config(ApplicationProvider.getApplicationContext())),
+            cameraGraphCreator = CameraGraphCreator(),
             cameraConfig = CameraConfig(cameraId),
+            callbackMap = CameraCallbackMap(),
+            requestListener = ComboRequestListener(),
             builder = useCaseCameraComponentBuilder,
             controls = controls as java.util.Set<UseCaseCameraControl>,
             cameraProperties = FakeCameraProperties(
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/internal/CameraGraphCreatorTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/internal/CameraGraphCreatorTest.kt
new file mode 100644
index 0000000..02100cc
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/internal/CameraGraphCreatorTest.kt
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.integration.internal
+
+import android.content.Context
+import android.graphics.Rect
+import android.hardware.camera2.CameraCharacteristics
+import android.hardware.camera2.CameraMetadata
+import android.os.Build
+import android.util.Size
+import androidx.camera.camera2.pipe.CameraGraph
+import androidx.camera.camera2.pipe.CameraId
+import androidx.camera.camera2.pipe.CameraPipe
+import androidx.camera.camera2.pipe.CameraStream
+import androidx.camera.camera2.pipe.StreamFormat
+import androidx.camera.camera2.pipe.integration.adapter.RobolectricCameraPipeTestRunner
+import androidx.test.core.app.ApplicationProvider
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.async
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+import org.robolectric.shadow.api.Shadow
+import org.robolectric.shadows.ShadowCameraCharacteristics
+import org.robolectric.shadows.ShadowCameraManager
+import org.robolectric.shadows.StreamConfigurationMapBuilder
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(RobolectricCameraPipeTestRunner::class)
+@DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class CameraGraphCreatorTest {
+
+    private val cameraGraphCreator: CameraGraphCreator = CameraGraphCreator()
+    private val context = ApplicationProvider.getApplicationContext() as Context
+    private val cameraPipe = CameraPipe(CameraPipe.Config(context))
+
+    private val stream1Config = CameraStream.Config.create(
+        Size(640, 480), StreamFormat.YUV_420_888)
+    private val stream2Config = CameraStream.Config.create(
+        Size(1280, 720), StreamFormat.YUV_420_888)
+    private val cameraGraph1Config = CameraGraph.Config(CameraId("0"), listOf(stream1Config))
+    private val cameraGraph2Config = CameraGraph.Config(CameraId("1"), listOf(stream2Config))
+
+    @Before
+    fun setUp() {
+        setupCameras()
+    }
+
+    @Test
+    fun createCameraGraph_singleMode() = runTest {
+        cameraGraphCreator.setConcurrentModeOn(false)
+        val cameraGraph = cameraGraphCreator.createCameraGraph(cameraPipe, cameraGraph1Config)
+
+        advanceUntilIdle()
+        assertThat(cameraGraph).isNotNull()
+    }
+
+    @Test
+    fun createCameraGraph_concurrentMode() = runTest {
+        cameraGraphCreator.setConcurrentModeOn(true)
+
+        val cameraGraph0 = async {
+            cameraGraphCreator.createCameraGraph(cameraPipe, cameraGraph1Config)
+        }
+        advanceUntilIdle()
+        assertThat(cameraGraph0.isCompleted).isFalse()
+
+        val cameraGraph1 = async {
+            cameraGraphCreator.createCameraGraph(cameraPipe, cameraGraph2Config)
+        }
+        advanceUntilIdle()
+
+        assertThat(cameraGraph0.isCompleted).isTrue()
+        assertThat(cameraGraph1.isCompleted).isTrue()
+        assertThat(cameraGraph0.await()).isNotNull()
+        assertThat(cameraGraph1.await()).isNotNull()
+    }
+
+    private fun setupCameras() {
+        val capabilities =
+            intArrayOf(CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE)
+
+        initCharacteristics("0", CameraCharacteristics.LENS_FACING_BACK, capabilities)
+        initCharacteristics("1", CameraCharacteristics.LENS_FACING_FRONT, capabilities)
+    }
+
+    private fun initCharacteristics(cameraId: String, lensFacing: Int, capabilities: IntArray?) {
+        val sensorWidth = 640
+        val sensorHeight = 480
+
+        val characteristics = ShadowCameraCharacteristics.newCameraCharacteristics()
+        val shadowCharacteristics =
+            Shadow.extract<ShadowCameraCharacteristics>(characteristics).apply {
+
+                set(CameraCharacteristics.LENS_FACING, lensFacing)
+
+                set(
+                    CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE,
+                    Rect(0, 0, sensorWidth, sensorHeight)
+                )
+
+                set(
+                    CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL,
+                    CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
+                )
+
+                set(
+                    CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP,
+                    StreamConfigurationMapBuilder.newBuilder().build()
+                )
+            }
+
+        capabilities?.let {
+            shadowCharacteristics.set(
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES, capabilities
+            )
+        }
+
+        // Add the camera to the camera service
+        (Shadow.extract<Any>(
+            ApplicationProvider.getApplicationContext<Context>()
+                .getSystemService(Context.CAMERA_SERVICE)
+        ) as ShadowCameraManager).addCamera(cameraId, characteristics)
+    }
+}
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraGraphSession.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraGraphSession.kt
index c3d481e..cd6027d 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraGraphSession.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraGraphSession.kt
@@ -29,6 +29,7 @@
 import androidx.camera.camera2.pipe.Request
 import androidx.camera.camera2.pipe.Result3A
 import androidx.camera.camera2.pipe.TorchState
+import androidx.camera.camera2.pipe.integration.impl.FakeCaptureFailure
 import androidx.camera.camera2.pipe.integration.testing.FakeCameraGraphSession.RequestStatus.ABORTED
 import androidx.camera.camera2.pipe.integration.testing.FakeCameraGraphSession.RequestStatus.FAILED
 import androidx.camera.camera2.pipe.integration.testing.FakeCameraGraphSession.RequestStatus.TOTAL_CAPTURE_DONE
@@ -169,19 +170,26 @@
         request: Request,
         status: RequestStatus
     ) {
+        val requestMetadata = FakeRequestMetadata(request = request)
         last().listeners.forEach { listener ->
             when (status) {
                 TOTAL_CAPTURE_DONE -> listener.onTotalCaptureResult(
-                    FakeRequestMetadata(request = request), FrameNumber(0), FakeFrameInfo()
+                    requestMetadata, FrameNumber(0), FakeFrameInfo()
                 )
 
-                FAILED -> @Suppress("DEPRECATION") listener.onFailed(
-                    FakeRequestMetadata(request = request), FrameNumber(0), getFakeCaptureFailure()
+                FAILED -> listener.onFailed(
+                    requestMetadata,
+                    FrameNumber(0),
+                    FakeCaptureFailure(
+                        requestMetadata,
+                        false,
+                        FrameNumber(0),
+                        CaptureFailure.REASON_ERROR,
+                        null
+                    )
                 )
 
-                ABORTED -> listener.onRequestSequenceAborted(
-                    FakeRequestMetadata(request = request)
-                )
+                ABORTED -> listener.onRequestSequenceAborted(requestMetadata)
             }
         }
     }
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeUseCaseCamera.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeUseCaseCamera.kt
index 5fd0c64..0af1e6c 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeUseCaseCamera.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeUseCaseCamera.kt
@@ -23,23 +23,22 @@
 import androidx.annotation.RequiresApi
 import androidx.camera.camera2.pipe.AeMode
 import androidx.camera.camera2.pipe.CameraGraph
+import androidx.camera.camera2.pipe.CameraStream
 import androidx.camera.camera2.pipe.Lock3ABehavior
 import androidx.camera.camera2.pipe.Request
 import androidx.camera.camera2.pipe.RequestTemplate
 import androidx.camera.camera2.pipe.Result3A
 import androidx.camera.camera2.pipe.StreamId
 import androidx.camera.camera2.pipe.integration.adapter.CameraStateAdapter
-import androidx.camera.camera2.pipe.integration.compat.StreamConfigurationMapCompat
-import androidx.camera.camera2.pipe.integration.compat.quirk.CameraQuirks
-import androidx.camera.camera2.pipe.integration.compat.workaround.OutputSizesCorrector
+import androidx.camera.camera2.pipe.integration.adapter.SessionConfigAdapter
 import androidx.camera.camera2.pipe.integration.config.UseCaseCameraComponent
 import androidx.camera.camera2.pipe.integration.config.UseCaseCameraConfig
 import androidx.camera.camera2.pipe.integration.impl.UseCaseCamera
 import androidx.camera.camera2.pipe.integration.impl.UseCaseCameraRequestControl
-import androidx.camera.camera2.pipe.testing.FakeCameraMetadata
 import androidx.camera.core.UseCase
 import androidx.camera.core.impl.CaptureConfig
 import androidx.camera.core.impl.Config
+import androidx.camera.core.impl.DeferrableSurface
 import java.util.concurrent.TimeUnit
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.Deferred
@@ -48,13 +47,13 @@
 
 class FakeUseCaseCameraComponentBuilder : UseCaseCameraComponent.Builder {
     var buildInvocationCount = 0
+    private var sessionConfigAdapter = SessionConfigAdapter(emptyList())
+    private var cameraGraph = FakeCameraGraph()
+    private var streamConfigMap = mutableMapOf<CameraStream.Config, DeferrableSurface>()
 
-    private val cameraQuirks = CameraQuirks(
-        FakeCameraMetadata(),
-        StreamConfigurationMapCompat(null, OutputSizesCorrector(FakeCameraMetadata(), null))
-    )
     private var config: UseCaseCameraConfig =
-        UseCaseCameraConfig(emptyList(), CameraStateAdapter(), cameraQuirks, CameraGraph.Flags())
+        UseCaseCameraConfig(emptyList(), sessionConfigAdapter, CameraStateAdapter(), cameraGraph,
+            streamConfigMap)
 
     override fun config(config: UseCaseCameraConfig): UseCaseCameraComponent.Builder {
         this.config = config
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraGraphSimulator.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraGraphSimulator.kt
index ba087f3..2f68f45 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraGraphSimulator.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraGraphSimulator.kt
@@ -33,7 +33,7 @@
 import androidx.camera.camera2.pipe.GraphState.GraphStateError
 import androidx.camera.camera2.pipe.Metadata
 import androidx.camera.camera2.pipe.Request
-import androidx.camera.camera2.pipe.RequestFailureWrapper
+import androidx.camera.camera2.pipe.RequestFailure
 import androidx.camera.camera2.pipe.StreamId
 import kotlinx.atomicfu.atomic
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -283,9 +283,9 @@
             }
         }
 
-        fun simulateFailure(requestFailureWrapper: RequestFailureWrapper) {
+        fun simulateFailure(requestFailure: RequestFailure) {
             requestSequence.invokeOnRequest(requestMetadata) {
-                it.onFailed(requestMetadata, frameNumber, requestFailureWrapper)
+                it.onFailed(requestMetadata, frameNumber, requestFailure)
             }
         }
 
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCameraDevices.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCameraDevices.kt
index 26fcc47..171f072 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCameraDevices.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCameraDevices.kt
@@ -28,6 +28,7 @@
 @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
 class FakeCameraDevices(
     private val defaultCameraBackendId: CameraBackendId,
+    private val concurrentCameraBackendIds: Set<Set<CameraBackendId>>,
     private val cameraMetadataMap: Map<CameraBackendId, List<CameraMetadata>>
 ) : CameraDevices {
     init {
@@ -46,12 +47,14 @@
 
     override suspend fun getConcurrentCameraIds(
         cameraBackendId: CameraBackendId?
-    ): Set<Set<CameraId>> {
-        return emptySet()
-    }
+    ): Set<Set<CameraId>> = awaitConcurrentCameraIds(cameraBackendId)
 
     override fun awaitConcurrentCameraIds(cameraBackendId: CameraBackendId?): Set<Set<CameraId>> {
-        return emptySet()
+        return concurrentCameraBackendIds.map { concurrentCameraIds ->
+            concurrentCameraIds.map {
+                    cameraId -> CameraId.fromCamera2Id(cameraId.value)
+            }.toSet()
+        }.toSet()
     }
 
     override suspend fun getCameraMetadata(
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeRequestListener.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeRequestListener.kt
index e097dc3..41b1c0b 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeRequestListener.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeRequestListener.kt
@@ -18,13 +18,13 @@
 
 package androidx.camera.camera2.pipe.testing
 
-import android.hardware.camera2.CaptureFailure
 import androidx.annotation.RequiresApi
 import androidx.camera.camera2.pipe.CameraTimestamp
 import androidx.camera.camera2.pipe.FrameInfo
 import androidx.camera.camera2.pipe.FrameMetadata
 import androidx.camera.camera2.pipe.FrameNumber
 import androidx.camera.camera2.pipe.Request
+import androidx.camera.camera2.pipe.RequestFailure
 import androidx.camera.camera2.pipe.RequestMetadata
 import androidx.camera.camera2.pipe.StreamId
 import kotlinx.coroutines.flow.MutableSharedFlow
@@ -148,18 +148,13 @@
             "($replayBuffer) may need to be increased."
     }
 
-    @Deprecated(
-        message = "Migrating to using RequestFailureWrapper instead of CaptureFailure",
-        level = DeprecationLevel.WARNING
-    )
     override fun onFailed(
         requestMetadata: RequestMetadata,
         frameNumber: FrameNumber,
-        captureFailure: CaptureFailure
+        requestFailure: RequestFailure
     ) = check(
         _onFailedFlow.tryEmit(
-            @Suppress("SyntheticAccessor")
-            OnFailed(requestMetadata, frameNumber, captureFailure)
+            OnFailed(requestMetadata, frameNumber, requestFailure)
         )
     ) {
         "Failed to emit OnFailed event! The size of the replay buffer" +
@@ -205,5 +200,5 @@
 class OnFailed(
     val requestMetadata: RequestMetadata,
     val frameNumber: FrameNumber,
-    val captureFailure: CaptureFailure
+    val requestFailure: RequestFailure
 ) : RequestListenerEvent()
diff --git a/camera/camera-camera2-pipe-testing/src/test/java/androidx/camera/camera2/pipe/testing/FakeCameraDevicesTest.kt b/camera/camera-camera2-pipe-testing/src/test/java/androidx/camera/camera2/pipe/testing/FakeCameraDevicesTest.kt
index d76f46d..5b52c5f 100644
--- a/camera/camera-camera2-pipe-testing/src/test/java/androidx/camera/camera2/pipe/testing/FakeCameraDevicesTest.kt
+++ b/camera/camera-camera2-pipe-testing/src/test/java/androidx/camera/camera2/pipe/testing/FakeCameraDevicesTest.kt
@@ -51,6 +51,10 @@
     fun getCameraIdsReturnsDefaultCameraIdList() = runTest {
         val cameraDevices = FakeCameraDevices(
             defaultCameraBackendId = FAKE_CAMERA_BACKEND_ID,
+            concurrentCameraBackendIds = setOf(
+                setOf(CameraBackendId("0"), CameraBackendId("1")),
+                setOf(CameraBackendId("0"), CameraBackendId("2"))
+            ),
             cameraMetadataMap = cameraMetadataMap
         )
         val devices = cameraDevices.getCameraIds()
@@ -69,6 +73,10 @@
     fun getCameraIdsWithBackendReturnsCustomCameraIdList() = runTest {
         val cameraDevices = FakeCameraDevices(
             defaultCameraBackendId = FAKE_CAMERA_BACKEND_ID,
+            concurrentCameraBackendIds = setOf(
+                setOf(CameraBackendId("0"), CameraBackendId("1")),
+                setOf(CameraBackendId("0"), CameraBackendId("2"))
+            ),
             cameraMetadataMap = cameraMetadataMap
         )
         val devices = cameraDevices.getCameraIds(EXTERNAL_BACKEND_ID)
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraph.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraph.kt
index 581aeac..3c81b44 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraph.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraph.kt
@@ -22,6 +22,7 @@
 import android.hardware.camera2.CaptureRequest
 import android.hardware.camera2.params.MeteringRectangle
 import android.hardware.camera2.params.SessionConfiguration
+import android.os.Build
 import android.view.Surface
 import androidx.annotation.RequiresApi
 import androidx.annotation.RestrictTo
@@ -151,7 +152,29 @@
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     data class Flags(
         val configureBlankSessionOnStop: Boolean = false,
-        val abortCapturesOnStop: Boolean = false,
+
+        /**
+         * When creating a new capture session, the camera framework waits for all the inflight
+         * capture requests from the prior session before creating the new session. Calling
+         * abortCaptures() triggers an explicit flush on the camera HAL side. Therefore, aborting
+         * the captures allows us to switch to a new capture session sooner (see the referenced bug
+         * for more info).
+         *
+         * However, there might be cases where we might not want to trigger the flush. For example,
+         * if we're recording a video, we may not want the video recording to be disrupted too
+         * early. Hence, this flag is provided so that we can override this behavior.
+         *
+         * Ideally we should be able to invoke abortCaptures() every time during close. However,
+         * improper flush implementations, which seem to occur largely on older devices, have shown
+         * to cause irregular behaviors, such as NPEs (b/139448807), capture session entering
+         * abnormal states (b/162314023), and (potentially) camera device close stalling based on
+         * testing, etc. Hence, we're enabling this behavior by default on API level >= R (30) for
+         * now.
+         *
+         * - Bug(s): b/287020251
+         * - API levels: R (30) and above
+         */
+        val abortCapturesOnStop: Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R,
 
         /**
          * A quirk that waits for the last repeating capture request to start before stopping the
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Requests.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Requests.kt
index 1c98dad..4ba4278 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Requests.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Requests.kt
@@ -142,7 +142,7 @@
         }
 
         @Deprecated(
-            message = "Migrating to using RequestFailureWrapper instead of CaptureFailure",
+            message = "Migrating to using RequestFailure instead of CaptureFailure",
             level = DeprecationLevel.WARNING
         )
         fun onFailed(
@@ -160,13 +160,13 @@
          *
          * @param requestMetadata the data about the camera2 request that was sent to the camera.
          * @param frameNumber the android frame number for this exposure
-         * @param requestFailureWrapper the android [RequestFailureWrapper] data wrapper
+         * @param requestFailure the android [RequestFailure] data wrapper
          * @see android.hardware.camera2.CameraCaptureSession.CaptureCallback.onCaptureFailed
          */
         fun onFailed(
             requestMetadata: RequestMetadata,
             frameNumber: FrameNumber,
-            requestFailureWrapper: RequestFailureWrapper
+            requestFailure: RequestFailure
         ) {
         }
 
@@ -256,7 +256,7 @@
  * constructor prevents directly creating an instance of it.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-interface RequestFailureWrapper {
+interface RequestFailure {
     val requestMetadata: RequestMetadata
 
     val frameNumber: FrameNumber
@@ -264,6 +264,8 @@
     val reason: Int
 
     val wasImageCaptured: Boolean
+
+    val captureFailure: CaptureFailure?
 }
 
 /**
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/AndroidCaptureFailure.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/AndroidCaptureFailure.kt
index e0c1a2c..c2d8842 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/AndroidCaptureFailure.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/AndroidCaptureFailure.kt
@@ -22,17 +22,28 @@
 import android.os.Build
 import androidx.annotation.RequiresApi
 import androidx.camera.camera2.pipe.FrameNumber
-import androidx.camera.camera2.pipe.RequestFailureWrapper
+import androidx.camera.camera2.pipe.RequestFailure
 import androidx.camera.camera2.pipe.RequestMetadata
+import androidx.camera.camera2.pipe.UnsafeWrapper
+import kotlin.reflect.KClass
 
 /**
- * This class implements the [RequestFailureWrapper] interface to create a
- * CaptureFailure object that can be used instead of the package-private [CaptureFailure]
+ * This class implements the [RequestFailure] interface by passing the package-private
+ * [CaptureFailure] object.
  */
 @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
 internal class AndroidCaptureFailure(
     override val requestMetadata: RequestMetadata,
-    override val wasImageCaptured: Boolean,
-    override val frameNumber: FrameNumber,
-    override val reason: Int
-) : RequestFailureWrapper
+    override val captureFailure: CaptureFailure
+) : RequestFailure, UnsafeWrapper {
+    override val frameNumber: FrameNumber = FrameNumber(captureFailure.frameNumber)
+    override val reason: Int = captureFailure.reason
+    override val wasImageCaptured: Boolean = captureFailure.wasImageCaptured()
+
+    @Suppress("UNCHECKED_CAST")
+    override fun <T : Any> unwrapAs(type: KClass<T>): T? =
+        when (type) {
+            CaptureFailure::class -> captureFailure as T?
+            else -> null
+        }
+}
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CaptureSequence.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CaptureSequence.kt
index f0ad932a..1efc937 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CaptureSequence.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CaptureSequence.kt
@@ -30,6 +30,7 @@
 import androidx.camera.camera2.pipe.CaptureSequences.invokeOnRequests
 import androidx.camera.camera2.pipe.FrameNumber
 import androidx.camera.camera2.pipe.Request
+import androidx.camera.camera2.pipe.RequestFailure
 import androidx.camera.camera2.pipe.RequestMetadata
 import androidx.camera.camera2.pipe.RequestNumber
 import androidx.camera.camera2.pipe.StreamId
@@ -154,42 +155,56 @@
         invokeOnRequest(request) { it.onComplete(request, frameNumber, frameInfo) }
     }
 
-    @Deprecated(
-        message = "Migrating to using RequestFailureWrapper instead of CaptureFailure",
-        level = DeprecationLevel.WARNING,
-        replaceWith = ReplaceWith("onFailed")
-    )
     override fun onCaptureFailed(
         captureSession: CameraCaptureSession,
         captureRequest: CaptureRequest,
         captureFailure: CaptureFailure
-    ) = onCaptureFailed(
-        captureRequest,
-        FrameNumber(captureFailure.frameNumber)
-    )
-
-    override fun onCaptureFailed(
-        captureRequest: CaptureRequest,
-        frameNumber: FrameNumber
     ) {
-        sequenceListener.onCaptureSequenceComplete(this)
-
         val requestNumber = readRequestNumber(captureRequest)
 
         // Load the request and throw if we are not able to find an associated request. Under
         // normal circumstances this should never happen.
         val request = readRequestMetadata(requestNumber)
 
-        val androidCaptureFailure = AndroidCaptureFailure(
+        val androidCaptureFailure = AndroidCaptureFailure(request, captureFailure)
+
+        invokeCaptureFailure(
+            request,
+            FrameNumber(captureFailure.frameNumber),
+            androidCaptureFailure
+        )
+    }
+
+    private fun invokeCaptureFailure(
+        request: RequestMetadata,
+        frameNumber: FrameNumber,
+        requestFailure: RequestFailure
+    ) {
+        sequenceListener.onCaptureSequenceComplete(this)
+        invokeOnRequest(request) {
+            it.onFailed(request, frameNumber, requestFailure)
+        }
+    }
+
+    override fun onCaptureFailed(
+        captureRequest: CaptureRequest,
+        frameNumber: FrameNumber
+    ) {
+        val requestNumber = readRequestNumber(captureRequest)
+
+        // Load the request and throw if we are not able to find an associated request. Under
+        // normal circumstances this should never happen.
+        val request = readRequestMetadata(requestNumber)
+
+        val simpleCaptureFailure = SimpleCaptureFailure(
             request,
             false,
             frameNumber,
-            CaptureFailure.REASON_ERROR
+            CaptureFailure.REASON_ERROR,
+            null
         )
 
-        invokeOnRequest(request) {
-            it.onFailed(request, frameNumber, androidCaptureFailure)
-        }
+        invokeCaptureFailure(request, frameNumber, simpleCaptureFailure)
     }
 
     override fun onCaptureBufferLost(
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CaptureSequenceProcessor.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CaptureSequenceProcessor.kt
index 212b808..f94eb82 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CaptureSequenceProcessor.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CaptureSequenceProcessor.kt
@@ -291,12 +291,12 @@
     }
 
     override fun abortCaptures(): Unit = synchronized(lock) {
-        if (closed) return
+        Log.debug { "$this#abortCaptures" }
         session.abortCaptures()
     }
 
     override fun stopRepeating(): Unit = synchronized(lock) {
-        if (closed) return
+        Log.debug { "$this#stopRepeating" }
         session.stopRepeating()
     }
 
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2Quirks.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2Quirks.kt
index c9ad6e2..bb36212 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2Quirks.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2Quirks.kt
@@ -85,4 +85,26 @@
             CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL]
         return level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
     }
+
+    companion object {
+        private val SHOULD_WAIT_FOR_REPEATING_DEVICE_MAP =
+            mapOf(
+                "Google" to setOf("oriole", "raven", "bluejay", "panther", "cheetah", "lynx"),
+            )
+
+        /**
+         * A quirk that waits for a certain number of repeating requests to complete before allowing
+         * (single) capture requests to be issued. This is needed on some devices where issuing a
+         * capture request too early might cause it to fail prematurely.
+         *
+         * - Bug(s): b/287020251, b/289284907
+         * - Device(s): See [SHOULD_WAIT_FOR_REPEATING_DEVICE_MAP]
+         * - API levels: Before 34 (U)
+         */
+        internal fun shouldWaitForRepeatingBeforeCapture(): Boolean {
+            return SHOULD_WAIT_FOR_REPEATING_DEVICE_MAP[
+                Build.MANUFACTURER]?.contains(Build.DEVICE) == true &&
+                Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE
+        }
+    }
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionState.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionState.kt
index 7a07920..9d21ba8 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionState.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionState.kt
@@ -248,7 +248,7 @@
      * a closed state. This will not cancel repeating requests or abort captures.
      */
     fun disconnect() {
-        shutdown(abortAndStopRepeating = false)
+        shutdown(abortAndStopRepeating = cameraGraphFlags.abortCapturesOnStop)
     }
 
     /**
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Exceptions.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Exceptions.kt
index 3cf8857..8a0adf7 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Exceptions.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Exceptions.kt
@@ -22,6 +22,7 @@
 import androidx.annotation.RequiresApi
 import androidx.camera.camera2.pipe.CameraError
 import androidx.camera.camera2.pipe.CameraId
+import androidx.camera.camera2.pipe.core.Log
 import androidx.camera.camera2.pipe.internal.CameraErrorListener
 
 /**
@@ -55,6 +56,7 @@
     try {
         return block()
     } catch (e: Exception) {
+        Log.warn { "Unexpected error: " + e.message }
         when (e) {
             is IllegalArgumentException,
             is IllegalStateException,
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExtensionSessionWrapper.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExtensionSessionWrapper.kt
index 2d69b39..1e04f26 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExtensionSessionWrapper.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExtensionSessionWrapper.kt
@@ -22,6 +22,7 @@
 import android.hardware.camera2.CameraExtensionSession
 import android.hardware.camera2.CaptureRequest
 import android.hardware.camera2.TotalCaptureResult
+import android.os.Build
 import android.view.Surface
 import androidx.annotation.RequiresApi
 import androidx.camera.camera2.pipe.FrameNumber
@@ -145,36 +146,56 @@
 ) : CameraExtensionSessionWrapper {
 
     private val frameNumbers: AtomicLong = atomic(0L)
-    private val frameNumbersMap: MutableMap<CameraExtensionSession, Long> = HashMap()
+    private val extensionSessionMap: MutableMap<CameraExtensionSession, Long> = HashMap()
 
     override fun capture(
         request: CaptureRequest,
         listener: CameraCaptureSession.CaptureCallback
     ): Int? = catchAndReportCameraExceptions(device.cameraId, cameraErrorListener) {
-        val frameQueue = LinkedList<Long>()
-        cameraExtensionSession.capture(
-            request,
-            callbackExecutor,
-            Camera2CaptureSessionCallbackToExtensionCaptureCallback(
-                listener as Camera2CaptureCallback,
-                frameQueue
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+            cameraExtensionSession.capture(
+                request,
+                callbackExecutor,
+                Camera2CaptureSessionCallbackToExtensionCaptureCallback(
+                    listener as Camera2CaptureCallback,
+                    LinkedList()
+                )
             )
-        )
+        } else {
+            cameraExtensionSession.capture(
+                request,
+                callbackExecutor,
+                Camera2CaptureSessionCallbackToExtensionCaptureCallbackAndroidS(
+                    listener as Camera2CaptureCallback,
+                    mutableMapOf()
+                )
+            )
+        }
     }
 
     override fun setRepeatingRequest(
         request: CaptureRequest,
         listener: CameraCaptureSession.CaptureCallback,
     ): Int? = catchAndReportCameraExceptions(device.cameraId, cameraErrorListener) {
-        val frameQueue = LinkedList<Long>()
-        cameraExtensionSession.setRepeatingRequest(
-            request,
-            callbackExecutor,
-            Camera2CaptureSessionCallbackToExtensionCaptureCallback(
-                listener as Camera2CaptureCallback,
-                frameQueue
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+            cameraExtensionSession.setRepeatingRequest(
+                request,
+                callbackExecutor,
+                Camera2CaptureSessionCallbackToExtensionCaptureCallback(
+                    listener as Camera2CaptureCallback,
+                    LinkedList()
+                )
             )
-        )
+        } else {
+            cameraExtensionSession.setRepeatingRequest(
+                request,
+                callbackExecutor,
+                Camera2CaptureSessionCallbackToExtensionCaptureCallbackAndroidS(
+                    listener as Camera2CaptureCallback,
+                    mutableMapOf()
+                )
+            )
+        }
     }
 
     override fun stopRepeating(): Boolean =
@@ -238,7 +259,7 @@
             timestamp: Long
         ) {
             val frameNumber = frameNumbers.incrementAndGet()
-            frameNumbersMap[session] = frameNumber
+            extensionSessionMap[session] = frameNumber
             frameQueue.add(frameNumber)
             captureCallback.onCaptureStarted(
                 request,
@@ -262,7 +283,7 @@
         }
 
         override fun onCaptureSequenceCompleted(session: CameraExtensionSession, sequenceId: Int) {
-            val frameNumber = frameNumbersMap[session]
+            val frameNumber = extensionSessionMap[session]
             captureCallback.onCaptureSequenceCompleted(sequenceId, frameNumber!!)
         }
 
@@ -275,8 +296,67 @@
             request: CaptureRequest,
             result: TotalCaptureResult
         ) {
+            Log.info { "Extension session listener's onComplete requires Android T or higher." }
             val frameNumber = frameQueue.remove()
             captureCallback.onCaptureCompleted(request, result, FrameNumber(frameNumber))
         }
     }
+
+    /**
+     * [CameraExtensionSession.ExtensionCaptureCallback]'s onCaptureResultAvailable is gated
+     * behind Android T, so any devices on Android S or older will not see onCaptureResultAvailable
+     * being triggered. This implementation calls onCaptureCompleted in onCaptureStarted and does
+     * not keep track of completed or failed frames for repeating requests.
+     */
+    inner class Camera2CaptureSessionCallbackToExtensionCaptureCallbackAndroidS(
+        private val captureCallback: Camera2CaptureCallback,
+        private val captureRequestMap: MutableMap<CaptureRequest, MutableList<Long>>
+
+    ) : CameraExtensionSession.ExtensionCaptureCallback() {
+
+        override fun onCaptureStarted(
+            session: CameraExtensionSession,
+            request: CaptureRequest,
+            timestamp: Long
+        ) {
+            val frameNumber = frameNumbers.incrementAndGet()
+            extensionSessionMap[session] = frameNumber
+            captureRequestMap.getOrPut(request) { mutableListOf() }.add(frameNumber)
+            captureCallback.onCaptureStarted(
+                request,
+                frameNumber,
+                timestamp
+            )
+        }
+
+        override fun onCaptureProcessStarted(
+            session: CameraExtensionSession,
+            request: CaptureRequest
+        ) {
+        }
+
+        override fun onCaptureFailed(session: CameraExtensionSession, request: CaptureRequest) {
+            if (captureRequestMap[request]!!.size == 1) {
+                val frameNumber = captureRequestMap[request]!![0]
+                captureCallback.onCaptureFailed(
+                    request,
+                    FrameNumber(frameNumber)
+                )
+            } else {
+                Log.info {
+                    "onCaptureFailed is not triggered for repeating requests. Request " +
+                        "frame numbers: " + captureRequestMap[request]!!.stream()
+                }
+            }
+        }
+
+        override fun onCaptureSequenceCompleted(session: CameraExtensionSession, sequenceId: Int) {
+            val frameNumber = extensionSessionMap[session]
+            captureCallback.onCaptureSequenceCompleted(sequenceId, frameNumber!!)
+        }
+
+        override fun onCaptureSequenceAborted(session: CameraExtensionSession, sequenceId: Int) {
+            captureCallback.onCaptureSequenceAborted(sequenceId)
+        }
+    }
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/SimpleCaptureFailure.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/SimpleCaptureFailure.kt
new file mode 100644
index 0000000..2d43ff8
--- /dev/null
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/SimpleCaptureFailure.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+
+package androidx.camera.camera2.pipe.compat
+
+import android.hardware.camera2.CaptureFailure
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.FrameNumber
+import androidx.camera.camera2.pipe.RequestFailure
+import androidx.camera.camera2.pipe.RequestMetadata
+
+/**
+ * This class implements the [RequestFailure] interface by extracting the fields of
+ * the package-private [CaptureFailure] object.
+ */
+@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+internal data class SimpleCaptureFailure(
+    override val requestMetadata: RequestMetadata,
+    override val wasImageCaptured: Boolean,
+    override val frameNumber: FrameNumber,
+    override val reason: Int,
+    override val captureFailure: CaptureFailure?
+) : RequestFailure
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/GraphProcessor.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/GraphProcessor.kt
index f000f4f..fdb24f2 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/GraphProcessor.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/GraphProcessor.kt
@@ -22,6 +22,8 @@
 import androidx.annotation.RequiresApi
 import androidx.camera.camera2.pipe.CameraGraph
 import androidx.camera.camera2.pipe.CaptureSequenceProcessor
+import androidx.camera.camera2.pipe.FrameInfo
+import androidx.camera.camera2.pipe.FrameNumber
 import androidx.camera.camera2.pipe.GraphState
 import androidx.camera.camera2.pipe.GraphState.GraphStateError
 import androidx.camera.camera2.pipe.GraphState.GraphStateStarted
@@ -29,6 +31,8 @@
 import androidx.camera.camera2.pipe.GraphState.GraphStateStopped
 import androidx.camera.camera2.pipe.GraphState.GraphStateStopping
 import androidx.camera.camera2.pipe.Request
+import androidx.camera.camera2.pipe.RequestMetadata
+import androidx.camera.camera2.pipe.compat.Camera2Quirks
 import androidx.camera.camera2.pipe.config.CameraGraphScope
 import androidx.camera.camera2.pipe.config.ForCameraGraph
 import androidx.camera.camera2.pipe.core.Debug
@@ -37,6 +41,8 @@
 import androidx.camera.camera2.pipe.core.Threads
 import androidx.camera.camera2.pipe.formatForLogs
 import androidx.camera.camera2.pipe.putAllMetadata
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
 import javax.inject.Inject
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.CoroutineScope
@@ -155,6 +161,27 @@
     @GuardedBy("lock")
     private var pendingParametersDeferred: CompletableDeferred<Boolean>? = null
 
+    // On some devices, we need to wait for 10 frames to complete before we can guarantee the
+    // success of single capture requests. This is a quirk identified as part of b/287020251 and
+    // reported in b/289284907.
+    private var repeatingRequestsCompleted = CountDownLatch(10)
+
+    // Graph listener added to repeating requests in order to handle the aforementioned quirk.
+    private val graphProcessorRepeatingListeners =
+        if (!Camera2Quirks.shouldWaitForRepeatingBeforeCapture()) {
+            graphListeners
+        } else {
+            graphListeners + object : Request.Listener {
+                override fun onComplete(
+                    requestMetadata: RequestMetadata,
+                    frameNumber: FrameNumber,
+                    result: FrameInfo
+                ) {
+                    repeatingRequestsCompleted.countDown()
+                }
+            }
+        }
+
     private val _graphState = MutableStateFlow<GraphState>(GraphStateStopped)
 
     override val graphState: StateFlow<GraphState>
@@ -452,7 +479,7 @@
                         requests = listOf(request),
                         defaultParameters = cameraGraphConfig.defaultParameters,
                         requiredParameters = requiredParameters,
-                        listeners = graphListeners
+                        listeners = graphProcessorRepeatingListeners,
                     )
                 ) {
                     // ONLY update the current repeating request if the update succeeds
@@ -502,6 +529,15 @@
     }
 
     private fun submitLoop() {
+        if (Camera2Quirks.shouldWaitForRepeatingBeforeCapture() && hasRepeatingRequest()) {
+            debug {
+                "Quirk: Waiting for 10 repeating requests to complete before submitting requests"
+            }
+            if (!repeatingRequestsCompleted.await(2, TimeUnit.SECONDS)) {
+                warn { "Failed to wait for 10 repeating requests to complete after 5 seconds" }
+            }
+        }
+
         var burst: List<Request>
         var processor: GraphRequestProcessor
 
diff --git a/camera/camera-camera2/api/1.3.0-beta02.txt b/camera/camera-camera2/api/1.3.0-beta02.txt
new file mode 100644
index 0000000..87c79d0
--- /dev/null
+++ b/camera/camera-camera2/api/1.3.0-beta02.txt
@@ -0,0 +1,54 @@
+// Signature format: 4.0
+package androidx.camera.camera2 {
+
+  @RequiresApi(21) public final class Camera2Config {
+    method public static androidx.camera.core.CameraXConfig defaultConfig();
+  }
+
+}
+
+package androidx.camera.camera2.interop {
+
+  @SuppressCompatibility @RequiresApi(21) @androidx.camera.camera2.interop.ExperimentalCamera2Interop public final class Camera2CameraControl {
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> addCaptureRequestOptions(androidx.camera.camera2.interop.CaptureRequestOptions);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> clearCaptureRequestOptions();
+    method public static androidx.camera.camera2.interop.Camera2CameraControl from(androidx.camera.core.CameraControl);
+    method public androidx.camera.camera2.interop.CaptureRequestOptions getCaptureRequestOptions();
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setCaptureRequestOptions(androidx.camera.camera2.interop.CaptureRequestOptions);
+  }
+
+  @SuppressCompatibility @RequiresApi(21) @androidx.camera.camera2.interop.ExperimentalCamera2Interop public final class Camera2CameraInfo {
+    method public static androidx.camera.camera2.interop.Camera2CameraInfo from(androidx.camera.core.CameraInfo);
+    method public <T> T? getCameraCharacteristic(android.hardware.camera2.CameraCharacteristics.Key<T!>);
+    method public String getCameraId();
+  }
+
+  @SuppressCompatibility @RequiresApi(21) @androidx.camera.camera2.interop.ExperimentalCamera2Interop public final class Camera2Interop {
+  }
+
+  @RequiresApi(21) public static final class Camera2Interop.Extender<T> {
+    ctor public Camera2Interop.Extender(androidx.camera.core.ExtendableBuilder<T!>);
+    method public <ValueT> androidx.camera.camera2.interop.Camera2Interop.Extender<T!> setCaptureRequestOption(android.hardware.camera2.CaptureRequest.Key<ValueT!>, ValueT);
+    method public androidx.camera.camera2.interop.Camera2Interop.Extender<T!> setDeviceStateCallback(android.hardware.camera2.CameraDevice.StateCallback);
+    method @RequiresApi(28) public androidx.camera.camera2.interop.Camera2Interop.Extender<T!> setPhysicalCameraId(String);
+    method public androidx.camera.camera2.interop.Camera2Interop.Extender<T!> setSessionCaptureCallback(android.hardware.camera2.CameraCaptureSession.CaptureCallback);
+    method public androidx.camera.camera2.interop.Camera2Interop.Extender<T!> setSessionStateCallback(android.hardware.camera2.CameraCaptureSession.StateCallback);
+    method @RequiresApi(33) public androidx.camera.camera2.interop.Camera2Interop.Extender<T!> setStreamUseCase(long);
+  }
+
+  @SuppressCompatibility @RequiresApi(21) @androidx.camera.camera2.interop.ExperimentalCamera2Interop public class CaptureRequestOptions {
+    method public <ValueT> ValueT? getCaptureRequestOption(android.hardware.camera2.CaptureRequest.Key<ValueT!>);
+  }
+
+  @RequiresApi(21) public static final class CaptureRequestOptions.Builder implements androidx.camera.core.ExtendableBuilder<androidx.camera.camera2.interop.CaptureRequestOptions> {
+    ctor public CaptureRequestOptions.Builder();
+    method public androidx.camera.camera2.interop.CaptureRequestOptions build();
+    method public <ValueT> androidx.camera.camera2.interop.CaptureRequestOptions.Builder clearCaptureRequestOption(android.hardware.camera2.CaptureRequest.Key<ValueT!>);
+    method public <ValueT> androidx.camera.camera2.interop.CaptureRequestOptions.Builder setCaptureRequestOption(android.hardware.camera2.CaptureRequest.Key<ValueT!>, ValueT);
+  }
+
+  @SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalCamera2Interop {
+  }
+
+}
+
diff --git a/camera/camera-camera2/api/res-1.3.0-beta02.txt b/camera/camera-camera2/api/res-1.3.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/camera/camera-camera2/api/res-1.3.0-beta02.txt
diff --git a/camera/camera-camera2/api/restricted_1.3.0-beta02.txt b/camera/camera-camera2/api/restricted_1.3.0-beta02.txt
new file mode 100644
index 0000000..87c79d0
--- /dev/null
+++ b/camera/camera-camera2/api/restricted_1.3.0-beta02.txt
@@ -0,0 +1,54 @@
+// Signature format: 4.0
+package androidx.camera.camera2 {
+
+  @RequiresApi(21) public final class Camera2Config {
+    method public static androidx.camera.core.CameraXConfig defaultConfig();
+  }
+
+}
+
+package androidx.camera.camera2.interop {
+
+  @SuppressCompatibility @RequiresApi(21) @androidx.camera.camera2.interop.ExperimentalCamera2Interop public final class Camera2CameraControl {
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> addCaptureRequestOptions(androidx.camera.camera2.interop.CaptureRequestOptions);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> clearCaptureRequestOptions();
+    method public static androidx.camera.camera2.interop.Camera2CameraControl from(androidx.camera.core.CameraControl);
+    method public androidx.camera.camera2.interop.CaptureRequestOptions getCaptureRequestOptions();
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setCaptureRequestOptions(androidx.camera.camera2.interop.CaptureRequestOptions);
+  }
+
+  @SuppressCompatibility @RequiresApi(21) @androidx.camera.camera2.interop.ExperimentalCamera2Interop public final class Camera2CameraInfo {
+    method public static androidx.camera.camera2.interop.Camera2CameraInfo from(androidx.camera.core.CameraInfo);
+    method public <T> T? getCameraCharacteristic(android.hardware.camera2.CameraCharacteristics.Key<T!>);
+    method public String getCameraId();
+  }
+
+  @SuppressCompatibility @RequiresApi(21) @androidx.camera.camera2.interop.ExperimentalCamera2Interop public final class Camera2Interop {
+  }
+
+  @RequiresApi(21) public static final class Camera2Interop.Extender<T> {
+    ctor public Camera2Interop.Extender(androidx.camera.core.ExtendableBuilder<T!>);
+    method public <ValueT> androidx.camera.camera2.interop.Camera2Interop.Extender<T!> setCaptureRequestOption(android.hardware.camera2.CaptureRequest.Key<ValueT!>, ValueT);
+    method public androidx.camera.camera2.interop.Camera2Interop.Extender<T!> setDeviceStateCallback(android.hardware.camera2.CameraDevice.StateCallback);
+    method @RequiresApi(28) public androidx.camera.camera2.interop.Camera2Interop.Extender<T!> setPhysicalCameraId(String);
+    method public androidx.camera.camera2.interop.Camera2Interop.Extender<T!> setSessionCaptureCallback(android.hardware.camera2.CameraCaptureSession.CaptureCallback);
+    method public androidx.camera.camera2.interop.Camera2Interop.Extender<T!> setSessionStateCallback(android.hardware.camera2.CameraCaptureSession.StateCallback);
+    method @RequiresApi(33) public androidx.camera.camera2.interop.Camera2Interop.Extender<T!> setStreamUseCase(long);
+  }
+
+  @SuppressCompatibility @RequiresApi(21) @androidx.camera.camera2.interop.ExperimentalCamera2Interop public class CaptureRequestOptions {
+    method public <ValueT> ValueT? getCaptureRequestOption(android.hardware.camera2.CaptureRequest.Key<ValueT!>);
+  }
+
+  @RequiresApi(21) public static final class CaptureRequestOptions.Builder implements androidx.camera.core.ExtendableBuilder<androidx.camera.camera2.interop.CaptureRequestOptions> {
+    ctor public CaptureRequestOptions.Builder();
+    method public androidx.camera.camera2.interop.CaptureRequestOptions build();
+    method public <ValueT> androidx.camera.camera2.interop.CaptureRequestOptions.Builder clearCaptureRequestOption(android.hardware.camera2.CaptureRequest.Key<ValueT!>);
+    method public <ValueT> androidx.camera.camera2.interop.CaptureRequestOptions.Builder setCaptureRequestOption(android.hardware.camera2.CaptureRequest.Key<ValueT!>, ValueT);
+  }
+
+  @SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalCamera2Interop {
+  }
+
+}
+
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/concurrent/Camera2CameraCoordinator.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/concurrent/Camera2CameraCoordinator.java
index 16d20ce..e6680bf 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/concurrent/Camera2CameraCoordinator.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/concurrent/Camera2CameraCoordinator.java
@@ -140,6 +140,15 @@
         mConcurrentCameraModeListeners.remove(listener);
     }
 
+    @Override
+    public void shutdown() {
+        mConcurrentCameraModeListeners.clear();
+        mConcurrentCameraIdMap.clear();
+        mActiveConcurrentCameraInfos.clear();
+        mConcurrentCameraIds.clear();
+        mCameraOperatingMode = CAMERA_OPERATING_MODE_UNSPECIFIED;
+    }
+
     private void retrieveConcurrentCameraIds() {
         try {
             mConcurrentCameraIds = mCameraManager.getConcurrentCameraIds();
@@ -159,8 +168,8 @@
                 if (!mConcurrentCameraIdMap.containsKey(cameraId2)) {
                     mConcurrentCameraIdMap.put(cameraId2, new ArrayList<>());
                 }
-                mConcurrentCameraIdMap.get(cameraIdList.get(0)).add(cameraIdList.get(1));
-                mConcurrentCameraIdMap.get(cameraIdList.get(1)).add(cameraIdList.get(0));
+                mConcurrentCameraIdMap.get(cameraId1).add(cameraIdList.get(1));
+                mConcurrentCameraIdMap.get(cameraId2).add(cameraIdList.get(0));
             }
         }
     }
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/concurrent/Camera2CameraCoordinatorTest.kt b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/concurrent/Camera2CameraCoordinatorTest.kt
index 6b7de32..fa3f220 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/concurrent/Camera2CameraCoordinatorTest.kt
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/concurrent/Camera2CameraCoordinatorTest.kt
@@ -57,8 +57,6 @@
 )
 class Camera2CameraCoordinatorTest {
 
-    private val mContext = ApplicationProvider.getApplicationContext<Context>()
-
     private lateinit var cameraCoordinator: CameraCoordinator
 
     @Before
@@ -109,26 +107,7 @@
 
     @Test
     fun getPairedCameraId() {
-        val characteristics0 = ShadowCameraCharacteristics.newCameraCharacteristics()
-        (Shadow.extract<Any>(
-            ApplicationProvider.getApplicationContext<Context>()
-                .getSystemService(Context.CAMERA_SERVICE)
-        ) as ShadowCameraManager)
-            .addCamera("0", characteristics0)
-        val characteristics1 = ShadowCameraCharacteristics.newCameraCharacteristics()
-        (Shadow.extract<Any>(
-            ApplicationProvider.getApplicationContext<Context>()
-                .getSystemService(Context.CAMERA_SERVICE)
-        ) as ShadowCameraManager)
-            .addCamera("1", characteristics1)
-
-        val mCameraManagerCompat =
-            CameraManagerCompat.from((ApplicationProvider.getApplicationContext() as Context))
-
-        cameraCoordinator.activeConcurrentCameraInfos = listOf(
-            Camera2CameraInfoImpl("0", mCameraManagerCompat),
-            Camera2CameraInfoImpl("1", mCameraManagerCompat)
-        )
+        cameraCoordinator.activeConcurrentCameraInfos = createConcurrentCameraInfos()
 
         assertThat(cameraCoordinator.getPairedConcurrentCameraId("0")).isEqualTo("1")
         assertThat(cameraCoordinator.getPairedConcurrentCameraId("1")).isEqualTo("0")
@@ -164,6 +143,41 @@
             anyInt(), anyInt())
     }
 
+    @Test
+    fun shutdown() {
+        cameraCoordinator.cameraOperatingMode = CAMERA_OPERATING_MODE_CONCURRENT
+        cameraCoordinator.activeConcurrentCameraInfos = createConcurrentCameraInfos()
+
+        cameraCoordinator.shutdown()
+
+        assertThat(cameraCoordinator.concurrentCameraSelectors).isEmpty()
+        assertThat(cameraCoordinator.activeConcurrentCameraInfos).isEmpty()
+        assertThat(cameraCoordinator.cameraOperatingMode).isEqualTo(
+            CAMERA_OPERATING_MODE_UNSPECIFIED)
+    }
+
+    private fun createConcurrentCameraInfos(): List<Camera2CameraInfoImpl> {
+        val characteristics0 = ShadowCameraCharacteristics.newCameraCharacteristics()
+        (Shadow.extract<Any>(
+            ApplicationProvider.getApplicationContext<Context>()
+                .getSystemService(Context.CAMERA_SERVICE)
+        ) as ShadowCameraManager)
+            .addCamera("0", characteristics0)
+        val characteristics1 = ShadowCameraCharacteristics.newCameraCharacteristics()
+        (Shadow.extract<Any>(
+            ApplicationProvider.getApplicationContext<Context>()
+                .getSystemService(Context.CAMERA_SERVICE)
+        ) as ShadowCameraManager)
+            .addCamera("1", characteristics1)
+        val cameraManagerCompat =
+            CameraManagerCompat.from((ApplicationProvider.getApplicationContext() as Context))
+
+        return listOf(
+            Camera2CameraInfoImpl("0", cameraManagerCompat),
+            Camera2CameraInfoImpl("1", cameraManagerCompat)
+        )
+    }
+
     private class FakeCameraManagerImpl : CameraManagerCompat.CameraManagerCompatImpl {
 
         private val mCameraManagerImpl = CameraManagerCompat.CameraManagerCompatImpl.from(
diff --git a/camera/camera-core/api/1.3.0-beta02.txt b/camera/camera-core/api/1.3.0-beta02.txt
new file mode 100644
index 0000000..adace2b
--- /dev/null
+++ b/camera/camera-core/api/1.3.0-beta02.txt
@@ -0,0 +1,612 @@
+// Signature format: 4.0
+package androidx.camera.core {
+
+  @RequiresApi(21) public class AspectRatio {
+    field public static final int RATIO_16_9 = 1; // 0x1
+    field public static final int RATIO_4_3 = 0; // 0x0
+    field public static final int RATIO_DEFAULT = -1; // 0xffffffff
+  }
+
+  @RequiresApi(21) public interface Camera {
+    method public androidx.camera.core.CameraControl getCameraControl();
+    method public androidx.camera.core.CameraInfo getCameraInfo();
+  }
+
+  @RequiresApi(21) public interface CameraControl {
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> cancelFocusAndMetering();
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enableTorch(boolean);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Integer!> setExposureCompensationIndex(int);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setLinearZoom(@FloatRange(from=0.0f, to=1.0f) float);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setZoomRatio(float);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.camera.core.FocusMeteringResult!> startFocusAndMetering(androidx.camera.core.FocusMeteringAction);
+  }
+
+  public static final class CameraControl.OperationCanceledException extends java.lang.Exception {
+  }
+
+  @RequiresApi(21) public abstract class CameraEffect {
+    ctor protected CameraEffect(int, java.util.concurrent.Executor, androidx.camera.core.ImageProcessor, androidx.core.util.Consumer<java.lang.Throwable!>);
+    ctor protected CameraEffect(int, java.util.concurrent.Executor, androidx.camera.core.SurfaceProcessor, androidx.core.util.Consumer<java.lang.Throwable!>);
+    method public androidx.core.util.Consumer<java.lang.Throwable!> getErrorListener();
+    method public java.util.concurrent.Executor getExecutor();
+    method public androidx.camera.core.SurfaceProcessor? getSurfaceProcessor();
+    method public int getTargets();
+    field public static final int IMAGE_CAPTURE = 4; // 0x4
+    field public static final int PREVIEW = 1; // 0x1
+    field public static final int VIDEO_CAPTURE = 2; // 0x2
+  }
+
+  @RequiresApi(21) public interface CameraFilter {
+    method public java.util.List<androidx.camera.core.CameraInfo!> filter(java.util.List<androidx.camera.core.CameraInfo!>);
+  }
+
+  @RequiresApi(21) public interface CameraInfo {
+    method public androidx.camera.core.CameraSelector getCameraSelector();
+    method public androidx.lifecycle.LiveData<androidx.camera.core.CameraState!> getCameraState();
+    method public androidx.camera.core.ExposureState getExposureState();
+    method @FloatRange(from=0, fromInclusive=false) public default float getIntrinsicZoomRatio();
+    method public default int getLensFacing();
+    method public int getSensorRotationDegrees();
+    method public int getSensorRotationDegrees(int);
+    method public default java.util.Set<android.util.Range<java.lang.Integer!>!> getSupportedFrameRateRanges();
+    method public androidx.lifecycle.LiveData<java.lang.Integer!> getTorchState();
+    method public androidx.lifecycle.LiveData<androidx.camera.core.ZoomState!> getZoomState();
+    method public boolean hasFlashUnit();
+    method public default boolean isFocusMeteringSupported(androidx.camera.core.FocusMeteringAction);
+    method @SuppressCompatibility @androidx.camera.core.ExperimentalZeroShutterLag public default boolean isZslSupported();
+  }
+
+  @RequiresApi(21) public final class CameraInfoUnavailableException extends java.lang.Exception {
+  }
+
+  @RequiresApi(21) public interface CameraProvider {
+    method public java.util.List<androidx.camera.core.CameraInfo!> getAvailableCameraInfos();
+    method public boolean hasCamera(androidx.camera.core.CameraSelector) throws androidx.camera.core.CameraInfoUnavailableException;
+  }
+
+  @RequiresApi(21) public final class CameraSelector {
+    method public java.util.List<androidx.camera.core.CameraInfo!> filter(java.util.List<androidx.camera.core.CameraInfo!>);
+    field public static final androidx.camera.core.CameraSelector DEFAULT_BACK_CAMERA;
+    field public static final androidx.camera.core.CameraSelector DEFAULT_FRONT_CAMERA;
+    field public static final int LENS_FACING_BACK = 1; // 0x1
+    field @SuppressCompatibility @androidx.camera.core.ExperimentalLensFacing public static final int LENS_FACING_EXTERNAL = 2; // 0x2
+    field public static final int LENS_FACING_FRONT = 0; // 0x0
+    field public static final int LENS_FACING_UNKNOWN = -1; // 0xffffffff
+  }
+
+  public static final class CameraSelector.Builder {
+    ctor public CameraSelector.Builder();
+    method public androidx.camera.core.CameraSelector.Builder addCameraFilter(androidx.camera.core.CameraFilter);
+    method public androidx.camera.core.CameraSelector build();
+    method public androidx.camera.core.CameraSelector.Builder requireLensFacing(int);
+  }
+
+  @RequiresApi(21) @com.google.auto.value.AutoValue public abstract class CameraState {
+    ctor public CameraState();
+    method public static androidx.camera.core.CameraState create(androidx.camera.core.CameraState.Type);
+    method public static androidx.camera.core.CameraState create(androidx.camera.core.CameraState.Type, androidx.camera.core.CameraState.StateError?);
+    method public abstract androidx.camera.core.CameraState.StateError? getError();
+    method public abstract androidx.camera.core.CameraState.Type getType();
+    field public static final int ERROR_CAMERA_DISABLED = 5; // 0x5
+    field public static final int ERROR_CAMERA_FATAL_ERROR = 6; // 0x6
+    field public static final int ERROR_CAMERA_IN_USE = 2; // 0x2
+    field public static final int ERROR_DO_NOT_DISTURB_MODE_ENABLED = 7; // 0x7
+    field public static final int ERROR_MAX_CAMERAS_IN_USE = 1; // 0x1
+    field public static final int ERROR_OTHER_RECOVERABLE_ERROR = 3; // 0x3
+    field public static final int ERROR_STREAM_CONFIG = 4; // 0x4
+  }
+
+  public enum CameraState.ErrorType {
+    enum_constant public static final androidx.camera.core.CameraState.ErrorType CRITICAL;
+    enum_constant public static final androidx.camera.core.CameraState.ErrorType RECOVERABLE;
+  }
+
+  @com.google.auto.value.AutoValue public abstract static class CameraState.StateError {
+    ctor public CameraState.StateError();
+    method public static androidx.camera.core.CameraState.StateError create(int);
+    method public static androidx.camera.core.CameraState.StateError create(int, Throwable?);
+    method public abstract Throwable? getCause();
+    method public abstract int getCode();
+    method public androidx.camera.core.CameraState.ErrorType getType();
+  }
+
+  public enum CameraState.Type {
+    enum_constant public static final androidx.camera.core.CameraState.Type CLOSED;
+    enum_constant public static final androidx.camera.core.CameraState.Type CLOSING;
+    enum_constant public static final androidx.camera.core.CameraState.Type OPEN;
+    enum_constant public static final androidx.camera.core.CameraState.Type OPENING;
+    enum_constant public static final androidx.camera.core.CameraState.Type PENDING_OPEN;
+  }
+
+  @RequiresApi(21) public class CameraUnavailableException extends java.lang.Exception {
+    ctor public CameraUnavailableException(int);
+    ctor public CameraUnavailableException(int, String?);
+    ctor public CameraUnavailableException(int, String?, Throwable?);
+    ctor public CameraUnavailableException(int, Throwable?);
+    method public int getReason();
+    field public static final int CAMERA_DISABLED = 1; // 0x1
+    field public static final int CAMERA_DISCONNECTED = 2; // 0x2
+    field public static final int CAMERA_ERROR = 3; // 0x3
+    field public static final int CAMERA_IN_USE = 4; // 0x4
+    field public static final int CAMERA_MAX_IN_USE = 5; // 0x5
+    field public static final int CAMERA_UNAVAILABLE_DO_NOT_DISTURB = 6; // 0x6
+    field public static final int CAMERA_UNKNOWN_ERROR = 0; // 0x0
+  }
+
+  @RequiresApi(21) public final class CameraXConfig {
+    method public androidx.camera.core.CameraSelector? getAvailableCamerasLimiter(androidx.camera.core.CameraSelector?);
+    method public java.util.concurrent.Executor? getCameraExecutor(java.util.concurrent.Executor?);
+    method public int getMinimumLoggingLevel();
+    method public android.os.Handler? getSchedulerHandler(android.os.Handler?);
+  }
+
+  public static final class CameraXConfig.Builder {
+    method public androidx.camera.core.CameraXConfig build();
+    method public static androidx.camera.core.CameraXConfig.Builder fromConfig(androidx.camera.core.CameraXConfig);
+    method public androidx.camera.core.CameraXConfig.Builder setAvailableCamerasLimiter(androidx.camera.core.CameraSelector);
+    method public androidx.camera.core.CameraXConfig.Builder setCameraExecutor(java.util.concurrent.Executor);
+    method public androidx.camera.core.CameraXConfig.Builder setMinimumLoggingLevel(@IntRange(from=android.util.Log.DEBUG, to=android.util.Log.ERROR) int);
+    method public androidx.camera.core.CameraXConfig.Builder setSchedulerHandler(android.os.Handler);
+  }
+
+  public static interface CameraXConfig.Provider {
+    method public androidx.camera.core.CameraXConfig getCameraXConfig();
+  }
+
+  @RequiresApi(21) public class ConcurrentCamera {
+    ctor public ConcurrentCamera(java.util.List<androidx.camera.core.Camera!>);
+    method public java.util.List<androidx.camera.core.Camera!> getCameras();
+  }
+
+  public static final class ConcurrentCamera.SingleCameraConfig {
+    ctor public ConcurrentCamera.SingleCameraConfig(androidx.camera.core.CameraSelector, androidx.camera.core.UseCaseGroup, androidx.lifecycle.LifecycleOwner);
+    method public androidx.camera.core.CameraSelector getCameraSelector();
+    method public androidx.lifecycle.LifecycleOwner getLifecycleOwner();
+    method public androidx.camera.core.UseCaseGroup getUseCaseGroup();
+  }
+
+  @RequiresApi(21) public final class DisplayOrientedMeteringPointFactory extends androidx.camera.core.MeteringPointFactory {
+    ctor public DisplayOrientedMeteringPointFactory(android.view.Display, androidx.camera.core.CameraInfo, float, float);
+  }
+
+  @RequiresApi(21) public final class DynamicRange {
+    ctor public DynamicRange(int, int);
+    method public int getBitDepth();
+    method public int getEncoding();
+    field public static final int BIT_DEPTH_10_BIT = 10; // 0xa
+    field public static final int BIT_DEPTH_8_BIT = 8; // 0x8
+    field public static final int BIT_DEPTH_UNSPECIFIED = 0; // 0x0
+    field public static final androidx.camera.core.DynamicRange DOLBY_VISION_10_BIT;
+    field public static final androidx.camera.core.DynamicRange DOLBY_VISION_8_BIT;
+    field public static final int ENCODING_DOLBY_VISION = 6; // 0x6
+    field public static final int ENCODING_HDR10 = 4; // 0x4
+    field public static final int ENCODING_HDR10_PLUS = 5; // 0x5
+    field public static final int ENCODING_HDR_UNSPECIFIED = 2; // 0x2
+    field public static final int ENCODING_HLG = 3; // 0x3
+    field public static final int ENCODING_SDR = 1; // 0x1
+    field public static final int ENCODING_UNSPECIFIED = 0; // 0x0
+    field public static final androidx.camera.core.DynamicRange HDR10_10_BIT;
+    field public static final androidx.camera.core.DynamicRange HDR10_PLUS_10_BIT;
+    field public static final androidx.camera.core.DynamicRange HDR_UNSPECIFIED_10_BIT;
+    field public static final androidx.camera.core.DynamicRange HLG_10_BIT;
+    field public static final androidx.camera.core.DynamicRange SDR;
+    field public static final androidx.camera.core.DynamicRange UNSPECIFIED;
+  }
+
+  @SuppressCompatibility @RequiresApi(21) @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalGetImage {
+  }
+
+  @SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalLensFacing {
+  }
+
+  @SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalUseCaseApi {
+  }
+
+  @SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalZeroShutterLag {
+  }
+
+  @RequiresApi(21) public interface ExposureState {
+    method public int getExposureCompensationIndex();
+    method public android.util.Range<java.lang.Integer!> getExposureCompensationRange();
+    method public android.util.Rational getExposureCompensationStep();
+    method public boolean isExposureCompensationSupported();
+  }
+
+  @RequiresApi(21) public interface ExtendableBuilder<T> {
+    method public T build();
+  }
+
+  @RequiresApi(21) public final class FocusMeteringAction {
+    method public long getAutoCancelDurationInMillis();
+    method public java.util.List<androidx.camera.core.MeteringPoint!> getMeteringPointsAe();
+    method public java.util.List<androidx.camera.core.MeteringPoint!> getMeteringPointsAf();
+    method public java.util.List<androidx.camera.core.MeteringPoint!> getMeteringPointsAwb();
+    method public boolean isAutoCancelEnabled();
+    field public static final int FLAG_AE = 2; // 0x2
+    field public static final int FLAG_AF = 1; // 0x1
+    field public static final int FLAG_AWB = 4; // 0x4
+  }
+
+  public static class FocusMeteringAction.Builder {
+    ctor public FocusMeteringAction.Builder(androidx.camera.core.MeteringPoint);
+    ctor public FocusMeteringAction.Builder(androidx.camera.core.MeteringPoint, int);
+    method public androidx.camera.core.FocusMeteringAction.Builder addPoint(androidx.camera.core.MeteringPoint);
+    method public androidx.camera.core.FocusMeteringAction.Builder addPoint(androidx.camera.core.MeteringPoint, int);
+    method public androidx.camera.core.FocusMeteringAction build();
+    method public androidx.camera.core.FocusMeteringAction.Builder disableAutoCancel();
+    method public androidx.camera.core.FocusMeteringAction.Builder setAutoCancelDuration(@IntRange(from=1) long, java.util.concurrent.TimeUnit);
+  }
+
+  @RequiresApi(21) public final class FocusMeteringResult {
+    method public boolean isFocusSuccessful();
+  }
+
+  @RequiresApi(21) public final class ImageAnalysis extends androidx.camera.core.UseCase {
+    method public void clearAnalyzer();
+    method @SuppressCompatibility @androidx.camera.core.ExperimentalUseCaseApi public java.util.concurrent.Executor? getBackgroundExecutor();
+    method public int getBackpressureStrategy();
+    method public int getImageQueueDepth();
+    method public int getOutputImageFormat();
+    method public androidx.camera.core.ResolutionInfo? getResolutionInfo();
+    method public androidx.camera.core.resolutionselector.ResolutionSelector? getResolutionSelector();
+    method public int getTargetRotation();
+    method public boolean isOutputImageRotationEnabled();
+    method public void setAnalyzer(java.util.concurrent.Executor, androidx.camera.core.ImageAnalysis.Analyzer);
+    method public void setTargetRotation(int);
+    field public static final int COORDINATE_SYSTEM_ORIGINAL = 0; // 0x0
+    field public static final int OUTPUT_IMAGE_FORMAT_RGBA_8888 = 2; // 0x2
+    field public static final int OUTPUT_IMAGE_FORMAT_YUV_420_888 = 1; // 0x1
+    field public static final int STRATEGY_BLOCK_PRODUCER = 1; // 0x1
+    field public static final int STRATEGY_KEEP_ONLY_LATEST = 0; // 0x0
+  }
+
+  public static interface ImageAnalysis.Analyzer {
+    method public void analyze(androidx.camera.core.ImageProxy);
+    method public default android.util.Size? getDefaultTargetResolution();
+    method public default int getTargetCoordinateSystem();
+    method public default void updateTransform(android.graphics.Matrix?);
+  }
+
+  public static final class ImageAnalysis.Builder implements androidx.camera.core.ExtendableBuilder<androidx.camera.core.ImageAnalysis> {
+    ctor public ImageAnalysis.Builder();
+    method public androidx.camera.core.ImageAnalysis build();
+    method public androidx.camera.core.ImageAnalysis.Builder setBackgroundExecutor(java.util.concurrent.Executor);
+    method public androidx.camera.core.ImageAnalysis.Builder setBackpressureStrategy(int);
+    method public androidx.camera.core.ImageAnalysis.Builder setImageQueueDepth(int);
+    method public androidx.camera.core.ImageAnalysis.Builder setOutputImageFormat(int);
+    method @RequiresApi(23) public androidx.camera.core.ImageAnalysis.Builder setOutputImageRotationEnabled(boolean);
+    method public androidx.camera.core.ImageAnalysis.Builder setResolutionSelector(androidx.camera.core.resolutionselector.ResolutionSelector);
+    method @Deprecated public androidx.camera.core.ImageAnalysis.Builder setTargetAspectRatio(int);
+    method public androidx.camera.core.ImageAnalysis.Builder setTargetName(String);
+    method @Deprecated public androidx.camera.core.ImageAnalysis.Builder setTargetResolution(android.util.Size);
+    method public androidx.camera.core.ImageAnalysis.Builder setTargetRotation(int);
+  }
+
+  @RequiresApi(21) public final class ImageCapture extends androidx.camera.core.UseCase {
+    method public int getCaptureMode();
+    method public int getFlashMode();
+    method @IntRange(from=1, to=100) public int getJpegQuality();
+    method public androidx.camera.core.ResolutionInfo? getResolutionInfo();
+    method public androidx.camera.core.resolutionselector.ResolutionSelector? getResolutionSelector();
+    method public int getTargetRotation();
+    method public void setCropAspectRatio(android.util.Rational);
+    method public void setFlashMode(int);
+    method public void setTargetRotation(int);
+    method public void takePicture(androidx.camera.core.ImageCapture.OutputFileOptions, java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageSavedCallback);
+    method public void takePicture(java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageCapturedCallback);
+    field public static final int CAPTURE_MODE_MAXIMIZE_QUALITY = 0; // 0x0
+    field public static final int CAPTURE_MODE_MINIMIZE_LATENCY = 1; // 0x1
+    field @SuppressCompatibility @androidx.camera.core.ExperimentalZeroShutterLag public static final int CAPTURE_MODE_ZERO_SHUTTER_LAG = 2; // 0x2
+    field public static final int ERROR_CAMERA_CLOSED = 3; // 0x3
+    field public static final int ERROR_CAPTURE_FAILED = 2; // 0x2
+    field public static final int ERROR_FILE_IO = 1; // 0x1
+    field public static final int ERROR_INVALID_CAMERA = 4; // 0x4
+    field public static final int ERROR_UNKNOWN = 0; // 0x0
+    field public static final int FLASH_MODE_AUTO = 0; // 0x0
+    field public static final int FLASH_MODE_OFF = 2; // 0x2
+    field public static final int FLASH_MODE_ON = 1; // 0x1
+  }
+
+  public static final class ImageCapture.Builder implements androidx.camera.core.ExtendableBuilder<androidx.camera.core.ImageCapture> {
+    ctor public ImageCapture.Builder();
+    method public androidx.camera.core.ImageCapture build();
+    method public androidx.camera.core.ImageCapture.Builder setCaptureMode(int);
+    method public androidx.camera.core.ImageCapture.Builder setFlashMode(int);
+    method public androidx.camera.core.ImageCapture.Builder setIoExecutor(java.util.concurrent.Executor);
+    method public androidx.camera.core.ImageCapture.Builder setJpegQuality(@IntRange(from=1, to=100) int);
+    method public androidx.camera.core.ImageCapture.Builder setResolutionSelector(androidx.camera.core.resolutionselector.ResolutionSelector);
+    method @Deprecated public androidx.camera.core.ImageCapture.Builder setTargetAspectRatio(int);
+    method public androidx.camera.core.ImageCapture.Builder setTargetName(String);
+    method @Deprecated public androidx.camera.core.ImageCapture.Builder setTargetResolution(android.util.Size);
+    method public androidx.camera.core.ImageCapture.Builder setTargetRotation(int);
+  }
+
+  public static final class ImageCapture.Metadata {
+    ctor public ImageCapture.Metadata();
+    method public android.location.Location? getLocation();
+    method public boolean isReversedHorizontal();
+    method public boolean isReversedVertical();
+    method public void setLocation(android.location.Location?);
+    method public void setReversedHorizontal(boolean);
+    method public void setReversedVertical(boolean);
+  }
+
+  public abstract static class ImageCapture.OnImageCapturedCallback {
+    ctor public ImageCapture.OnImageCapturedCallback();
+    method public void onCaptureSuccess(androidx.camera.core.ImageProxy);
+    method public void onError(androidx.camera.core.ImageCaptureException);
+  }
+
+  public static interface ImageCapture.OnImageSavedCallback {
+    method public void onError(androidx.camera.core.ImageCaptureException);
+    method public void onImageSaved(androidx.camera.core.ImageCapture.OutputFileResults);
+  }
+
+  public static final class ImageCapture.OutputFileOptions {
+  }
+
+  public static final class ImageCapture.OutputFileOptions.Builder {
+    ctor public ImageCapture.OutputFileOptions.Builder(android.content.ContentResolver, android.net.Uri, android.content.ContentValues);
+    ctor public ImageCapture.OutputFileOptions.Builder(java.io.File);
+    ctor public ImageCapture.OutputFileOptions.Builder(java.io.OutputStream);
+    method public androidx.camera.core.ImageCapture.OutputFileOptions build();
+    method public androidx.camera.core.ImageCapture.OutputFileOptions.Builder setMetadata(androidx.camera.core.ImageCapture.Metadata);
+  }
+
+  public static class ImageCapture.OutputFileResults {
+    method public android.net.Uri? getSavedUri();
+  }
+
+  @RequiresApi(21) public class ImageCaptureException extends java.lang.Exception {
+    ctor public ImageCaptureException(int, String, Throwable?);
+    method public int getImageCaptureError();
+  }
+
+  @RequiresApi(21) public interface ImageInfo {
+    method public int getRotationDegrees();
+    method public default android.graphics.Matrix getSensorToBufferTransformMatrix();
+    method public long getTimestamp();
+  }
+
+  public interface ImageProcessor {
+    method public androidx.camera.core.ImageProcessor.Response process(androidx.camera.core.ImageProcessor.Request) throws androidx.camera.core.ProcessingException;
+  }
+
+  public static interface ImageProcessor.Request {
+    method public androidx.camera.core.ImageProxy getInputImage();
+    method public int getOutputFormat();
+  }
+
+  public static interface ImageProcessor.Response {
+    method public androidx.camera.core.ImageProxy getOutputImage();
+  }
+
+  @RequiresApi(21) public interface ImageProxy extends java.lang.AutoCloseable {
+    method public void close();
+    method public android.graphics.Rect getCropRect();
+    method public int getFormat();
+    method public int getHeight();
+    method @SuppressCompatibility @androidx.camera.core.ExperimentalGetImage public android.media.Image? getImage();
+    method public androidx.camera.core.ImageInfo getImageInfo();
+    method public androidx.camera.core.ImageProxy.PlaneProxy![] getPlanes();
+    method public int getWidth();
+    method public void setCropRect(android.graphics.Rect?);
+    method public default android.graphics.Bitmap toBitmap();
+  }
+
+  public static interface ImageProxy.PlaneProxy {
+    method public java.nio.ByteBuffer getBuffer();
+    method public int getPixelStride();
+    method public int getRowStride();
+  }
+
+  @RequiresApi(21) public class InitializationException extends java.lang.Exception {
+    ctor public InitializationException(String?);
+    ctor public InitializationException(String?, Throwable?);
+    ctor public InitializationException(Throwable?);
+  }
+
+  @RequiresApi(21) public class MeteringPoint {
+    method public float getSize();
+  }
+
+  @RequiresApi(21) public abstract class MeteringPointFactory {
+    method public final androidx.camera.core.MeteringPoint createPoint(float, float);
+    method public final androidx.camera.core.MeteringPoint createPoint(float, float, float);
+    method public static float getDefaultPointSize();
+  }
+
+  @RequiresApi(21) public class MirrorMode {
+    field public static final int MIRROR_MODE_OFF = 0; // 0x0
+    field public static final int MIRROR_MODE_ON = 1; // 0x1
+    field public static final int MIRROR_MODE_ON_FRONT_ONLY = 2; // 0x2
+  }
+
+  @RequiresApi(21) public final class Preview extends androidx.camera.core.UseCase {
+    method public androidx.camera.core.ResolutionInfo? getResolutionInfo();
+    method public androidx.camera.core.resolutionselector.ResolutionSelector? getResolutionSelector();
+    method public android.util.Range<java.lang.Integer!> getTargetFrameRate();
+    method public int getTargetRotation();
+    method @UiThread public void setSurfaceProvider(androidx.camera.core.Preview.SurfaceProvider?);
+    method @UiThread public void setSurfaceProvider(java.util.concurrent.Executor, androidx.camera.core.Preview.SurfaceProvider?);
+    method public void setTargetRotation(int);
+  }
+
+  public static final class Preview.Builder implements androidx.camera.core.ExtendableBuilder<androidx.camera.core.Preview> {
+    ctor public Preview.Builder();
+    method public androidx.camera.core.Preview build();
+    method public androidx.camera.core.Preview.Builder setResolutionSelector(androidx.camera.core.resolutionselector.ResolutionSelector);
+    method @Deprecated public androidx.camera.core.Preview.Builder setTargetAspectRatio(int);
+    method public androidx.camera.core.Preview.Builder setTargetFrameRate(android.util.Range<java.lang.Integer!>);
+    method public androidx.camera.core.Preview.Builder setTargetName(String);
+    method @Deprecated public androidx.camera.core.Preview.Builder setTargetResolution(android.util.Size);
+    method public androidx.camera.core.Preview.Builder setTargetRotation(int);
+  }
+
+  public static interface Preview.SurfaceProvider {
+    method public void onSurfaceRequested(androidx.camera.core.SurfaceRequest);
+  }
+
+  public class ProcessingException extends java.lang.Exception {
+    ctor public ProcessingException();
+  }
+
+  @RequiresApi(21) public class ResolutionInfo {
+    ctor public ResolutionInfo(android.util.Size, android.graphics.Rect, int);
+    method public android.graphics.Rect getCropRect();
+    method public android.util.Size getResolution();
+    method public int getRotationDegrees();
+  }
+
+  @RequiresApi(21) public class SurfaceOrientedMeteringPointFactory extends androidx.camera.core.MeteringPointFactory {
+    ctor public SurfaceOrientedMeteringPointFactory(float, float);
+    ctor public SurfaceOrientedMeteringPointFactory(float, float, androidx.camera.core.UseCase);
+  }
+
+  public interface SurfaceOutput extends java.io.Closeable {
+    method public void close();
+    method public android.util.Size getSize();
+    method public android.view.Surface getSurface(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.camera.core.SurfaceOutput.Event!>);
+    method public int getTargets();
+    method public void updateTransformMatrix(float[], float[]);
+  }
+
+  @com.google.auto.value.AutoValue public abstract static class SurfaceOutput.Event {
+    method public abstract int getEventCode();
+    method public abstract androidx.camera.core.SurfaceOutput getSurfaceOutput();
+    field public static final int EVENT_REQUEST_CLOSE = 0; // 0x0
+  }
+
+  public interface SurfaceProcessor {
+    method public void onInputSurface(androidx.camera.core.SurfaceRequest) throws androidx.camera.core.ProcessingException;
+    method public void onOutputSurface(androidx.camera.core.SurfaceOutput) throws androidx.camera.core.ProcessingException;
+  }
+
+  @RequiresApi(21) public final class SurfaceRequest {
+    method public void addRequestCancellationListener(java.util.concurrent.Executor, Runnable);
+    method public void clearTransformationInfoListener();
+    method public androidx.camera.core.DynamicRange getDynamicRange();
+    method public android.util.Size getResolution();
+    method public boolean invalidate();
+    method public void provideSurface(android.view.Surface, java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.camera.core.SurfaceRequest.Result!>);
+    method public void setTransformationInfoListener(java.util.concurrent.Executor, androidx.camera.core.SurfaceRequest.TransformationInfoListener);
+    method public boolean willNotProvideSurface();
+  }
+
+  @com.google.auto.value.AutoValue public abstract static class SurfaceRequest.Result {
+    method public abstract int getResultCode();
+    method public abstract android.view.Surface getSurface();
+    field public static final int RESULT_INVALID_SURFACE = 2; // 0x2
+    field public static final int RESULT_REQUEST_CANCELLED = 1; // 0x1
+    field public static final int RESULT_SURFACE_ALREADY_PROVIDED = 3; // 0x3
+    field public static final int RESULT_SURFACE_USED_SUCCESSFULLY = 0; // 0x0
+    field public static final int RESULT_WILL_NOT_PROVIDE_SURFACE = 4; // 0x4
+  }
+
+  @com.google.auto.value.AutoValue public abstract static class SurfaceRequest.TransformationInfo {
+    method public abstract android.graphics.Rect getCropRect();
+    method public abstract int getRotationDegrees();
+  }
+
+  public static interface SurfaceRequest.TransformationInfoListener {
+    method public void onTransformationInfoUpdate(androidx.camera.core.SurfaceRequest.TransformationInfo);
+  }
+
+  @RequiresApi(21) public class TorchState {
+    field public static final int OFF = 0; // 0x0
+    field public static final int ON = 1; // 0x1
+  }
+
+  @RequiresApi(21) public abstract class UseCase {
+    method public static int snapToSurfaceRotation(@IntRange(from=0, to=359) int);
+  }
+
+  @RequiresApi(21) public final class UseCaseGroup {
+    method public java.util.List<androidx.camera.core.CameraEffect!> getEffects();
+    method public java.util.List<androidx.camera.core.UseCase!> getUseCases();
+    method public androidx.camera.core.ViewPort? getViewPort();
+  }
+
+  public static final class UseCaseGroup.Builder {
+    ctor public UseCaseGroup.Builder();
+    method public androidx.camera.core.UseCaseGroup.Builder addEffect(androidx.camera.core.CameraEffect);
+    method public androidx.camera.core.UseCaseGroup.Builder addUseCase(androidx.camera.core.UseCase);
+    method public androidx.camera.core.UseCaseGroup build();
+    method public androidx.camera.core.UseCaseGroup.Builder setViewPort(androidx.camera.core.ViewPort);
+  }
+
+  @RequiresApi(21) public final class ViewPort {
+    method public android.util.Rational getAspectRatio();
+    method public int getLayoutDirection();
+    method public int getRotation();
+    method public int getScaleType();
+    field public static final int FILL_CENTER = 1; // 0x1
+    field public static final int FILL_END = 2; // 0x2
+    field public static final int FILL_START = 0; // 0x0
+    field public static final int FIT = 3; // 0x3
+  }
+
+  public static final class ViewPort.Builder {
+    ctor public ViewPort.Builder(android.util.Rational, int);
+    method public androidx.camera.core.ViewPort build();
+    method public androidx.camera.core.ViewPort.Builder setLayoutDirection(int);
+    method public androidx.camera.core.ViewPort.Builder setScaleType(int);
+  }
+
+  @RequiresApi(21) public interface ZoomState {
+    method public float getLinearZoom();
+    method public float getMaxZoomRatio();
+    method public float getMinZoomRatio();
+    method public float getZoomRatio();
+  }
+
+}
+
+package androidx.camera.core.resolutionselector {
+
+  @RequiresApi(21) public final class AspectRatioStrategy {
+    ctor public AspectRatioStrategy(int, int);
+    method public int getFallbackRule();
+    method public int getPreferredAspectRatio();
+    field public static final int FALLBACK_RULE_AUTO = 1; // 0x1
+    field public static final int FALLBACK_RULE_NONE = 0; // 0x0
+    field public static final androidx.camera.core.resolutionselector.AspectRatioStrategy RATIO_16_9_FALLBACK_AUTO_STRATEGY;
+    field public static final androidx.camera.core.resolutionselector.AspectRatioStrategy RATIO_4_3_FALLBACK_AUTO_STRATEGY;
+  }
+
+  @RequiresApi(21) public interface ResolutionFilter {
+    method public java.util.List<android.util.Size!> filter(java.util.List<android.util.Size!>, int);
+  }
+
+  @RequiresApi(21) public final class ResolutionSelector {
+    method public int getAllowedResolutionMode();
+    method public androidx.camera.core.resolutionselector.AspectRatioStrategy getAspectRatioStrategy();
+    method public androidx.camera.core.resolutionselector.ResolutionFilter? getResolutionFilter();
+    method public androidx.camera.core.resolutionselector.ResolutionStrategy? getResolutionStrategy();
+    field public static final int PREFER_CAPTURE_RATE_OVER_HIGHER_RESOLUTION = 0; // 0x0
+    field public static final int PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE = 1; // 0x1
+  }
+
+  public static final class ResolutionSelector.Builder {
+    ctor public ResolutionSelector.Builder();
+    method public androidx.camera.core.resolutionselector.ResolutionSelector build();
+    method public androidx.camera.core.resolutionselector.ResolutionSelector.Builder setAllowedResolutionMode(int);
+    method public androidx.camera.core.resolutionselector.ResolutionSelector.Builder setAspectRatioStrategy(androidx.camera.core.resolutionselector.AspectRatioStrategy);
+    method public androidx.camera.core.resolutionselector.ResolutionSelector.Builder setResolutionFilter(androidx.camera.core.resolutionselector.ResolutionFilter);
+    method public androidx.camera.core.resolutionselector.ResolutionSelector.Builder setResolutionStrategy(androidx.camera.core.resolutionselector.ResolutionStrategy);
+  }
+
+  @RequiresApi(21) public final class ResolutionStrategy {
+    ctor public ResolutionStrategy(android.util.Size, int);
+    method public android.util.Size? getBoundSize();
+    method public int getFallbackRule();
+    field public static final int FALLBACK_RULE_CLOSEST_HIGHER = 2; // 0x2
+    field public static final int FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER = 1; // 0x1
+    field public static final int FALLBACK_RULE_CLOSEST_LOWER = 4; // 0x4
+    field public static final int FALLBACK_RULE_CLOSEST_LOWER_THEN_HIGHER = 3; // 0x3
+    field public static final int FALLBACK_RULE_NONE = 0; // 0x0
+    field public static final androidx.camera.core.resolutionselector.ResolutionStrategy HIGHEST_AVAILABLE_STRATEGY;
+  }
+
+}
+
diff --git a/camera/camera-core/api/res-1.3.0-beta02.txt b/camera/camera-core/api/res-1.3.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/camera/camera-core/api/res-1.3.0-beta02.txt
diff --git a/camera/camera-core/api/restricted_1.3.0-beta02.txt b/camera/camera-core/api/restricted_1.3.0-beta02.txt
new file mode 100644
index 0000000..adace2b
--- /dev/null
+++ b/camera/camera-core/api/restricted_1.3.0-beta02.txt
@@ -0,0 +1,612 @@
+// Signature format: 4.0
+package androidx.camera.core {
+
+  @RequiresApi(21) public class AspectRatio {
+    field public static final int RATIO_16_9 = 1; // 0x1
+    field public static final int RATIO_4_3 = 0; // 0x0
+    field public static final int RATIO_DEFAULT = -1; // 0xffffffff
+  }
+
+  @RequiresApi(21) public interface Camera {
+    method public androidx.camera.core.CameraControl getCameraControl();
+    method public androidx.camera.core.CameraInfo getCameraInfo();
+  }
+
+  @RequiresApi(21) public interface CameraControl {
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> cancelFocusAndMetering();
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enableTorch(boolean);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Integer!> setExposureCompensationIndex(int);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setLinearZoom(@FloatRange(from=0.0f, to=1.0f) float);
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setZoomRatio(float);
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.camera.core.FocusMeteringResult!> startFocusAndMetering(androidx.camera.core.FocusMeteringAction);
+  }
+
+  public static final class CameraControl.OperationCanceledException extends java.lang.Exception {
+  }
+
+  @RequiresApi(21) public abstract class CameraEffect {
+    ctor protected CameraEffect(int, java.util.concurrent.Executor, androidx.camera.core.ImageProcessor, androidx.core.util.Consumer<java.lang.Throwable!>);
+    ctor protected CameraEffect(int, java.util.concurrent.Executor, androidx.camera.core.SurfaceProcessor, androidx.core.util.Consumer<java.lang.Throwable!>);
+    method public androidx.core.util.Consumer<java.lang.Throwable!> getErrorListener();
+    method public java.util.concurrent.Executor getExecutor();
+    method public androidx.camera.core.SurfaceProcessor? getSurfaceProcessor();
+    method public int getTargets();
+    field public static final int IMAGE_CAPTURE = 4; // 0x4
+    field public static final int PREVIEW = 1; // 0x1
+    field public static final int VIDEO_CAPTURE = 2; // 0x2
+  }
+
+  @RequiresApi(21) public interface CameraFilter {
+    method public java.util.List<androidx.camera.core.CameraInfo!> filter(java.util.List<androidx.camera.core.CameraInfo!>);
+  }
+
+  @RequiresApi(21) public interface CameraInfo {
+    method public androidx.camera.core.CameraSelector getCameraSelector();
+    method public androidx.lifecycle.LiveData<androidx.camera.core.CameraState!> getCameraState();
+    method public androidx.camera.core.ExposureState getExposureState();
+    method @FloatRange(from=0, fromInclusive=false) public default float getIntrinsicZoomRatio();
+    method public default int getLensFacing();
+    method public int getSensorRotationDegrees();
+    method public int getSensorRotationDegrees(int);
+    method public default java.util.Set<android.util.Range<java.lang.Integer!>!> getSupportedFrameRateRanges();
+    method public androidx.lifecycle.LiveData<java.lang.Integer!> getTorchState();
+    method public androidx.lifecycle.LiveData<androidx.camera.core.ZoomState!> getZoomState();
+    method public boolean hasFlashUnit();
+    method public default boolean isFocusMeteringSupported(androidx.camera.core.FocusMeteringAction);
+    method @SuppressCompatibility @androidx.camera.core.ExperimentalZeroShutterLag public default boolean isZslSupported();
+  }
+
+  @RequiresApi(21) public final class CameraInfoUnavailableException extends java.lang.Exception {
+  }
+
+  @RequiresApi(21) public interface CameraProvider {
+    method public java.util.List<androidx.camera.core.CameraInfo!> getAvailableCameraInfos();
+    method public boolean hasCamera(androidx.camera.core.CameraSelector) throws androidx.camera.core.CameraInfoUnavailableException;
+  }
+
+  @RequiresApi(21) public final class CameraSelector {
+    method public java.util.List<androidx.camera.core.CameraInfo!> filter(java.util.List<androidx.camera.core.CameraInfo!>);
+    field public static final androidx.camera.core.CameraSelector DEFAULT_BACK_CAMERA;
+    field public static final androidx.camera.core.CameraSelector DEFAULT_FRONT_CAMERA;
+    field public static final int LENS_FACING_BACK = 1; // 0x1
+    field @SuppressCompatibility @androidx.camera.core.ExperimentalLensFacing public static final int LENS_FACING_EXTERNAL = 2; // 0x2
+    field public static final int LENS_FACING_FRONT = 0; // 0x0
+    field public static final int LENS_FACING_UNKNOWN = -1; // 0xffffffff
+  }
+
+  public static final class CameraSelector.Builder {
+    ctor public CameraSelector.Builder();
+    method public androidx.camera.core.CameraSelector.Builder addCameraFilter(androidx.camera.core.CameraFilter);
+    method public androidx.camera.core.CameraSelector build();
+    method public androidx.camera.core.CameraSelector.Builder requireLensFacing(int);
+  }
+
+  @RequiresApi(21) @com.google.auto.value.AutoValue public abstract class CameraState {
+    ctor public CameraState();
+    method public static androidx.camera.core.CameraState create(androidx.camera.core.CameraState.Type);
+    method public static androidx.camera.core.CameraState create(androidx.camera.core.CameraState.Type, androidx.camera.core.CameraState.StateError?);
+    method public abstract androidx.camera.core.CameraState.StateError? getError();
+    method public abstract androidx.camera.core.CameraState.Type getType();
+    field public static final int ERROR_CAMERA_DISABLED = 5; // 0x5
+    field public static final int ERROR_CAMERA_FATAL_ERROR = 6; // 0x6
+    field public static final int ERROR_CAMERA_IN_USE = 2; // 0x2
+    field public static final int ERROR_DO_NOT_DISTURB_MODE_ENABLED = 7; // 0x7
+    field public static final int ERROR_MAX_CAMERAS_IN_USE = 1; // 0x1
+    field public static final int ERROR_OTHER_RECOVERABLE_ERROR = 3; // 0x3
+    field public static final int ERROR_STREAM_CONFIG = 4; // 0x4
+  }
+
+  public enum CameraState.ErrorType {
+    enum_constant public static final androidx.camera.core.CameraState.ErrorType CRITICAL;
+    enum_constant public static final androidx.camera.core.CameraState.ErrorType RECOVERABLE;
+  }
+
+  @com.google.auto.value.AutoValue public abstract static class CameraState.StateError {
+    ctor public CameraState.StateError();
+    method public static androidx.camera.core.CameraState.StateError create(int);
+    method public static androidx.camera.core.CameraState.StateError create(int, Throwable?);
+    method public abstract Throwable? getCause();
+    method public abstract int getCode();
+    method public androidx.camera.core.CameraState.ErrorType getType();
+  }
+
+  public enum CameraState.Type {
+    enum_constant public static final androidx.camera.core.CameraState.Type CLOSED;
+    enum_constant public static final androidx.camera.core.CameraState.Type CLOSING;
+    enum_constant public static final androidx.camera.core.CameraState.Type OPEN;
+    enum_constant public static final androidx.camera.core.CameraState.Type OPENING;
+    enum_constant public static final androidx.camera.core.CameraState.Type PENDING_OPEN;
+  }
+
+  @RequiresApi(21) public class CameraUnavailableException extends java.lang.Exception {
+    ctor public CameraUnavailableException(int);
+    ctor public CameraUnavailableException(int, String?);
+    ctor public CameraUnavailableException(int, String?, Throwable?);
+    ctor public CameraUnavailableException(int, Throwable?);
+    method public int getReason();
+    field public static final int CAMERA_DISABLED = 1; // 0x1
+    field public static final int CAMERA_DISCONNECTED = 2; // 0x2
+    field public static final int CAMERA_ERROR = 3; // 0x3
+    field public static final int CAMERA_IN_USE = 4; // 0x4
+    field public static final int CAMERA_MAX_IN_USE = 5; // 0x5
+    field public static final int CAMERA_UNAVAILABLE_DO_NOT_DISTURB = 6; // 0x6
+    field public static final int CAMERA_UNKNOWN_ERROR = 0; // 0x0
+  }
+
+  @RequiresApi(21) public final class CameraXConfig {
+    method public androidx.camera.core.CameraSelector? getAvailableCamerasLimiter(androidx.camera.core.CameraSelector?);
+    method public java.util.concurrent.Executor? getCameraExecutor(java.util.concurrent.Executor?);
+    method public int getMinimumLoggingLevel();
+    method public android.os.Handler? getSchedulerHandler(android.os.Handler?);
+  }
+
+  public static final class CameraXConfig.Builder {
+    method public androidx.camera.core.CameraXConfig build();
+    method public static androidx.camera.core.CameraXConfig.Builder fromConfig(androidx.camera.core.CameraXConfig);
+    method public androidx.camera.core.CameraXConfig.Builder setAvailableCamerasLimiter(androidx.camera.core.CameraSelector);
+    method public androidx.camera.core.CameraXConfig.Builder setCameraExecutor(java.util.concurrent.Executor);
+    method public androidx.camera.core.CameraXConfig.Builder setMinimumLoggingLevel(@IntRange(from=android.util.Log.DEBUG, to=android.util.Log.ERROR) int);
+    method public androidx.camera.core.CameraXConfig.Builder setSchedulerHandler(android.os.Handler);
+  }
+
+  public static interface CameraXConfig.Provider {
+    method public androidx.camera.core.CameraXConfig getCameraXConfig();
+  }
+
+  @RequiresApi(21) public class ConcurrentCamera {
+    ctor public ConcurrentCamera(java.util.List<androidx.camera.core.Camera!>);
+    method public java.util.List<androidx.camera.core.Camera!> getCameras();
+  }
+
+  public static final class ConcurrentCamera.SingleCameraConfig {
+    ctor public ConcurrentCamera.SingleCameraConfig(androidx.camera.core.CameraSelector, androidx.camera.core.UseCaseGroup, androidx.lifecycle.LifecycleOwner);
+    method public androidx.camera.core.CameraSelector getCameraSelector();
+    method public androidx.lifecycle.LifecycleOwner getLifecycleOwner();
+    method public androidx.camera.core.UseCaseGroup getUseCaseGroup();
+  }
+
+  @RequiresApi(21) public final class DisplayOrientedMeteringPointFactory extends androidx.camera.core.MeteringPointFactory {
+    ctor public DisplayOrientedMeteringPointFactory(android.view.Display, androidx.camera.core.CameraInfo, float, float);
+  }
+
+  @RequiresApi(21) public final class DynamicRange {
+    ctor public DynamicRange(int, int);
+    method public int getBitDepth();
+    method public int getEncoding();
+    field public static final int BIT_DEPTH_10_BIT = 10; // 0xa
+    field public static final int BIT_DEPTH_8_BIT = 8; // 0x8
+    field public static final int BIT_DEPTH_UNSPECIFIED = 0; // 0x0
+    field public static final androidx.camera.core.DynamicRange DOLBY_VISION_10_BIT;
+    field public static final androidx.camera.core.DynamicRange DOLBY_VISION_8_BIT;
+    field public static final int ENCODING_DOLBY_VISION = 6; // 0x6
+    field public static final int ENCODING_HDR10 = 4; // 0x4
+    field public static final int ENCODING_HDR10_PLUS = 5; // 0x5
+    field public static final int ENCODING_HDR_UNSPECIFIED = 2; // 0x2
+    field public static final int ENCODING_HLG = 3; // 0x3
+    field public static final int ENCODING_SDR = 1; // 0x1
+    field public static final int ENCODING_UNSPECIFIED = 0; // 0x0
+    field public static final androidx.camera.core.DynamicRange HDR10_10_BIT;
+    field public static final androidx.camera.core.DynamicRange HDR10_PLUS_10_BIT;
+    field public static final androidx.camera.core.DynamicRange HDR_UNSPECIFIED_10_BIT;
+    field public static final androidx.camera.core.DynamicRange HLG_10_BIT;
+    field public static final androidx.camera.core.DynamicRange SDR;
+    field public static final androidx.camera.core.DynamicRange UNSPECIFIED;
+  }
+
+  @SuppressCompatibility @RequiresApi(21) @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalGetImage {
+  }
+
+  @SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalLensFacing {
+  }
+
+  @SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalUseCaseApi {
+  }
+
+  @SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalZeroShutterLag {
+  }
+
+  @RequiresApi(21) public interface ExposureState {
+    method public int getExposureCompensationIndex();
+    method public android.util.Range<java.lang.Integer!> getExposureCompensationRange();
+    method public android.util.Rational getExposureCompensationStep();
+    method public boolean isExposureCompensationSupported();
+  }
+
+  @RequiresApi(21) public interface ExtendableBuilder<T> {
+    method public T build();
+  }
+
+  @RequiresApi(21) public final class FocusMeteringAction {
+    method public long getAutoCancelDurationInMillis();
+    method public java.util.List<androidx.camera.core.MeteringPoint!> getMeteringPointsAe();
+    method public java.util.List<androidx.camera.core.MeteringPoint!> getMeteringPointsAf();
+    method public java.util.List<androidx.camera.core.MeteringPoint!> getMeteringPointsAwb();
+    method public boolean isAutoCancelEnabled();
+    field public static final int FLAG_AE = 2; // 0x2
+    field public static final int FLAG_AF = 1; // 0x1
+    field public static final int FLAG_AWB = 4; // 0x4
+  }
+
+  public static class FocusMeteringAction.Builder {
+    ctor public FocusMeteringAction.Builder(androidx.camera.core.MeteringPoint);
+    ctor public FocusMeteringAction.Builder(androidx.camera.core.MeteringPoint, int);
+    method public androidx.camera.core.FocusMeteringAction.Builder addPoint(androidx.camera.core.MeteringPoint);
+    method public androidx.camera.core.FocusMeteringAction.Builder addPoint(androidx.camera.core.MeteringPoint, int);
+    method public androidx.camera.core.FocusMeteringAction build();
+    method public androidx.camera.core.FocusMeteringAction.Builder disableAutoCancel();
+    method public androidx.camera.core.FocusMeteringAction.Builder setAutoCancelDuration(@IntRange(from=1) long, java.util.concurrent.TimeUnit);
+  }
+
+  @RequiresApi(21) public final class FocusMeteringResult {
+    method public boolean isFocusSuccessful();
+  }
+
+  @RequiresApi(21) public final class ImageAnalysis extends androidx.camera.core.UseCase {
+    method public void clearAnalyzer();
+    method @SuppressCompatibility @androidx.camera.core.ExperimentalUseCaseApi public java.util.concurrent.Executor? getBackgroundExecutor();
+    method public int getBackpressureStrategy();
+    method public int getImageQueueDepth();
+    method public int getOutputImageFormat();
+    method public androidx.camera.core.ResolutionInfo? getResolutionInfo();
+    method public androidx.camera.core.resolutionselector.ResolutionSelector? getResolutionSelector();
+    method public int getTargetRotation();
+    method public boolean isOutputImageRotationEnabled();
+    method public void setAnalyzer(java.util.concurrent.Executor, androidx.camera.core.ImageAnalysis.Analyzer);
+    method public void setTargetRotation(int);
+    field public static final int COORDINATE_SYSTEM_ORIGINAL = 0; // 0x0
+    field public static final int OUTPUT_IMAGE_FORMAT_RGBA_8888 = 2; // 0x2
+    field public static final int OUTPUT_IMAGE_FORMAT_YUV_420_888 = 1; // 0x1
+    field public static final int STRATEGY_BLOCK_PRODUCER = 1; // 0x1
+    field public static final int STRATEGY_KEEP_ONLY_LATEST = 0; // 0x0
+  }
+
+  public static interface ImageAnalysis.Analyzer {
+    method public void analyze(androidx.camera.core.ImageProxy);
+    method public default android.util.Size? getDefaultTargetResolution();
+    method public default int getTargetCoordinateSystem();
+    method public default void updateTransform(android.graphics.Matrix?);
+  }
+
+  public static final class ImageAnalysis.Builder implements androidx.camera.core.ExtendableBuilder<androidx.camera.core.ImageAnalysis> {
+    ctor public ImageAnalysis.Builder();
+    method public androidx.camera.core.ImageAnalysis build();
+    method public androidx.camera.core.ImageAnalysis.Builder setBackgroundExecutor(java.util.concurrent.Executor);
+    method public androidx.camera.core.ImageAnalysis.Builder setBackpressureStrategy(int);
+    method public androidx.camera.core.ImageAnalysis.Builder setImageQueueDepth(int);
+    method public androidx.camera.core.ImageAnalysis.Builder setOutputImageFormat(int);
+    method @RequiresApi(23) public androidx.camera.core.ImageAnalysis.Builder setOutputImageRotationEnabled(boolean);
+    method public androidx.camera.core.ImageAnalysis.Builder setResolutionSelector(androidx.camera.core.resolutionselector.ResolutionSelector);
+    method @Deprecated public androidx.camera.core.ImageAnalysis.Builder setTargetAspectRatio(int);
+    method public androidx.camera.core.ImageAnalysis.Builder setTargetName(String);
+    method @Deprecated public androidx.camera.core.ImageAnalysis.Builder setTargetResolution(android.util.Size);
+    method public androidx.camera.core.ImageAnalysis.Builder setTargetRotation(int);
+  }
+
+  @RequiresApi(21) public final class ImageCapture extends androidx.camera.core.UseCase {
+    method public int getCaptureMode();
+    method public int getFlashMode();
+    method @IntRange(from=1, to=100) public int getJpegQuality();
+    method public androidx.camera.core.ResolutionInfo? getResolutionInfo();
+    method public androidx.camera.core.resolutionselector.ResolutionSelector? getResolutionSelector();
+    method public int getTargetRotation();
+    method public void setCropAspectRatio(android.util.Rational);
+    method public void setFlashMode(int);
+    method public void setTargetRotation(int);
+    method public void takePicture(androidx.camera.core.ImageCapture.OutputFileOptions, java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageSavedCallback);
+    method public void takePicture(java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageCapturedCallback);
+    field public static final int CAPTURE_MODE_MAXIMIZE_QUALITY = 0; // 0x0
+    field public static final int CAPTURE_MODE_MINIMIZE_LATENCY = 1; // 0x1
+    field @SuppressCompatibility @androidx.camera.core.ExperimentalZeroShutterLag public static final int CAPTURE_MODE_ZERO_SHUTTER_LAG = 2; // 0x2
+    field public static final int ERROR_CAMERA_CLOSED = 3; // 0x3
+    field public static final int ERROR_CAPTURE_FAILED = 2; // 0x2
+    field public static final int ERROR_FILE_IO = 1; // 0x1
+    field public static final int ERROR_INVALID_CAMERA = 4; // 0x4
+    field public static final int ERROR_UNKNOWN = 0; // 0x0
+    field public static final int FLASH_MODE_AUTO = 0; // 0x0
+    field public static final int FLASH_MODE_OFF = 2; // 0x2
+    field public static final int FLASH_MODE_ON = 1; // 0x1
+  }
+
+  public static final class ImageCapture.Builder implements androidx.camera.core.ExtendableBuilder<androidx.camera.core.ImageCapture> {
+    ctor public ImageCapture.Builder();
+    method public androidx.camera.core.ImageCapture build();
+    method public androidx.camera.core.ImageCapture.Builder setCaptureMode(int);
+    method public androidx.camera.core.ImageCapture.Builder setFlashMode(int);
+    method public androidx.camera.core.ImageCapture.Builder setIoExecutor(java.util.concurrent.Executor);
+    method public androidx.camera.core.ImageCapture.Builder setJpegQuality(@IntRange(from=1, to=100) int);
+    method public androidx.camera.core.ImageCapture.Builder setResolutionSelector(androidx.camera.core.resolutionselector.ResolutionSelector);
+    method @Deprecated public androidx.camera.core.ImageCapture.Builder setTargetAspectRatio(int);
+    method public androidx.camera.core.ImageCapture.Builder setTargetName(String);
+    method @Deprecated public androidx.camera.core.ImageCapture.Builder setTargetResolution(android.util.Size);
+    method public androidx.camera.core.ImageCapture.Builder setTargetRotation(int);
+  }
+
+  public static final class ImageCapture.Metadata {
+    ctor public ImageCapture.Metadata();
+    method public android.location.Location? getLocation();
+    method public boolean isReversedHorizontal();
+    method public boolean isReversedVertical();
+    method public void setLocation(android.location.Location?);
+    method public void setReversedHorizontal(boolean);
+    method public void setReversedVertical(boolean);
+  }
+
+  public abstract static class ImageCapture.OnImageCapturedCallback {
+    ctor public ImageCapture.OnImageCapturedCallback();
+    method public void onCaptureSuccess(androidx.camera.core.ImageProxy);
+    method public void onError(androidx.camera.core.ImageCaptureException);
+  }
+
+  public static interface ImageCapture.OnImageSavedCallback {
+    method public void onError(androidx.camera.core.ImageCaptureException);
+    method public void onImageSaved(androidx.camera.core.ImageCapture.OutputFileResults);
+  }
+
+  public static final class ImageCapture.OutputFileOptions {
+  }
+
+  public static final class ImageCapture.OutputFileOptions.Builder {
+    ctor public ImageCapture.OutputFileOptions.Builder(android.content.ContentResolver, android.net.Uri, android.content.ContentValues);
+    ctor public ImageCapture.OutputFileOptions.Builder(java.io.File);
+    ctor public ImageCapture.OutputFileOptions.Builder(java.io.OutputStream);
+    method public androidx.camera.core.ImageCapture.OutputFileOptions build();
+    method public androidx.camera.core.ImageCapture.OutputFileOptions.Builder setMetadata(androidx.camera.core.ImageCapture.Metadata);
+  }
+
+  public static class ImageCapture.OutputFileResults {
+    method public android.net.Uri? getSavedUri();
+  }
+
+  @RequiresApi(21) public class ImageCaptureException extends java.lang.Exception {
+    ctor public ImageCaptureException(int, String, Throwable?);
+    method public int getImageCaptureError();
+  }
+
+  @RequiresApi(21) public interface ImageInfo {
+    method public int getRotationDegrees();
+    method public default android.graphics.Matrix getSensorToBufferTransformMatrix();
+    method public long getTimestamp();
+  }
+
+  public interface ImageProcessor {
+    method public androidx.camera.core.ImageProcessor.Response process(androidx.camera.core.ImageProcessor.Request) throws androidx.camera.core.ProcessingException;
+  }
+
+  public static interface ImageProcessor.Request {
+    method public androidx.camera.core.ImageProxy getInputImage();
+    method public int getOutputFormat();
+  }
+
+  public static interface ImageProcessor.Response {
+    method public androidx.camera.core.ImageProxy getOutputImage();
+  }
+
+  @RequiresApi(21) public interface ImageProxy extends java.lang.AutoCloseable {
+    method public void close();
+    method public android.graphics.Rect getCropRect();
+    method public int getFormat();
+    method public int getHeight();
+    method @SuppressCompatibility @androidx.camera.core.ExperimentalGetImage public android.media.Image? getImage();
+    method public androidx.camera.core.ImageInfo getImageInfo();
+    method public androidx.camera.core.ImageProxy.PlaneProxy![] getPlanes();
+    method public int getWidth();
+    method public void setCropRect(android.graphics.Rect?);
+    method public default android.graphics.Bitmap toBitmap();
+  }
+
+  public static interface ImageProxy.PlaneProxy {
+    method public java.nio.ByteBuffer getBuffer();
+    method public int getPixelStride();
+    method public int getRowStride();
+  }
+
+  @RequiresApi(21) public class InitializationException extends java.lang.Exception {
+    ctor public InitializationException(String?);
+    ctor public InitializationException(String?, Throwable?);
+    ctor public InitializationException(Throwable?);
+  }
+
+  @RequiresApi(21) public class MeteringPoint {
+    method public float getSize();
+  }
+
+  @RequiresApi(21) public abstract class MeteringPointFactory {
+    method public final androidx.camera.core.MeteringPoint createPoint(float, float);
+    method public final androidx.camera.core.MeteringPoint createPoint(float, float, float);
+    method public static float getDefaultPointSize();
+  }
+
+  @RequiresApi(21) public class MirrorMode {
+    field public static final int MIRROR_MODE_OFF = 0; // 0x0
+    field public static final int MIRROR_MODE_ON = 1; // 0x1
+    field public static final int MIRROR_MODE_ON_FRONT_ONLY = 2; // 0x2
+  }
+
+  @RequiresApi(21) public final class Preview extends androidx.camera.core.UseCase {
+    method public androidx.camera.core.ResolutionInfo? getResolutionInfo();
+    method public androidx.camera.core.resolutionselector.ResolutionSelector? getResolutionSelector();
+    method public android.util.Range<java.lang.Integer!> getTargetFrameRate();
+    method public int getTargetRotation();
+    method @UiThread public void setSurfaceProvider(androidx.camera.core.Preview.SurfaceProvider?);
+    method @UiThread public void setSurfaceProvider(java.util.concurrent.Executor, androidx.camera.core.Preview.SurfaceProvider?);
+    method public void setTargetRotation(int);
+  }
+
+  public static final class Preview.Builder implements androidx.camera.core.ExtendableBuilder<androidx.camera.core.Preview> {
+    ctor public Preview.Builder();
+    method public androidx.camera.core.Preview build();
+    method public androidx.camera.core.Preview.Builder setResolutionSelector(androidx.camera.core.resolutionselector.ResolutionSelector);
+    method @Deprecated public androidx.camera.core.Preview.Builder setTargetAspectRatio(int);
+    method public androidx.camera.core.Preview.Builder setTargetFrameRate(android.util.Range<java.lang.Integer!>);
+    method public androidx.camera.core.Preview.Builder setTargetName(String);
+    method @Deprecated public androidx.camera.core.Preview.Builder setTargetResolution(android.util.Size);
+    method public androidx.camera.core.Preview.Builder setTargetRotation(int);
+  }
+
+  public static interface Preview.SurfaceProvider {
+    method public void onSurfaceRequested(androidx.camera.core.SurfaceRequest);
+  }
+
+  public class ProcessingException extends java.lang.Exception {
+    ctor public ProcessingException();
+  }
+
+  @RequiresApi(21) public class ResolutionInfo {
+    ctor public ResolutionInfo(android.util.Size, android.graphics.Rect, int);
+    method public android.graphics.Rect getCropRect();
+    method public android.util.Size getResolution();
+    method public int getRotationDegrees();
+  }
+
+  @RequiresApi(21) public class SurfaceOrientedMeteringPointFactory extends androidx.camera.core.MeteringPointFactory {
+    ctor public SurfaceOrientedMeteringPointFactory(float, float);
+    ctor public SurfaceOrientedMeteringPointFactory(float, float, androidx.camera.core.UseCase);
+  }
+
+  public interface SurfaceOutput extends java.io.Closeable {
+    method public void close();
+    method public android.util.Size getSize();
+    method public android.view.Surface getSurface(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.camera.core.SurfaceOutput.Event!>);
+    method public int getTargets();
+    method public void updateTransformMatrix(float[], float[]);
+  }
+
+  @com.google.auto.value.AutoValue public abstract static class SurfaceOutput.Event {
+    method public abstract int getEventCode();
+    method public abstract androidx.camera.core.SurfaceOutput getSurfaceOutput();
+    field public static final int EVENT_REQUEST_CLOSE = 0; // 0x0
+  }
+
+  public interface SurfaceProcessor {
+    method public void onInputSurface(androidx.camera.core.SurfaceRequest) throws androidx.camera.core.ProcessingException;
+    method public void onOutputSurface(androidx.camera.core.SurfaceOutput) throws androidx.camera.core.ProcessingException;
+  }
+
+  @RequiresApi(21) public final class SurfaceRequest {
+    method public void addRequestCancellationListener(java.util.concurrent.Executor, Runnable);
+    method public void clearTransformationInfoListener();
+    method public androidx.camera.core.DynamicRange getDynamicRange();
+    method public android.util.Size getResolution();
+    method public boolean invalidate();
+    method public void provideSurface(android.view.Surface, java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.camera.core.SurfaceRequest.Result!>);
+    method public void setTransformationInfoListener(java.util.concurrent.Executor, androidx.camera.core.SurfaceRequest.TransformationInfoListener);
+    method public boolean willNotProvideSurface();
+  }
+
+  @com.google.auto.value.AutoValue public abstract static class SurfaceRequest.Result {
+    method public abstract int getResultCode();
+    method public abstract android.view.Surface getSurface();
+    field public static final int RESULT_INVALID_SURFACE = 2; // 0x2
+    field public static final int RESULT_REQUEST_CANCELLED = 1; // 0x1
+    field public static final int RESULT_SURFACE_ALREADY_PROVIDED = 3; // 0x3
+    field public static final int RESULT_SURFACE_USED_SUCCESSFULLY = 0; // 0x0
+    field public static final int RESULT_WILL_NOT_PROVIDE_SURFACE = 4; // 0x4
+  }
+
+  @com.google.auto.value.AutoValue public abstract static class SurfaceRequest.TransformationInfo {
+    method public abstract android.graphics.Rect getCropRect();
+    method public abstract int getRotationDegrees();
+  }
+
+  public static interface SurfaceRequest.TransformationInfoListener {
+    method public void onTransformationInfoUpdate(androidx.camera.core.SurfaceRequest.TransformationInfo);
+  }
+
+  @RequiresApi(21) public class TorchState {
+    field public static final int OFF = 0; // 0x0
+    field public static final int ON = 1; // 0x1
+  }
+
+  @RequiresApi(21) public abstract class UseCase {
+    method public static int snapToSurfaceRotation(@IntRange(from=0, to=359) int);
+  }
+
+  @RequiresApi(21) public final class UseCaseGroup {
+    method public java.util.List<androidx.camera.core.CameraEffect!> getEffects();
+    method public java.util.List<androidx.camera.core.UseCase!> getUseCases();
+    method public androidx.camera.core.ViewPort? getViewPort();
+  }
+
+  public static final class UseCaseGroup.Builder {
+    ctor public UseCaseGroup.Builder();
+    method public androidx.camera.core.UseCaseGroup.Builder addEffect(androidx.camera.core.CameraEffect);
+    method public androidx.camera.core.UseCaseGroup.Builder addUseCase(androidx.camera.core.UseCase);
+    method public androidx.camera.core.UseCaseGroup build();
+    method public androidx.camera.core.UseCaseGroup.Builder setViewPort(androidx.camera.core.ViewPort);
+  }
+
+  @RequiresApi(21) public final class ViewPort {
+    method public android.util.Rational getAspectRatio();
+    method public int getLayoutDirection();
+    method public int getRotation();
+    method public int getScaleType();
+    field public static final int FILL_CENTER = 1; // 0x1
+    field public static final int FILL_END = 2; // 0x2
+    field public static final int FILL_START = 0; // 0x0
+    field public static final int FIT = 3; // 0x3
+  }
+
+  public static final class ViewPort.Builder {
+    ctor public ViewPort.Builder(android.util.Rational, int);
+    method public androidx.camera.core.ViewPort build();
+    method public androidx.camera.core.ViewPort.Builder setLayoutDirection(int);
+    method public androidx.camera.core.ViewPort.Builder setScaleType(int);
+  }
+
+  @RequiresApi(21) public interface ZoomState {
+    method public float getLinearZoom();
+    method public float getMaxZoomRatio();
+    method public float getMinZoomRatio();
+    method public float getZoomRatio();
+  }
+
+}
+
+package androidx.camera.core.resolutionselector {
+
+  @RequiresApi(21) public final class AspectRatioStrategy {
+    ctor public AspectRatioStrategy(int, int);
+    method public int getFallbackRule();
+    method public int getPreferredAspectRatio();
+    field public static final int FALLBACK_RULE_AUTO = 1; // 0x1
+    field public static final int FALLBACK_RULE_NONE = 0; // 0x0
+    field public static final androidx.camera.core.resolutionselector.AspectRatioStrategy RATIO_16_9_FALLBACK_AUTO_STRATEGY;
+    field public static final androidx.camera.core.resolutionselector.AspectRatioStrategy RATIO_4_3_FALLBACK_AUTO_STRATEGY;
+  }
+
+  @RequiresApi(21) public interface ResolutionFilter {
+    method public java.util.List<android.util.Size!> filter(java.util.List<android.util.Size!>, int);
+  }
+
+  @RequiresApi(21) public final class ResolutionSelector {
+    method public int getAllowedResolutionMode();
+    method public androidx.camera.core.resolutionselector.AspectRatioStrategy getAspectRatioStrategy();
+    method public androidx.camera.core.resolutionselector.ResolutionFilter? getResolutionFilter();
+    method public androidx.camera.core.resolutionselector.ResolutionStrategy? getResolutionStrategy();
+    field public static final int PREFER_CAPTURE_RATE_OVER_HIGHER_RESOLUTION = 0; // 0x0
+    field public static final int PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE = 1; // 0x1
+  }
+
+  public static final class ResolutionSelector.Builder {
+    ctor public ResolutionSelector.Builder();
+    method public androidx.camera.core.resolutionselector.ResolutionSelector build();
+    method public androidx.camera.core.resolutionselector.ResolutionSelector.Builder setAllowedResolutionMode(int);
+    method public androidx.camera.core.resolutionselector.ResolutionSelector.Builder setAspectRatioStrategy(androidx.camera.core.resolutionselector.AspectRatioStrategy);
+    method public androidx.camera.core.resolutionselector.ResolutionSelector.Builder setResolutionFilter(androidx.camera.core.resolutionselector.ResolutionFilter);
+    method public androidx.camera.core.resolutionselector.ResolutionSelector.Builder setResolutionStrategy(androidx.camera.core.resolutionselector.ResolutionStrategy);
+  }
+
+  @RequiresApi(21) public final class ResolutionStrategy {
+    ctor public ResolutionStrategy(android.util.Size, int);
+    method public android.util.Size? getBoundSize();
+    method public int getFallbackRule();
+    field public static final int FALLBACK_RULE_CLOSEST_HIGHER = 2; // 0x2
+    field public static final int FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER = 1; // 0x1
+    field public static final int FALLBACK_RULE_CLOSEST_LOWER = 4; // 0x4
+    field public static final int FALLBACK_RULE_CLOSEST_LOWER_THEN_HIGHER = 3; // 0x3
+    field public static final int FALLBACK_RULE_NONE = 0; // 0x0
+    field public static final androidx.camera.core.resolutionselector.ResolutionStrategy HIGHEST_AVAILABLE_STRATEGY;
+  }
+
+}
+
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/UseCaseTest.kt b/camera/camera-core/src/androidTest/java/androidx/camera/core/UseCaseTest.kt
index 9850f20..665f69c 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/UseCaseTest.kt
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/UseCaseTest.kt
@@ -33,6 +33,7 @@
 import androidx.camera.core.impl.StreamSpec
 import androidx.camera.core.impl.UseCaseConfigFactory
 import androidx.camera.core.internal.CameraUseCaseAdapter
+import androidx.camera.core.internal.TargetConfig.OPTION_TARGET_NAME
 import androidx.camera.testing.fakes.FakeCamera
 import androidx.camera.testing.fakes.FakeCameraCoordinator
 import androidx.camera.testing.fakes.FakeCameraDeviceSurfaceManager
@@ -312,6 +313,24 @@
         }
     }
 
+    @Test
+    fun keepUseCaseTargetName_whenMergingConfigs() {
+        val targetName = "Fake-UseCase-TargetName"
+        val fakeUseCase = FakeUseCaseConfig.Builder().setTargetName(targetName).build()
+        val extendedConfig = FakeUseCaseConfig.Builder().apply {
+            mutableConfig.insertOption(OPTION_TARGET_NAME, "Extended-Config-TargetName")
+        }.useCaseConfig
+        val defaultConfig = FakeUseCaseConfig.Builder().apply {
+            mutableConfig.insertOption(OPTION_TARGET_NAME, "Default-Config-TargetName")
+        }.useCaseConfig
+        val mergedConfig = fakeUseCase.mergeConfigs(
+            FakeCameraInfoInternal(0, CameraSelector.LENS_FACING_BACK),
+            extendedConfig,
+            defaultConfig
+        )
+        assertThat(mergedConfig.targetName).isEqualTo(targetName)
+    }
+
     private fun createFakeUseCase(
         targetRotation: Int = Surface.ROTATION_0,
         mirrorMode: Int? = null,
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/Preview.java b/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
index e36507c..a7686c7 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
@@ -454,11 +454,14 @@
     }
 
     private void sendSurfaceRequest() {
+        // App receives TransformationInfo when 1) the listener is set or 2) the info is sent. We
+        // should send the info before the listen is set so the app only receives once.
+        sendTransformationInfoIfReady();
+
+        // Send the SurfaceRequest.
         final SurfaceProvider surfaceProvider = checkNotNull(mSurfaceProvider);
         final SurfaceRequest surfaceRequest = checkNotNull(mCurrentSurfaceRequest);
-
         mSurfaceProviderExecutor.execute(() -> surfaceProvider.onSurfaceRequested(surfaceRequest));
-        sendTransformationInfoIfReady();
     }
 
     /**
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java b/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java
index a002de7..bd07576 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java
@@ -241,12 +241,7 @@
         // If any options need special handling, this is the place to do it. For now we'll just copy
         // over all options.
         for (Option<?> opt : mUseCaseConfig.listOptions()) {
-            @SuppressWarnings("unchecked") // Options/values are being copied directly
-            Option<Object> objectOpt = (Option<Object>) opt;
-
-            mergedConfig.insertOption(objectOpt,
-                    mUseCaseConfig.getOptionPriority(opt),
-                    mUseCaseConfig.retrieveOption(objectOpt));
+            Config.mergeOptionValue(mergedConfig, mergedConfig, mUseCaseConfig, opt);
         }
 
         if (extendedConfig != null) {
@@ -258,9 +253,7 @@
                 if (objectOpt.getId().equals(TargetConfig.OPTION_TARGET_NAME.getId())) {
                     continue;
                 }
-                mergedConfig.insertOption(objectOpt,
-                        extendedConfig.getOptionPriority(opt),
-                        extendedConfig.retrieveOption(objectOpt));
+                Config.mergeOptionValue(mergedConfig, mergedConfig, extendedConfig, opt);
             }
         }
 
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/concurrent/CameraCoordinator.java b/camera/camera-core/src/main/java/androidx/camera/core/concurrent/CameraCoordinator.java
index 60d6be3..b98d11e 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/concurrent/CameraCoordinator.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/concurrent/CameraCoordinator.java
@@ -130,6 +130,11 @@
     void removeListener(@NonNull ConcurrentCameraModeListener listener);
 
     /**
+     * Clean up all the resources when CameraX shutdown.
+     */
+    void shutdown();
+
+    /**
      * Interface for concurrent camera mode update.
      *
      * <p>Everytime user changes {@link CameraOperatingMode}, the observer will be notified and
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/JpegBytes2Disk.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/JpegBytes2Disk.java
index 55b074b..cec0faa 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/JpegBytes2Disk.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/JpegBytes2Disk.java
@@ -24,6 +24,7 @@
 import android.net.Uri;
 import android.os.Build;
 import android.provider.MediaStore;
+import android.util.Range;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -31,6 +32,7 @@
 import androidx.camera.core.ImageCapture;
 import androidx.camera.core.ImageCaptureException;
 import androidx.camera.core.impl.utils.Exif;
+import androidx.camera.core.internal.compat.workaround.InvalidJpegDataParser;
 import androidx.camera.core.processing.Operation;
 import androidx.camera.core.processing.Packet;
 
@@ -109,7 +111,16 @@
     private static void writeBytesToFile(
             @NonNull File tempFile, @NonNull byte[] bytes) throws ImageCaptureException {
         try (FileOutputStream output = new FileOutputStream(tempFile)) {
-            output.write(bytes);
+            InvalidJpegDataParser invalidJpegDataParser = new InvalidJpegDataParser();
+            Range<Integer> invalidDataRange = invalidJpegDataParser.getInvalidDataRange(bytes);
+
+            if (invalidDataRange != null) {
+                output.write(bytes, 0, invalidDataRange.getLower());
+                output.write(bytes, invalidDataRange.getUpper() + 1,
+                        (bytes.length - invalidDataRange.getUpper() - 1));
+            } else {
+                output.write(bytes);
+            }
         } catch (IOException e) {
             throw new ImageCaptureException(ERROR_FILE_IO, "Failed to write to temp file", e);
         }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/Config.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/Config.java
index 67493a2..ab1314e 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/Config.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/Config.java
@@ -323,27 +323,44 @@
             // If any options need special handling, this is the place to do it. For now we'll
             // just copy over all options.
             for (Config.Option<?> opt : extendedConfig.listOptions()) {
-                @SuppressWarnings("unchecked") // Options/values are being copied directly
-                Config.Option<Object> objectOpt = (Config.Option<Object>) opt;
-
-                // ResolutionSelector needs special handling to merge the underlying settings.
-                if (Objects.equals(objectOpt, ImageOutputConfig.OPTION_RESOLUTION_SELECTOR)) {
-                    ResolutionSelector resolutionSelectorToOverride =
-                            (ResolutionSelector) extendedConfig.retrieveOption(objectOpt);
-                    ResolutionSelector baseResolutionSelector =
-                            (ResolutionSelector) baseConfig.retrieveOption(objectOpt);
-                    mergedConfig.insertOption(objectOpt,
-                            extendedConfig.getOptionPriority(opt),
-                            ResolutionSelectorUtil.overrideResolutionSelectors(
-                                    baseResolutionSelector, resolutionSelectorToOverride));
-                } else {
-                    mergedConfig.insertOption(objectOpt,
-                            extendedConfig.getOptionPriority(opt),
-                            extendedConfig.retrieveOption(objectOpt));
-                }
+                mergeOptionValue(mergedConfig, baseConfig, extendedConfig, opt);
             }
         }
 
         return OptionsBundle.from(mergedConfig);
     }
+
+    /**
+     * Merges a specific option value from two configs.
+     *
+     * @param mergedConfig   the final output config
+     * @param baseConfig     the base config contains the option value which might be overridden by
+     *                       the corresponding option value in the extend config.
+     * @param extendedConfig the extended config contains the option value which might override
+     *                       the corresponding option value in the base config.
+     * @param opt            the option to merge
+     */
+    static void mergeOptionValue(@NonNull MutableOptionsBundle mergedConfig,
+            @NonNull Config baseConfig,
+            @NonNull Config extendedConfig,
+            @NonNull Option<?> opt) {
+        @SuppressWarnings("unchecked") // Options/values are being copied directly
+        Config.Option<Object> objectOpt = (Config.Option<Object>) opt;
+
+        // ResolutionSelector needs special handling to merge the underlying settings.
+        if (Objects.equals(objectOpt, ImageOutputConfig.OPTION_RESOLUTION_SELECTOR)) {
+            ResolutionSelector resolutionSelectorToOverride =
+                    (ResolutionSelector) extendedConfig.retrieveOption(objectOpt, null);
+            ResolutionSelector baseResolutionSelector =
+                    (ResolutionSelector) baseConfig.retrieveOption(objectOpt, null);
+            mergedConfig.insertOption(objectOpt,
+                    extendedConfig.getOptionPriority(opt),
+                    ResolutionSelectorUtil.overrideResolutionSelectors(
+                            baseResolutionSelector, resolutionSelectorToOverride));
+        } else {
+            mergedConfig.insertOption(objectOpt,
+                    extendedConfig.getOptionPriority(opt),
+                    extendedConfig.retrieveOption(objectOpt));
+        }
+    }
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/quirk/DeviceQuirksLoader.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/quirk/DeviceQuirksLoader.java
index 801f381..4735c7c 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/quirk/DeviceQuirksLoader.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/quirk/DeviceQuirksLoader.java
@@ -52,6 +52,9 @@
         if (LowMemoryQuirk.load()) {
             quirks.add(new LowMemoryQuirk());
         }
+        if (LargeJpegImageQuirk.load()) {
+            quirks.add(new LargeJpegImageQuirk());
+        }
 
         return quirks;
     }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/quirk/LargeJpegImageQuirk.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/quirk/LargeJpegImageQuirk.java
new file mode 100644
index 0000000..e73a437
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/quirk/LargeJpegImageQuirk.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.core.internal.compat.quirk;
+
+import android.os.Build;
+
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.impl.Quirk;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+
+/**
+ * <p>QuirkSummary
+ *     Bug Id: 288828159
+ *     Description: Quirk required to check whether the captured JPEG image contains redundant
+ *                  0's padding data. For example, Samsung A5 (2017) series devices have the
+ *                  problem and result in the output JPEG image to be extremely large (about 32 MB).
+ *     Device(s): Samsung Galaxy A5 (2017) series
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public final class LargeJpegImageQuirk implements Quirk {
+
+    private static final Set<String> DEVICE_MODELS = new HashSet<>(Arrays.asList(
+            // Samsung Galaxy A5 series devices
+            "SM-A520F",
+            "SM-A520X",
+            "SM-A520W",
+            "SM-A520K",
+            "SM-A520L",
+            "SM-A520S"
+    ));
+
+    static boolean load() {
+        return DEVICE_MODELS.contains(Build.MODEL.toUpperCase(Locale.US));
+    }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/workaround/InvalidJpegDataParser.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/workaround/InvalidJpegDataParser.java
new file mode 100644
index 0000000..4f3bdbb
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/compat/workaround/InvalidJpegDataParser.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.core.internal.compat.workaround;
+
+import android.util.Range;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.internal.compat.quirk.DeviceQuirks;
+import androidx.camera.core.internal.compat.quirk.LargeJpegImageQuirk;
+
+/**
+ * Workaround to check whether the captured JPEG image contains redundant 0's padding data.
+ *
+ * @see LargeJpegImageQuirk
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public class InvalidJpegDataParser {
+    private final boolean mHasQuirk = DeviceQuirks.get(LargeJpegImageQuirk.class) != null;
+
+    /**
+     * Retrieves the invalid data position range from the input JPEG byte data array.
+     *
+     * @return the invalid data position range of the JPEG byte data, or {@code null} when
+     * invalid data position range can't be found.
+     */
+    @Nullable
+    public Range<Integer> getInvalidDataRange(@NonNull byte[] bytes) {
+        if (!mHasQuirk) {
+            return null;
+        }
+
+        // Parses the JFIF segments from the start of the JPEG image data
+        int markPosition = 0x2;
+        while (true) {
+            // Breaks the while-loop and return null if the mark byte can't be correctly found.
+            if (markPosition + 4 > bytes.length || bytes[markPosition] != ((byte) 0xff)) {
+                return null;
+            }
+
+            int segmentLength =
+                    ((bytes[markPosition + 2] & 0xff) << 8) | (bytes[markPosition + 3] & 0xff);
+
+            // Breaks the while-loop when finding the SOS (FF DA) mark
+            if (bytes[markPosition] == ((byte) 0xff) && bytes[markPosition + 1] == ((byte) 0xda)) {
+                break;
+            }
+            markPosition += segmentLength + 2;
+        }
+
+        // Finds the EOI (FF D9) mark to know the end position of the valid compressed image data
+        int eoiPosition = markPosition + 2;
+
+        while (true) {
+            // Breaks the while-loop and return null if EOI mark can't be found
+            if (eoiPosition + 2 > bytes.length) {
+                return null;
+            }
+
+            if (bytes[eoiPosition] == ((byte) 0xff) && bytes[eoiPosition + 1] == ((byte) 0xd9)) {
+                break;
+            }
+            eoiPosition++;
+        }
+
+        // The captured images might have non-zero data after the EOI byte. Those valid data should
+        // be kept. Searches the final valid byte from the end side can save the processing time.
+        int finalValidBytePosition = bytes.length - 1;
+
+        while (true) {
+            // Breaks the while-loop and return null if finalValidBytePosition has reach the EOI
+            // mark position.
+            if (finalValidBytePosition <= eoiPosition) {
+                return null;
+            }
+
+            if (bytes[finalValidBytePosition] == ((byte) 0xff)) {
+                break;
+            }
+            finalValidBytePosition--;
+        }
+
+        if (finalValidBytePosition - 1 > eoiPosition + 2) {
+            return Range.create(eoiPosition + 2, finalValidBytePosition - 1);
+        } else {
+            return null;
+        }
+    }
+}
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/PreviewTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/PreviewTest.kt
index 768b8bb..7f43051 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/PreviewTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/PreviewTest.kt
@@ -28,12 +28,14 @@
 import android.util.Rational
 import android.util.Size
 import android.view.Surface
+import android.view.Surface.ROTATION_90
 import androidx.annotation.RequiresApi
 import androidx.camera.core.CameraEffect.IMAGE_CAPTURE
 import androidx.camera.core.CameraEffect.PREVIEW
 import androidx.camera.core.CameraEffect.VIDEO_CAPTURE
 import androidx.camera.core.CameraSelector.LENS_FACING_FRONT
 import androidx.camera.core.MirrorMode.MIRROR_MODE_ON_FRONT_ONLY
+import androidx.camera.core.Preview.SurfaceProvider
 import androidx.camera.core.SurfaceRequest.TransformationInfo
 import androidx.camera.core.impl.CameraFactory
 import androidx.camera.core.impl.CameraThreadConfig
@@ -45,6 +47,7 @@
 import androidx.camera.core.impl.UseCaseConfig
 import androidx.camera.core.impl.UseCaseConfigFactory
 import androidx.camera.core.impl.utils.executor.CameraXExecutors
+import androidx.camera.core.impl.utils.executor.CameraXExecutors.directExecutor
 import androidx.camera.core.impl.utils.executor.CameraXExecutors.mainThreadExecutor
 import androidx.camera.core.internal.CameraUseCaseAdapter
 import androidx.camera.core.internal.utils.SizeUtil
@@ -63,6 +66,8 @@
 import com.google.common.truth.Truth.assertThat
 import java.util.Collections
 import java.util.concurrent.ExecutionException
+import java.util.concurrent.Semaphore
+import java.util.concurrent.TimeUnit
 import org.junit.After
 import org.junit.Assert
 import org.junit.Before
@@ -165,6 +170,23 @@
     }
 
     @Test
+    fun attachPreview_receiveTransformationInfoOnlyOnce() {
+        // Arrange.
+        val semaphore = Semaphore(0)
+
+        // Act: create preview and listen to transformation info.
+        createPreview(surfaceProvider = {
+            it.setTransformationInfoListener(directExecutor()) {
+                semaphore.release()
+            }
+        })
+
+        // Assert: only receive transformation info once.
+        assertThat(semaphore.tryAcquire(1, 1, TimeUnit.SECONDS)).isTrue()
+        assertThat(semaphore.tryAcquire(2, 1, TimeUnit.SECONDS)).isFalse()
+    }
+
+    @Test
     fun createPreview_sessionConfigMatchesStreamSpec() {
         // Act: Create a preview use case.
         val preview = createPreview()
@@ -477,7 +499,7 @@
         val preview = createPreview(
             effect,
             frontCamera,
-            targetRotation = Surface.ROTATION_90
+            targetRotation = ROTATION_90
         )
         assertThat(preview.cameraEdge.hasCameraTransform()).isTrue()
         // Assert: rotationDegrees is not flipped.
@@ -491,7 +513,7 @@
         val preview = createPreview(
             effect,
             frontCamera,
-            targetRotation = Surface.ROTATION_90
+            targetRotation = ROTATION_90
         )
         // Assert: rotationDegrees is 0.
         assertThat(preview.cameraEdge.rotationDegrees).isEqualTo(0)
@@ -504,7 +526,7 @@
         val preview = createPreview(
             effect,
             frontCamera,
-            targetRotation = Surface.ROTATION_90
+            targetRotation = ROTATION_90
         )
         // Assert
         assertThat(preview.cameraEdge.hasCameraTransform()).isFalse()
@@ -518,7 +540,7 @@
         val preview = createPreview(
             effect,
             frontCamera,
-            targetRotation = Surface.ROTATION_90
+            targetRotation = ROTATION_90
         )
         // Assert
         assertThat(preview.cameraEdge.mirroring).isFalse()
@@ -802,13 +824,15 @@
     private fun createPreview(
         effect: CameraEffect? = null,
         camera: FakeCamera = backCamera,
-        targetRotation: Int = Surface.ROTATION_0
+        targetRotation: Int = ROTATION_90,
+        surfaceProvider: SurfaceProvider = SurfaceProvider {
+        }
     ): Preview {
         previewToDetach = Preview.Builder()
             .setTargetRotation(targetRotation)
             .build()
         previewToDetach.effect = effect
-        previewToDetach.setSurfaceProvider(CameraXExecutors.directExecutor()) {}
+        previewToDetach.setSurfaceProvider(directExecutor(), surfaceProvider)
         val previewConfig = PreviewConfig(
             cameraXConfig.getUseCaseConfigFactoryProvider(null)!!.newInstance(context).getConfig(
                 UseCaseConfigFactory.CaptureType.PREVIEW,
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/impl/ConfigTest.java b/camera/camera-core/src/test/java/androidx/camera/core/impl/ConfigTest.java
index 7967b76..3943a57 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/impl/ConfigTest.java
+++ b/camera/camera-core/src/test/java/androidx/camera/core/impl/ConfigTest.java
@@ -19,11 +19,18 @@
 import static androidx.camera.core.impl.Config.OptionPriority.ALWAYS_OVERRIDE;
 import static androidx.camera.core.impl.Config.OptionPriority.OPTIONAL;
 import static androidx.camera.core.impl.Config.OptionPriority.REQUIRED;
+import static androidx.camera.core.impl.ImageOutputConfig.OPTION_RESOLUTION_SELECTOR;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import android.os.Build;
 
+import androidx.camera.core.resolutionselector.AspectRatioStrategy;
+import androidx.camera.core.resolutionselector.ResolutionFilter;
+import androidx.camera.core.resolutionselector.ResolutionSelector;
+import androidx.camera.core.resolutionselector.ResolutionStrategy;
+import androidx.camera.testing.fakes.FakeUseCaseConfig;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
@@ -126,4 +133,32 @@
         assertThat(Config.hasConflict(ALWAYS_OVERRIDE, REQUIRED)).isFalse();
     }
 
+    @Test
+    public void overrideResolutionSelectorCorrectly() {
+        MutableOptionsBundle mergedConfig = MutableOptionsBundle.create();
+        MutableConfig baseConfig = new FakeUseCaseConfig.Builder().getMutableConfig();
+        baseConfig.insertOption(OPTION_RESOLUTION_SELECTOR,
+                new ResolutionSelector.Builder().setAspectRatioStrategy(
+                                AspectRatioStrategy.RATIO_4_3_FALLBACK_AUTO_STRATEGY)
+                        .setResolutionStrategy(
+                                ResolutionStrategy.HIGHEST_AVAILABLE_STRATEGY).build());
+        MutableConfig extendedConfig = new FakeUseCaseConfig.Builder().getMutableConfig();
+        ResolutionFilter resolutionFilter = (supportedSizes, rotationDegrees) -> null;
+        extendedConfig.insertOption(OPTION_RESOLUTION_SELECTOR,
+                new ResolutionSelector.Builder().setAspectRatioStrategy(
+                                AspectRatioStrategy.RATIO_16_9_FALLBACK_AUTO_STRATEGY)
+                        .setResolutionFilter(resolutionFilter).build());
+        Config.mergeOptionValue(mergedConfig, baseConfig, extendedConfig,
+                OPTION_RESOLUTION_SELECTOR);
+
+        ResolutionSelector mergedResolutionSelector =
+                mergedConfig.retrieveOption(OPTION_RESOLUTION_SELECTOR);
+
+        assertThat(mergedResolutionSelector.getAspectRatioStrategy()).isEqualTo(
+                AspectRatioStrategy.RATIO_16_9_FALLBACK_AUTO_STRATEGY);
+        assertThat(mergedResolutionSelector.getResolutionStrategy()).isEqualTo(
+                ResolutionStrategy.HIGHEST_AVAILABLE_STRATEGY);
+        assertThat(mergedResolutionSelector.getResolutionFilter()).isEqualTo(resolutionFilter);
+    }
+
 }
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/internal/compat/workaround/InvalidJpegDataParserTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/internal/compat/workaround/InvalidJpegDataParserTest.kt
new file mode 100644
index 0000000..3623de8
--- /dev/null
+++ b/camera/camera-core/src/test/java/androidx/camera/core/internal/compat/workaround/InvalidJpegDataParserTest.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.core.internal.compat.workaround
+
+import android.os.Build
+import android.util.Range
+import com.google.common.truth.Truth.assertThat
+import java.util.Objects
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.ParameterizedRobolectricTestRunner
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+import org.robolectric.util.ReflectionHelpers
+
+// Correct Jpeg byte array 1 that has additional segment data in the end of the file
+private val correctJpegByteArray1 = listOf(
+    0xff, 0xd8, 0xff, 0xe1, 0x00, 0x06, 0x55, 0x55, 0x55, 0x55, 0xff, 0xda, 0x99, 0x99, 0x99, 0x99,
+    0xff, 0xd9, 0xff, 0x00, 0x00, 0xe5, 0x92, 0x00, 0x00, 0xe6, 0x01, 0x00
+).map { it.toByte() }.toByteArray()
+
+// Correct Jpeg byte array 2 that doesn't have additional segment data in the end of the file
+private val correctJpegByteArray2 = listOf(
+    0xff, 0xd8, 0xff, 0xe1, 0x00, 0x06, 0x55, 0x55, 0x55, 0x55, 0xff, 0xda, 0x99, 0x99, 0x99, 0x99,
+    0xff, 0xd9
+).map { it.toByte() }.toByteArray()
+
+// Invalid data starts from position 18 to position 31.
+private val problematicJpegByteArray = listOf(
+    0xff, 0xd8, 0xff, 0xe1, 0x00, 0x06, 0x55, 0x55, 0x55, 0x55, 0xff, 0xda, 0x99, 0x99, 0x99, 0x99,
+    0xff, 0xd9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xff, 0x00, 0x00, 0xe5, 0x92, 0x00, 0x00, 0xe6, 0x01, 0x00
+).map { it.toByte() }.toByteArray()
+
+// Invalid very short data
+private val invalidVeryShortData = listOf(
+    0xff, 0xd8
+).map { it.toByte() }.toByteArray()
+
+// Invalid data without SOS byte
+private val invalidNoSosData = listOf(
+    0xff, 0xd8, 0xff, 0xe1, 0x00, 0x06, 0x55, 0x55, 0x55, 0x55, 0xff, 0x00, 0x99, 0x99, 0x99, 0x99,
+    0xff, 0xd9, 0xff, 0x00, 0x00, 0xe5, 0x92, 0x00, 0x00, 0xe6, 0x01, 0x00
+).map { it.toByte() }.toByteArray()
+
+// Invalid data without EOI byte
+private val invalidNoEoiData = listOf(
+    0xff, 0xd8, 0xff, 0xe1, 0x00, 0x06, 0x55, 0x55, 0x55, 0x55, 0xff, 0xda, 0x99, 0x99, 0x99, 0x99,
+    0xff, 0x00, 0xff, 0x00, 0x00, 0xe5, 0x92, 0x00, 0x00, 0xe6, 0x01, 0x00
+).map { it.toByte() }.toByteArray()
+
+@RunWith(ParameterizedRobolectricTestRunner::class)
+@DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class InvalidJpegDataParserTest(
+    private val model: String,
+    private val data: ByteArray,
+    private val range: Range<*>?,
+    ) {
+
+    companion object {
+        @JvmStatic
+        @ParameterizedRobolectricTestRunner.Parameters(name = "model={0}, data={1}, range={2}")
+        fun data() = mutableListOf<Array<Any?>>().apply {
+            add(arrayOf("SM-A520F", problematicJpegByteArray, Range.create(18, 31)))
+            add(arrayOf("SM-A520F", correctJpegByteArray1, null))
+            add(arrayOf("SM-A520F", correctJpegByteArray2, null))
+            add(arrayOf("SM-A520F", invalidVeryShortData, null))
+            add(arrayOf("SM-A520F", invalidNoSosData, null))
+            add(arrayOf("SM-A520F", invalidNoEoiData, null))
+            add(arrayOf("fake-model", problematicJpegByteArray, null))
+            add(arrayOf("fake-model", correctJpegByteArray1, null))
+            add(arrayOf("fake-model", correctJpegByteArray2, null))
+        }
+    }
+
+    @Test
+    fun canGetInvalidJpegDataRange() {
+        ReflectionHelpers.setStaticField(Build::class.java, "MODEL", model)
+        assertThat(
+            Objects.equals(
+                InvalidJpegDataParser().getInvalidDataRange(data),
+                range
+            )
+        ).isTrue()
+    }
+}
diff --git a/camera/camera-effects-still-portrait/api/1.3.0-beta02.txt b/camera/camera-effects-still-portrait/api/1.3.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/camera/camera-effects-still-portrait/api/1.3.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/camera/camera-effects-still-portrait/api/res-1.3.0-beta02.txt b/camera/camera-effects-still-portrait/api/res-1.3.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/camera/camera-effects-still-portrait/api/res-1.3.0-beta02.txt
diff --git a/camera/camera-effects-still-portrait/api/restricted_1.3.0-beta02.txt b/camera/camera-effects-still-portrait/api/restricted_1.3.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/camera/camera-effects-still-portrait/api/restricted_1.3.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/camera/camera-effects/api/1.3.0-beta02.txt b/camera/camera-effects/api/1.3.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/camera/camera-effects/api/1.3.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/camera/camera-effects/api/res-1.3.0-beta02.txt b/camera/camera-effects/api/res-1.3.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/camera/camera-effects/api/res-1.3.0-beta02.txt
diff --git a/camera/camera-effects/api/restricted_1.3.0-beta02.txt b/camera/camera-effects/api/restricted_1.3.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/camera/camera-effects/api/restricted_1.3.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/camera/camera-extensions/api/1.3.0-beta02.txt b/camera/camera-extensions/api/1.3.0-beta02.txt
new file mode 100644
index 0000000..5c6e740
--- /dev/null
+++ b/camera/camera-extensions/api/1.3.0-beta02.txt
@@ -0,0 +1,22 @@
+// Signature format: 4.0
+package androidx.camera.extensions {
+
+  @RequiresApi(21) public final class ExtensionMode {
+    field public static final int AUTO = 5; // 0x5
+    field public static final int BOKEH = 1; // 0x1
+    field public static final int FACE_RETOUCH = 4; // 0x4
+    field public static final int HDR = 2; // 0x2
+    field public static final int NIGHT = 3; // 0x3
+    field public static final int NONE = 0; // 0x0
+  }
+
+  @RequiresApi(21) public final class ExtensionsManager {
+    method public android.util.Range<java.lang.Long!>? getEstimatedCaptureLatencyRange(androidx.camera.core.CameraSelector, int);
+    method public androidx.camera.core.CameraSelector getExtensionEnabledCameraSelector(androidx.camera.core.CameraSelector, int);
+    method public static com.google.common.util.concurrent.ListenableFuture<androidx.camera.extensions.ExtensionsManager!> getInstanceAsync(android.content.Context, androidx.camera.core.CameraProvider);
+    method public boolean isExtensionAvailable(androidx.camera.core.CameraSelector, int);
+    method public boolean isImageAnalysisSupported(androidx.camera.core.CameraSelector, int);
+  }
+
+}
+
diff --git a/camera/camera-extensions/api/res-1.3.0-beta02.txt b/camera/camera-extensions/api/res-1.3.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/camera/camera-extensions/api/res-1.3.0-beta02.txt
diff --git a/camera/camera-extensions/api/restricted_1.3.0-beta02.txt b/camera/camera-extensions/api/restricted_1.3.0-beta02.txt
new file mode 100644
index 0000000..5c6e740
--- /dev/null
+++ b/camera/camera-extensions/api/restricted_1.3.0-beta02.txt
@@ -0,0 +1,22 @@
+// Signature format: 4.0
+package androidx.camera.extensions {
+
+  @RequiresApi(21) public final class ExtensionMode {
+    field public static final int AUTO = 5; // 0x5
+    field public static final int BOKEH = 1; // 0x1
+    field public static final int FACE_RETOUCH = 4; // 0x4
+    field public static final int HDR = 2; // 0x2
+    field public static final int NIGHT = 3; // 0x3
+    field public static final int NONE = 0; // 0x0
+  }
+
+  @RequiresApi(21) public final class ExtensionsManager {
+    method public android.util.Range<java.lang.Long!>? getEstimatedCaptureLatencyRange(androidx.camera.core.CameraSelector, int);
+    method public androidx.camera.core.CameraSelector getExtensionEnabledCameraSelector(androidx.camera.core.CameraSelector, int);
+    method public static com.google.common.util.concurrent.ListenableFuture<androidx.camera.extensions.ExtensionsManager!> getInstanceAsync(android.content.Context, androidx.camera.core.CameraProvider);
+    method public boolean isExtensionAvailable(androidx.camera.core.CameraSelector, int);
+    method public boolean isImageAnalysisSupported(androidx.camera.core.CameraSelector, int);
+  }
+
+}
+
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/AdvancedSessionProcessorTest.kt b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/AdvancedSessionProcessorTest.kt
index 0b356f1..bd31f81 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/AdvancedSessionProcessorTest.kt
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/AdvancedSessionProcessorTest.kt
@@ -230,7 +230,7 @@
     @Test
     fun getRealtimeLatencyEstimate_advancedSessionProcessorInvokesSessionProcessorImpl() =
         runBlocking {
-            ExtensionVersion.injectInstance(FakeExtensionVersion(Version.VERSION_1_4))
+            assumeTrue(ExtensionVersion.isMinimumCompatibleVersion(Version.VERSION_1_4))
             ClientVersion.setCurrentVersion(ClientVersion("1.4.0"))
 
             val fakeSessionProcessImpl = object : SessionProcessorImpl by FakeSessionProcessImpl() {
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/FakeExtensionVersion.kt b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/FakeExtensionVersion.kt
deleted file mode 100644
index e0bce46..0000000
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/FakeExtensionVersion.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.camera.extensions.internal
-
-import androidx.test.filters.SdkSuppress
-
-/**
- * A fake extension version with a settable version string used for testing
- */
-@SdkSuppress(minSdkVersion = 23) // BasicVendorExtender requires API level 23
-class FakeExtensionVersion(
-    private val version: Version = Version.VERSION_1_3,
-    private val isAdvancedExtenderSupported: Boolean = true
-) : ExtensionVersion() {
-    override fun isAdvancedExtenderSupportedInternal(): Boolean = isAdvancedExtenderSupported
-
-    override fun getVersionObject(): Version = version
-}
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessorTest.kt b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessorTest.kt
index de57389..c494bd2 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessorTest.kt
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessorTest.kt
@@ -70,7 +70,6 @@
 import androidx.camera.extensions.impl.RequestUpdateProcessorImpl
 import androidx.camera.extensions.internal.ClientVersion
 import androidx.camera.extensions.internal.ExtensionVersion
-import androidx.camera.extensions.internal.FakeExtensionVersion
 import androidx.camera.extensions.internal.Version
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.CameraUtil
@@ -345,7 +344,7 @@
     @Test
     fun getRealtimeCaptureLatencyEstimate_invokesCaptureExtenderImpl(): Unit = runBlocking {
         assumeTrue(hasCaptureProcessor)
-        ExtensionVersion.injectInstance(FakeExtensionVersion(Version.VERSION_1_4))
+        assumeTrue(ExtensionVersion.isMinimumCompatibleVersion(Version.VERSION_1_4))
         ClientVersion.setCurrentVersion(ClientVersion("1.4.0"))
         fakeCaptureExtenderImpl = object : FakeImageCaptureExtenderImpl(hasCaptureProcessor) {
             override fun getRealtimeCaptureLatency(): Pair<Long, Long> = Pair(1000L, 10L)
diff --git a/camera/camera-lifecycle/api/1.3.0-beta02.txt b/camera/camera-lifecycle/api/1.3.0-beta02.txt
new file mode 100644
index 0000000..d0621cb
--- /dev/null
+++ b/camera/camera-lifecycle/api/1.3.0-beta02.txt
@@ -0,0 +1,23 @@
+// Signature format: 4.0
+package androidx.camera.lifecycle {
+
+  @SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalCameraProviderConfiguration {
+  }
+
+  @RequiresApi(21) public final class ProcessCameraProvider implements androidx.camera.core.CameraProvider {
+    method @MainThread public androidx.camera.core.Camera bindToLifecycle(androidx.lifecycle.LifecycleOwner, androidx.camera.core.CameraSelector, androidx.camera.core.UseCase!...);
+    method @MainThread public androidx.camera.core.Camera bindToLifecycle(androidx.lifecycle.LifecycleOwner, androidx.camera.core.CameraSelector, androidx.camera.core.UseCaseGroup);
+    method @MainThread public androidx.camera.core.ConcurrentCamera bindToLifecycle(java.util.List<androidx.camera.core.ConcurrentCamera.SingleCameraConfig!>);
+    method @SuppressCompatibility @androidx.camera.lifecycle.ExperimentalCameraProviderConfiguration public static void configureInstance(androidx.camera.core.CameraXConfig);
+    method public java.util.List<androidx.camera.core.CameraInfo!> getAvailableCameraInfos();
+    method public java.util.List<java.util.List<androidx.camera.core.CameraInfo!>!> getAvailableConcurrentCameraInfos();
+    method public static com.google.common.util.concurrent.ListenableFuture<androidx.camera.lifecycle.ProcessCameraProvider!> getInstance(android.content.Context);
+    method public boolean hasCamera(androidx.camera.core.CameraSelector) throws androidx.camera.core.CameraInfoUnavailableException;
+    method public boolean isBound(androidx.camera.core.UseCase);
+    method @MainThread public boolean isConcurrentCameraModeOn();
+    method @MainThread public void unbind(androidx.camera.core.UseCase!...);
+    method @MainThread public void unbindAll();
+  }
+
+}
+
diff --git a/camera/camera-lifecycle/api/res-1.3.0-beta02.txt b/camera/camera-lifecycle/api/res-1.3.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/camera/camera-lifecycle/api/res-1.3.0-beta02.txt
diff --git a/camera/camera-lifecycle/api/restricted_1.3.0-beta02.txt b/camera/camera-lifecycle/api/restricted_1.3.0-beta02.txt
new file mode 100644
index 0000000..d0621cb
--- /dev/null
+++ b/camera/camera-lifecycle/api/restricted_1.3.0-beta02.txt
@@ -0,0 +1,23 @@
+// Signature format: 4.0
+package androidx.camera.lifecycle {
+
+  @SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalCameraProviderConfiguration {
+  }
+
+  @RequiresApi(21) public final class ProcessCameraProvider implements androidx.camera.core.CameraProvider {
+    method @MainThread public androidx.camera.core.Camera bindToLifecycle(androidx.lifecycle.LifecycleOwner, androidx.camera.core.CameraSelector, androidx.camera.core.UseCase!...);
+    method @MainThread public androidx.camera.core.Camera bindToLifecycle(androidx.lifecycle.LifecycleOwner, androidx.camera.core.CameraSelector, androidx.camera.core.UseCaseGroup);
+    method @MainThread public androidx.camera.core.ConcurrentCamera bindToLifecycle(java.util.List<androidx.camera.core.ConcurrentCamera.SingleCameraConfig!>);
+    method @SuppressCompatibility @androidx.camera.lifecycle.ExperimentalCameraProviderConfiguration public static void configureInstance(androidx.camera.core.CameraXConfig);
+    method public java.util.List<androidx.camera.core.CameraInfo!> getAvailableCameraInfos();
+    method public java.util.List<java.util.List<androidx.camera.core.CameraInfo!>!> getAvailableConcurrentCameraInfos();
+    method public static com.google.common.util.concurrent.ListenableFuture<androidx.camera.lifecycle.ProcessCameraProvider!> getInstance(android.content.Context);
+    method public boolean hasCamera(androidx.camera.core.CameraSelector) throws androidx.camera.core.CameraInfoUnavailableException;
+    method public boolean isBound(androidx.camera.core.UseCase);
+    method @MainThread public boolean isConcurrentCameraModeOn();
+    method @MainThread public void unbind(androidx.camera.core.UseCase!...);
+    method @MainThread public void unbindAll();
+  }
+
+}
+
diff --git a/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/ProcessCameraProviderTest.kt b/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/ProcessCameraProviderTest.kt
index f585001..7dbc5c3 100644
--- a/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/ProcessCameraProviderTest.kt
+++ b/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/ProcessCameraProviderTest.kt
@@ -30,6 +30,7 @@
 import androidx.camera.core.ConcurrentCamera.SingleCameraConfig
 import androidx.camera.core.Preview
 import androidx.camera.core.UseCaseGroup
+import androidx.camera.core.concurrent.CameraCoordinator.CAMERA_OPERATING_MODE_UNSPECIFIED
 import androidx.camera.core.impl.CameraFactory
 import androidx.camera.core.impl.utils.executor.CameraXExecutors.mainThreadExecutor
 import androidx.camera.testing.fakes.FakeAppConfig
@@ -63,6 +64,7 @@
     private val context = ApplicationProvider.getApplicationContext() as Context
     private val lifecycleOwner0 = FakeLifecycleOwner()
     private val lifecycleOwner1 = FakeLifecycleOwner()
+    private val cameraCoordinator = FakeCameraCoordinator()
 
     private lateinit var provider: ProcessCameraProvider
 
@@ -665,6 +667,10 @@
 
         // Should not throw exception
         ProcessCameraProvider.configureInstance(FakeAppConfig.create())
+        assertThat(cameraCoordinator.cameraOperatingMode).isEqualTo(
+            CAMERA_OPERATING_MODE_UNSPECIFIED)
+        assertThat(cameraCoordinator.concurrentCameraSelectors).isEmpty()
+        assertThat(cameraCoordinator.activeConcurrentCameraInfos).isEmpty()
     }
 
     @Test
@@ -841,7 +847,6 @@
     }
 
     private fun createConcurrentCameraAppConfig(): CameraXConfig {
-        val cameraCoordinator = FakeCameraCoordinator()
         val combination0 = mapOf(
             "0" to CameraSelector.Builder().requireLensFacing(LENS_FACING_BACK).build(),
             "1" to CameraSelector.Builder().requireLensFacing(LENS_FACING_FRONT).build())
diff --git a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.java b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.java
index d675fb858..f6e56cd 100644
--- a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.java
+++ b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.java
@@ -291,6 +291,10 @@
             mLifecycleCameraRepository.clear();
         });
 
+        if (mCameraX != null) {
+            mCameraX.getCameraFactory().getCameraCoordinator().shutdown();
+        }
+
         ListenableFuture<Void> shutdownFuture = mCameraX != null ? mCameraX.shutdown() :
                 Futures.immediateFuture(null);
 
diff --git a/camera/camera-mlkit-vision/api/1.3.0-beta02.txt b/camera/camera-mlkit-vision/api/1.3.0-beta02.txt
new file mode 100644
index 0000000..0599c25
--- /dev/null
+++ b/camera/camera-mlkit-vision/api/1.3.0-beta02.txt
@@ -0,0 +1,20 @@
+// Signature format: 4.0
+package androidx.camera.mlkit.vision {
+
+  @RequiresApi(21) public class MlKitAnalyzer implements androidx.camera.core.ImageAnalysis.Analyzer {
+    ctor public MlKitAnalyzer(java.util.List<com.google.mlkit.vision.interfaces.Detector<?>!>, int, java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.camera.mlkit.vision.MlKitAnalyzer.Result!>);
+    method public final void analyze(androidx.camera.core.ImageProxy);
+    method public final android.util.Size getDefaultTargetResolution();
+    method public final int getTargetCoordinateSystem();
+    method public final void updateTransform(android.graphics.Matrix?);
+  }
+
+  public static final class MlKitAnalyzer.Result {
+    ctor public MlKitAnalyzer.Result(java.util.Map<com.google.mlkit.vision.interfaces.Detector<?>!,java.lang.Object!>, long, java.util.Map<com.google.mlkit.vision.interfaces.Detector<?>!,java.lang.Throwable!>);
+    method public Throwable? getThrowable(com.google.mlkit.vision.interfaces.Detector<?>);
+    method public long getTimestamp();
+    method public <T> T? getValue(com.google.mlkit.vision.interfaces.Detector<T!>);
+  }
+
+}
+
diff --git a/camera/camera-mlkit-vision/api/res-1.3.0-beta02.txt b/camera/camera-mlkit-vision/api/res-1.3.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/camera/camera-mlkit-vision/api/res-1.3.0-beta02.txt
diff --git a/camera/camera-mlkit-vision/api/restricted_1.3.0-beta02.txt b/camera/camera-mlkit-vision/api/restricted_1.3.0-beta02.txt
new file mode 100644
index 0000000..0599c25
--- /dev/null
+++ b/camera/camera-mlkit-vision/api/restricted_1.3.0-beta02.txt
@@ -0,0 +1,20 @@
+// Signature format: 4.0
+package androidx.camera.mlkit.vision {
+
+  @RequiresApi(21) public class MlKitAnalyzer implements androidx.camera.core.ImageAnalysis.Analyzer {
+    ctor public MlKitAnalyzer(java.util.List<com.google.mlkit.vision.interfaces.Detector<?>!>, int, java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.camera.mlkit.vision.MlKitAnalyzer.Result!>);
+    method public final void analyze(androidx.camera.core.ImageProxy);
+    method public final android.util.Size getDefaultTargetResolution();
+    method public final int getTargetCoordinateSystem();
+    method public final void updateTransform(android.graphics.Matrix?);
+  }
+
+  public static final class MlKitAnalyzer.Result {
+    ctor public MlKitAnalyzer.Result(java.util.Map<com.google.mlkit.vision.interfaces.Detector<?>!,java.lang.Object!>, long, java.util.Map<com.google.mlkit.vision.interfaces.Detector<?>!,java.lang.Throwable!>);
+    method public Throwable? getThrowable(com.google.mlkit.vision.interfaces.Detector<?>);
+    method public long getTimestamp();
+    method public <T> T? getValue(com.google.mlkit.vision.interfaces.Detector<T!>);
+  }
+
+}
+
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraCoordinator.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraCoordinator.java
index 3281ec1..1dc5b00 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraCoordinator.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraCoordinator.java
@@ -125,4 +125,13 @@
     public void removeListener(@NonNull ConcurrentCameraModeListener listener) {
         mConcurrentCameraModeListeners.remove(listener);
     }
+
+    @Override
+    public void shutdown() {
+        mConcurrentCameraIdMap.clear();
+        mConcurrentCameraIds.clear();
+        mConcurrentCameraSelectors.clear();
+        mActiveConcurrentCameraInfos.clear();
+        mConcurrentCameraModeListeners.clear();
+    }
 }
diff --git a/camera/camera-video/api/1.3.0-beta02.txt b/camera/camera-video/api/1.3.0-beta02.txt
new file mode 100644
index 0000000..b9cc77a
--- /dev/null
+++ b/camera/camera-video/api/1.3.0-beta02.txt
@@ -0,0 +1,207 @@
+// Signature format: 4.0
+package androidx.camera.video {
+
+  @RequiresApi(21) @com.google.auto.value.AutoValue public abstract class AudioStats {
+    method public abstract int getAudioState();
+    method public abstract Throwable? getErrorCause();
+    method public boolean hasAudio();
+    method public boolean hasError();
+    field public static final int AUDIO_STATE_ACTIVE = 0; // 0x0
+    field public static final int AUDIO_STATE_DISABLED = 1; // 0x1
+    field public static final int AUDIO_STATE_ENCODER_ERROR = 3; // 0x3
+    field public static final int AUDIO_STATE_MUTED = 5; // 0x5
+    field public static final int AUDIO_STATE_SOURCE_ERROR = 4; // 0x4
+    field public static final int AUDIO_STATE_SOURCE_SILENCED = 2; // 0x2
+  }
+
+  @SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalPersistentRecording {
+  }
+
+  @RequiresApi(21) public class FallbackStrategy {
+    method public static androidx.camera.video.FallbackStrategy higherQualityOrLowerThan(androidx.camera.video.Quality);
+    method public static androidx.camera.video.FallbackStrategy higherQualityThan(androidx.camera.video.Quality);
+    method public static androidx.camera.video.FallbackStrategy lowerQualityOrHigherThan(androidx.camera.video.Quality);
+    method public static androidx.camera.video.FallbackStrategy lowerQualityThan(androidx.camera.video.Quality);
+  }
+
+  @RequiresApi(21) public final class FileDescriptorOutputOptions extends androidx.camera.video.OutputOptions {
+    method public android.os.ParcelFileDescriptor getParcelFileDescriptor();
+  }
+
+  @RequiresApi(21) public static final class FileDescriptorOutputOptions.Builder {
+    ctor public FileDescriptorOutputOptions.Builder(android.os.ParcelFileDescriptor);
+    method public androidx.camera.video.FileDescriptorOutputOptions build();
+    method public androidx.camera.video.FileDescriptorOutputOptions.Builder setDurationLimitMillis(@IntRange(from=0) long);
+    method public androidx.camera.video.FileDescriptorOutputOptions.Builder setFileSizeLimit(@IntRange(from=0) long);
+    method public androidx.camera.video.FileDescriptorOutputOptions.Builder setLocation(android.location.Location?);
+  }
+
+  @RequiresApi(21) public final class FileOutputOptions extends androidx.camera.video.OutputOptions {
+    method public java.io.File getFile();
+  }
+
+  @RequiresApi(21) public static final class FileOutputOptions.Builder {
+    ctor public FileOutputOptions.Builder(java.io.File);
+    method public androidx.camera.video.FileOutputOptions build();
+    method public androidx.camera.video.FileOutputOptions.Builder setDurationLimitMillis(@IntRange(from=0) long);
+    method public androidx.camera.video.FileOutputOptions.Builder setFileSizeLimit(@IntRange(from=0) long);
+    method public androidx.camera.video.FileOutputOptions.Builder setLocation(android.location.Location?);
+  }
+
+  @RequiresApi(21) public final class MediaStoreOutputOptions extends androidx.camera.video.OutputOptions {
+    method public android.net.Uri getCollectionUri();
+    method public android.content.ContentResolver getContentResolver();
+    method public android.content.ContentValues getContentValues();
+    field public static final android.content.ContentValues EMPTY_CONTENT_VALUES;
+  }
+
+  public static final class MediaStoreOutputOptions.Builder {
+    ctor public MediaStoreOutputOptions.Builder(android.content.ContentResolver, android.net.Uri);
+    method public androidx.camera.video.MediaStoreOutputOptions build();
+    method public androidx.camera.video.MediaStoreOutputOptions.Builder setContentValues(android.content.ContentValues);
+    method public androidx.camera.video.MediaStoreOutputOptions.Builder setDurationLimitMillis(@IntRange(from=0) long);
+    method public androidx.camera.video.MediaStoreOutputOptions.Builder setFileSizeLimit(@IntRange(from=0) long);
+    method public androidx.camera.video.MediaStoreOutputOptions.Builder setLocation(android.location.Location?);
+  }
+
+  @RequiresApi(21) public abstract class OutputOptions {
+    method @IntRange(from=0) public long getDurationLimitMillis();
+    method @IntRange(from=0) public long getFileSizeLimit();
+    method public android.location.Location? getLocation();
+    field public static final int DURATION_UNLIMITED = 0; // 0x0
+    field public static final int FILE_SIZE_UNLIMITED = 0; // 0x0
+  }
+
+  @RequiresApi(21) @com.google.auto.value.AutoValue public abstract class OutputResults {
+    ctor public OutputResults();
+    method public abstract android.net.Uri getOutputUri();
+  }
+
+  @RequiresApi(21) public final class PendingRecording {
+    method @SuppressCompatibility @androidx.camera.video.ExperimentalPersistentRecording public androidx.camera.video.PendingRecording asPersistentRecording();
+    method @CheckResult public androidx.camera.video.Recording start(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.camera.video.VideoRecordEvent!>);
+    method @RequiresPermission(android.Manifest.permission.RECORD_AUDIO) public androidx.camera.video.PendingRecording withAudioEnabled();
+  }
+
+  @RequiresApi(21) public class Quality {
+    field public static final androidx.camera.video.Quality FHD;
+    field public static final androidx.camera.video.Quality HD;
+    field public static final androidx.camera.video.Quality HIGHEST;
+    field public static final androidx.camera.video.Quality LOWEST;
+    field public static final androidx.camera.video.Quality SD;
+    field public static final androidx.camera.video.Quality UHD;
+  }
+
+  @RequiresApi(21) public final class QualitySelector {
+    method public static androidx.camera.video.QualitySelector from(androidx.camera.video.Quality);
+    method public static androidx.camera.video.QualitySelector from(androidx.camera.video.Quality, androidx.camera.video.FallbackStrategy);
+    method public static androidx.camera.video.QualitySelector fromOrderedList(java.util.List<androidx.camera.video.Quality!>);
+    method public static androidx.camera.video.QualitySelector fromOrderedList(java.util.List<androidx.camera.video.Quality!>, androidx.camera.video.FallbackStrategy);
+    method public static android.util.Size? getResolution(androidx.camera.core.CameraInfo, androidx.camera.video.Quality);
+    method @Deprecated public static java.util.List<androidx.camera.video.Quality!> getSupportedQualities(androidx.camera.core.CameraInfo);
+    method @Deprecated public static boolean isQualitySupported(androidx.camera.core.CameraInfo, androidx.camera.video.Quality);
+  }
+
+  @RequiresApi(21) public final class Recorder implements androidx.camera.video.VideoOutput {
+    method public int getAspectRatio();
+    method public java.util.concurrent.Executor? getExecutor();
+    method public androidx.camera.video.QualitySelector getQualitySelector();
+    method public int getTargetVideoEncodingBitRate();
+    method public static androidx.camera.video.VideoCapabilities getVideoCapabilities(androidx.camera.core.CameraInfo);
+    method public void onSurfaceRequested(androidx.camera.core.SurfaceRequest);
+    method @RequiresApi(26) public androidx.camera.video.PendingRecording prepareRecording(android.content.Context, androidx.camera.video.FileDescriptorOutputOptions);
+    method public androidx.camera.video.PendingRecording prepareRecording(android.content.Context, androidx.camera.video.FileOutputOptions);
+    method public androidx.camera.video.PendingRecording prepareRecording(android.content.Context, androidx.camera.video.MediaStoreOutputOptions);
+    field public static final androidx.camera.video.QualitySelector DEFAULT_QUALITY_SELECTOR;
+  }
+
+  @RequiresApi(21) public static final class Recorder.Builder {
+    ctor public Recorder.Builder();
+    method public androidx.camera.video.Recorder build();
+    method public androidx.camera.video.Recorder.Builder setAspectRatio(int);
+    method public androidx.camera.video.Recorder.Builder setExecutor(java.util.concurrent.Executor);
+    method public androidx.camera.video.Recorder.Builder setQualitySelector(androidx.camera.video.QualitySelector);
+    method public androidx.camera.video.Recorder.Builder setTargetVideoEncodingBitRate(@IntRange(from=1) int);
+  }
+
+  @RequiresApi(21) public final class Recording implements java.lang.AutoCloseable {
+    method public void close();
+    method @SuppressCompatibility @androidx.camera.video.ExperimentalPersistentRecording public boolean isPersistent();
+    method public void mute(boolean);
+    method public void pause();
+    method public void resume();
+    method public void stop();
+  }
+
+  @RequiresApi(21) @com.google.auto.value.AutoValue public abstract class RecordingStats {
+    method public abstract androidx.camera.video.AudioStats getAudioStats();
+    method public abstract long getNumBytesRecorded();
+    method public abstract long getRecordedDurationNanos();
+  }
+
+  @RequiresApi(21) public interface VideoCapabilities {
+    method public java.util.Set<androidx.camera.core.DynamicRange!> getSupportedDynamicRanges();
+    method public java.util.List<androidx.camera.video.Quality!> getSupportedQualities(androidx.camera.core.DynamicRange);
+    method public boolean isQualitySupported(androidx.camera.video.Quality, androidx.camera.core.DynamicRange);
+  }
+
+  @RequiresApi(21) public final class VideoCapture<T extends androidx.camera.video.VideoOutput> extends androidx.camera.core.UseCase {
+    method public androidx.camera.core.DynamicRange getDynamicRange();
+    method public int getMirrorMode();
+    method public T getOutput();
+    method public android.util.Range<java.lang.Integer!> getTargetFrameRate();
+    method public int getTargetRotation();
+    method public void setTargetRotation(int);
+    method public static <T extends androidx.camera.video.VideoOutput> androidx.camera.video.VideoCapture<T!> withOutput(T);
+  }
+
+  @RequiresApi(21) public static final class VideoCapture.Builder<T extends androidx.camera.video.VideoOutput> implements androidx.camera.core.ExtendableBuilder<androidx.camera.video.VideoCapture> {
+    ctor public VideoCapture.Builder(T);
+    method public androidx.camera.video.VideoCapture<T!> build();
+    method public androidx.camera.video.VideoCapture.Builder<T!> setDynamicRange(androidx.camera.core.DynamicRange);
+    method public androidx.camera.video.VideoCapture.Builder<T!> setMirrorMode(int);
+    method public androidx.camera.video.VideoCapture.Builder<T!> setTargetFrameRate(android.util.Range<java.lang.Integer!>);
+    method public androidx.camera.video.VideoCapture.Builder<T!> setTargetRotation(int);
+  }
+
+  @RequiresApi(21) public interface VideoOutput {
+    method public void onSurfaceRequested(androidx.camera.core.SurfaceRequest);
+  }
+
+  @RequiresApi(21) public abstract class VideoRecordEvent {
+    method public androidx.camera.video.OutputOptions getOutputOptions();
+    method public androidx.camera.video.RecordingStats getRecordingStats();
+  }
+
+  @RequiresApi(21) public static final class VideoRecordEvent.Finalize extends androidx.camera.video.VideoRecordEvent {
+    method public Throwable? getCause();
+    method public int getError();
+    method public androidx.camera.video.OutputResults getOutputResults();
+    method public boolean hasError();
+    field public static final int ERROR_DURATION_LIMIT_REACHED = 9; // 0x9
+    field public static final int ERROR_ENCODING_FAILED = 6; // 0x6
+    field public static final int ERROR_FILE_SIZE_LIMIT_REACHED = 2; // 0x2
+    field public static final int ERROR_INSUFFICIENT_STORAGE = 3; // 0x3
+    field public static final int ERROR_INVALID_OUTPUT_OPTIONS = 5; // 0x5
+    field public static final int ERROR_NONE = 0; // 0x0
+    field public static final int ERROR_NO_VALID_DATA = 8; // 0x8
+    field public static final int ERROR_RECORDER_ERROR = 7; // 0x7
+    field public static final int ERROR_RECORDING_GARBAGE_COLLECTED = 10; // 0xa
+    field public static final int ERROR_SOURCE_INACTIVE = 4; // 0x4
+    field public static final int ERROR_UNKNOWN = 1; // 0x1
+  }
+
+  @RequiresApi(21) public static final class VideoRecordEvent.Pause extends androidx.camera.video.VideoRecordEvent {
+  }
+
+  @RequiresApi(21) public static final class VideoRecordEvent.Resume extends androidx.camera.video.VideoRecordEvent {
+  }
+
+  @RequiresApi(21) public static final class VideoRecordEvent.Start extends androidx.camera.video.VideoRecordEvent {
+  }
+
+  @RequiresApi(21) public static final class VideoRecordEvent.Status extends androidx.camera.video.VideoRecordEvent {
+  }
+
+}
+
diff --git a/camera/camera-video/api/current.txt b/camera/camera-video/api/current.txt
index fb0f4c7..b9cc77a 100644
--- a/camera/camera-video/api/current.txt
+++ b/camera/camera-video/api/current.txt
@@ -14,6 +14,9 @@
     field public static final int AUDIO_STATE_SOURCE_SILENCED = 2; // 0x2
   }
 
+  @SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalPersistentRecording {
+  }
+
   @RequiresApi(21) public class FallbackStrategy {
     method public static androidx.camera.video.FallbackStrategy higherQualityOrLowerThan(androidx.camera.video.Quality);
     method public static androidx.camera.video.FallbackStrategy higherQualityThan(androidx.camera.video.Quality);
@@ -75,6 +78,7 @@
   }
 
   @RequiresApi(21) public final class PendingRecording {
+    method @SuppressCompatibility @androidx.camera.video.ExperimentalPersistentRecording public androidx.camera.video.PendingRecording asPersistentRecording();
     method @CheckResult public androidx.camera.video.Recording start(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.camera.video.VideoRecordEvent!>);
     method @RequiresPermission(android.Manifest.permission.RECORD_AUDIO) public androidx.camera.video.PendingRecording withAudioEnabled();
   }
@@ -122,6 +126,7 @@
 
   @RequiresApi(21) public final class Recording implements java.lang.AutoCloseable {
     method public void close();
+    method @SuppressCompatibility @androidx.camera.video.ExperimentalPersistentRecording public boolean isPersistent();
     method public void mute(boolean);
     method public void pause();
     method public void resume();
diff --git a/camera/camera-video/api/res-1.3.0-beta02.txt b/camera/camera-video/api/res-1.3.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/camera/camera-video/api/res-1.3.0-beta02.txt
diff --git a/camera/camera-video/api/restricted_1.3.0-beta02.txt b/camera/camera-video/api/restricted_1.3.0-beta02.txt
new file mode 100644
index 0000000..b9cc77a
--- /dev/null
+++ b/camera/camera-video/api/restricted_1.3.0-beta02.txt
@@ -0,0 +1,207 @@
+// Signature format: 4.0
+package androidx.camera.video {
+
+  @RequiresApi(21) @com.google.auto.value.AutoValue public abstract class AudioStats {
+    method public abstract int getAudioState();
+    method public abstract Throwable? getErrorCause();
+    method public boolean hasAudio();
+    method public boolean hasError();
+    field public static final int AUDIO_STATE_ACTIVE = 0; // 0x0
+    field public static final int AUDIO_STATE_DISABLED = 1; // 0x1
+    field public static final int AUDIO_STATE_ENCODER_ERROR = 3; // 0x3
+    field public static final int AUDIO_STATE_MUTED = 5; // 0x5
+    field public static final int AUDIO_STATE_SOURCE_ERROR = 4; // 0x4
+    field public static final int AUDIO_STATE_SOURCE_SILENCED = 2; // 0x2
+  }
+
+  @SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalPersistentRecording {
+  }
+
+  @RequiresApi(21) public class FallbackStrategy {
+    method public static androidx.camera.video.FallbackStrategy higherQualityOrLowerThan(androidx.camera.video.Quality);
+    method public static androidx.camera.video.FallbackStrategy higherQualityThan(androidx.camera.video.Quality);
+    method public static androidx.camera.video.FallbackStrategy lowerQualityOrHigherThan(androidx.camera.video.Quality);
+    method public static androidx.camera.video.FallbackStrategy lowerQualityThan(androidx.camera.video.Quality);
+  }
+
+  @RequiresApi(21) public final class FileDescriptorOutputOptions extends androidx.camera.video.OutputOptions {
+    method public android.os.ParcelFileDescriptor getParcelFileDescriptor();
+  }
+
+  @RequiresApi(21) public static final class FileDescriptorOutputOptions.Builder {
+    ctor public FileDescriptorOutputOptions.Builder(android.os.ParcelFileDescriptor);
+    method public androidx.camera.video.FileDescriptorOutputOptions build();
+    method public androidx.camera.video.FileDescriptorOutputOptions.Builder setDurationLimitMillis(@IntRange(from=0) long);
+    method public androidx.camera.video.FileDescriptorOutputOptions.Builder setFileSizeLimit(@IntRange(from=0) long);
+    method public androidx.camera.video.FileDescriptorOutputOptions.Builder setLocation(android.location.Location?);
+  }
+
+  @RequiresApi(21) public final class FileOutputOptions extends androidx.camera.video.OutputOptions {
+    method public java.io.File getFile();
+  }
+
+  @RequiresApi(21) public static final class FileOutputOptions.Builder {
+    ctor public FileOutputOptions.Builder(java.io.File);
+    method public androidx.camera.video.FileOutputOptions build();
+    method public androidx.camera.video.FileOutputOptions.Builder setDurationLimitMillis(@IntRange(from=0) long);
+    method public androidx.camera.video.FileOutputOptions.Builder setFileSizeLimit(@IntRange(from=0) long);
+    method public androidx.camera.video.FileOutputOptions.Builder setLocation(android.location.Location?);
+  }
+
+  @RequiresApi(21) public final class MediaStoreOutputOptions extends androidx.camera.video.OutputOptions {
+    method public android.net.Uri getCollectionUri();
+    method public android.content.ContentResolver getContentResolver();
+    method public android.content.ContentValues getContentValues();
+    field public static final android.content.ContentValues EMPTY_CONTENT_VALUES;
+  }
+
+  public static final class MediaStoreOutputOptions.Builder {
+    ctor public MediaStoreOutputOptions.Builder(android.content.ContentResolver, android.net.Uri);
+    method public androidx.camera.video.MediaStoreOutputOptions build();
+    method public androidx.camera.video.MediaStoreOutputOptions.Builder setContentValues(android.content.ContentValues);
+    method public androidx.camera.video.MediaStoreOutputOptions.Builder setDurationLimitMillis(@IntRange(from=0) long);
+    method public androidx.camera.video.MediaStoreOutputOptions.Builder setFileSizeLimit(@IntRange(from=0) long);
+    method public androidx.camera.video.MediaStoreOutputOptions.Builder setLocation(android.location.Location?);
+  }
+
+  @RequiresApi(21) public abstract class OutputOptions {
+    method @IntRange(from=0) public long getDurationLimitMillis();
+    method @IntRange(from=0) public long getFileSizeLimit();
+    method public android.location.Location? getLocation();
+    field public static final int DURATION_UNLIMITED = 0; // 0x0
+    field public static final int FILE_SIZE_UNLIMITED = 0; // 0x0
+  }
+
+  @RequiresApi(21) @com.google.auto.value.AutoValue public abstract class OutputResults {
+    ctor public OutputResults();
+    method public abstract android.net.Uri getOutputUri();
+  }
+
+  @RequiresApi(21) public final class PendingRecording {
+    method @SuppressCompatibility @androidx.camera.video.ExperimentalPersistentRecording public androidx.camera.video.PendingRecording asPersistentRecording();
+    method @CheckResult public androidx.camera.video.Recording start(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.camera.video.VideoRecordEvent!>);
+    method @RequiresPermission(android.Manifest.permission.RECORD_AUDIO) public androidx.camera.video.PendingRecording withAudioEnabled();
+  }
+
+  @RequiresApi(21) public class Quality {
+    field public static final androidx.camera.video.Quality FHD;
+    field public static final androidx.camera.video.Quality HD;
+    field public static final androidx.camera.video.Quality HIGHEST;
+    field public static final androidx.camera.video.Quality LOWEST;
+    field public static final androidx.camera.video.Quality SD;
+    field public static final androidx.camera.video.Quality UHD;
+  }
+
+  @RequiresApi(21) public final class QualitySelector {
+    method public static androidx.camera.video.QualitySelector from(androidx.camera.video.Quality);
+    method public static androidx.camera.video.QualitySelector from(androidx.camera.video.Quality, androidx.camera.video.FallbackStrategy);
+    method public static androidx.camera.video.QualitySelector fromOrderedList(java.util.List<androidx.camera.video.Quality!>);
+    method public static androidx.camera.video.QualitySelector fromOrderedList(java.util.List<androidx.camera.video.Quality!>, androidx.camera.video.FallbackStrategy);
+    method public static android.util.Size? getResolution(androidx.camera.core.CameraInfo, androidx.camera.video.Quality);
+    method @Deprecated public static java.util.List<androidx.camera.video.Quality!> getSupportedQualities(androidx.camera.core.CameraInfo);
+    method @Deprecated public static boolean isQualitySupported(androidx.camera.core.CameraInfo, androidx.camera.video.Quality);
+  }
+
+  @RequiresApi(21) public final class Recorder implements androidx.camera.video.VideoOutput {
+    method public int getAspectRatio();
+    method public java.util.concurrent.Executor? getExecutor();
+    method public androidx.camera.video.QualitySelector getQualitySelector();
+    method public int getTargetVideoEncodingBitRate();
+    method public static androidx.camera.video.VideoCapabilities getVideoCapabilities(androidx.camera.core.CameraInfo);
+    method public void onSurfaceRequested(androidx.camera.core.SurfaceRequest);
+    method @RequiresApi(26) public androidx.camera.video.PendingRecording prepareRecording(android.content.Context, androidx.camera.video.FileDescriptorOutputOptions);
+    method public androidx.camera.video.PendingRecording prepareRecording(android.content.Context, androidx.camera.video.FileOutputOptions);
+    method public androidx.camera.video.PendingRecording prepareRecording(android.content.Context, androidx.camera.video.MediaStoreOutputOptions);
+    field public static final androidx.camera.video.QualitySelector DEFAULT_QUALITY_SELECTOR;
+  }
+
+  @RequiresApi(21) public static final class Recorder.Builder {
+    ctor public Recorder.Builder();
+    method public androidx.camera.video.Recorder build();
+    method public androidx.camera.video.Recorder.Builder setAspectRatio(int);
+    method public androidx.camera.video.Recorder.Builder setExecutor(java.util.concurrent.Executor);
+    method public androidx.camera.video.Recorder.Builder setQualitySelector(androidx.camera.video.QualitySelector);
+    method public androidx.camera.video.Recorder.Builder setTargetVideoEncodingBitRate(@IntRange(from=1) int);
+  }
+
+  @RequiresApi(21) public final class Recording implements java.lang.AutoCloseable {
+    method public void close();
+    method @SuppressCompatibility @androidx.camera.video.ExperimentalPersistentRecording public boolean isPersistent();
+    method public void mute(boolean);
+    method public void pause();
+    method public void resume();
+    method public void stop();
+  }
+
+  @RequiresApi(21) @com.google.auto.value.AutoValue public abstract class RecordingStats {
+    method public abstract androidx.camera.video.AudioStats getAudioStats();
+    method public abstract long getNumBytesRecorded();
+    method public abstract long getRecordedDurationNanos();
+  }
+
+  @RequiresApi(21) public interface VideoCapabilities {
+    method public java.util.Set<androidx.camera.core.DynamicRange!> getSupportedDynamicRanges();
+    method public java.util.List<androidx.camera.video.Quality!> getSupportedQualities(androidx.camera.core.DynamicRange);
+    method public boolean isQualitySupported(androidx.camera.video.Quality, androidx.camera.core.DynamicRange);
+  }
+
+  @RequiresApi(21) public final class VideoCapture<T extends androidx.camera.video.VideoOutput> extends androidx.camera.core.UseCase {
+    method public androidx.camera.core.DynamicRange getDynamicRange();
+    method public int getMirrorMode();
+    method public T getOutput();
+    method public android.util.Range<java.lang.Integer!> getTargetFrameRate();
+    method public int getTargetRotation();
+    method public void setTargetRotation(int);
+    method public static <T extends androidx.camera.video.VideoOutput> androidx.camera.video.VideoCapture<T!> withOutput(T);
+  }
+
+  @RequiresApi(21) public static final class VideoCapture.Builder<T extends androidx.camera.video.VideoOutput> implements androidx.camera.core.ExtendableBuilder<androidx.camera.video.VideoCapture> {
+    ctor public VideoCapture.Builder(T);
+    method public androidx.camera.video.VideoCapture<T!> build();
+    method public androidx.camera.video.VideoCapture.Builder<T!> setDynamicRange(androidx.camera.core.DynamicRange);
+    method public androidx.camera.video.VideoCapture.Builder<T!> setMirrorMode(int);
+    method public androidx.camera.video.VideoCapture.Builder<T!> setTargetFrameRate(android.util.Range<java.lang.Integer!>);
+    method public androidx.camera.video.VideoCapture.Builder<T!> setTargetRotation(int);
+  }
+
+  @RequiresApi(21) public interface VideoOutput {
+    method public void onSurfaceRequested(androidx.camera.core.SurfaceRequest);
+  }
+
+  @RequiresApi(21) public abstract class VideoRecordEvent {
+    method public androidx.camera.video.OutputOptions getOutputOptions();
+    method public androidx.camera.video.RecordingStats getRecordingStats();
+  }
+
+  @RequiresApi(21) public static final class VideoRecordEvent.Finalize extends androidx.camera.video.VideoRecordEvent {
+    method public Throwable? getCause();
+    method public int getError();
+    method public androidx.camera.video.OutputResults getOutputResults();
+    method public boolean hasError();
+    field public static final int ERROR_DURATION_LIMIT_REACHED = 9; // 0x9
+    field public static final int ERROR_ENCODING_FAILED = 6; // 0x6
+    field public static final int ERROR_FILE_SIZE_LIMIT_REACHED = 2; // 0x2
+    field public static final int ERROR_INSUFFICIENT_STORAGE = 3; // 0x3
+    field public static final int ERROR_INVALID_OUTPUT_OPTIONS = 5; // 0x5
+    field public static final int ERROR_NONE = 0; // 0x0
+    field public static final int ERROR_NO_VALID_DATA = 8; // 0x8
+    field public static final int ERROR_RECORDER_ERROR = 7; // 0x7
+    field public static final int ERROR_RECORDING_GARBAGE_COLLECTED = 10; // 0xa
+    field public static final int ERROR_SOURCE_INACTIVE = 4; // 0x4
+    field public static final int ERROR_UNKNOWN = 1; // 0x1
+  }
+
+  @RequiresApi(21) public static final class VideoRecordEvent.Pause extends androidx.camera.video.VideoRecordEvent {
+  }
+
+  @RequiresApi(21) public static final class VideoRecordEvent.Resume extends androidx.camera.video.VideoRecordEvent {
+  }
+
+  @RequiresApi(21) public static final class VideoRecordEvent.Start extends androidx.camera.video.VideoRecordEvent {
+  }
+
+  @RequiresApi(21) public static final class VideoRecordEvent.Status extends androidx.camera.video.VideoRecordEvent {
+  }
+
+}
+
diff --git a/camera/camera-video/api/restricted_current.txt b/camera/camera-video/api/restricted_current.txt
index fb0f4c7..b9cc77a 100644
--- a/camera/camera-video/api/restricted_current.txt
+++ b/camera/camera-video/api/restricted_current.txt
@@ -14,6 +14,9 @@
     field public static final int AUDIO_STATE_SOURCE_SILENCED = 2; // 0x2
   }
 
+  @SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface ExperimentalPersistentRecording {
+  }
+
   @RequiresApi(21) public class FallbackStrategy {
     method public static androidx.camera.video.FallbackStrategy higherQualityOrLowerThan(androidx.camera.video.Quality);
     method public static androidx.camera.video.FallbackStrategy higherQualityThan(androidx.camera.video.Quality);
@@ -75,6 +78,7 @@
   }
 
   @RequiresApi(21) public final class PendingRecording {
+    method @SuppressCompatibility @androidx.camera.video.ExperimentalPersistentRecording public androidx.camera.video.PendingRecording asPersistentRecording();
     method @CheckResult public androidx.camera.video.Recording start(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.camera.video.VideoRecordEvent!>);
     method @RequiresPermission(android.Manifest.permission.RECORD_AUDIO) public androidx.camera.video.PendingRecording withAudioEnabled();
   }
@@ -122,6 +126,7 @@
 
   @RequiresApi(21) public final class Recording implements java.lang.AutoCloseable {
     method public void close();
+    method @SuppressCompatibility @androidx.camera.video.ExperimentalPersistentRecording public boolean isPersistent();
     method public void mute(boolean);
     method public void pause();
     method public void resume();
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoCaptureDeviceTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoCaptureDeviceTest.kt
index 43507b1..ecbde2c 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoCaptureDeviceTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoCaptureDeviceTest.kt
@@ -28,8 +28,10 @@
 import androidx.camera.core.CameraXConfig
 import androidx.camera.core.DynamicRange
 import androidx.camera.core.DynamicRange.BIT_DEPTH_10_BIT
+import androidx.camera.core.DynamicRange.HDR10_10_BIT
 import androidx.camera.core.DynamicRange.HDR_UNSPECIFIED_10_BIT
 import androidx.camera.core.DynamicRange.HLG_10_BIT
+import androidx.camera.core.DynamicRange.SDR
 import androidx.camera.core.Preview
 import androidx.camera.core.SurfaceRequest
 import androidx.camera.core.impl.CameraInfoInternal
@@ -39,7 +41,9 @@
 import androidx.camera.testing.CameraPipeConfigTestRule
 import androidx.camera.testing.CameraUtil
 import androidx.camera.testing.CameraXUtil
+import androidx.camera.testing.EncoderProfilesUtil.RESOLUTION_1080P
 import androidx.camera.testing.EncoderProfilesUtil.RESOLUTION_2160P
+import androidx.camera.testing.EncoderProfilesUtil.RESOLUTION_480P
 import androidx.camera.testing.EncoderProfilesUtil.RESOLUTION_720P
 import androidx.camera.testing.EncoderProfilesUtil.createFakeEncoderProfilesProxy
 import androidx.camera.testing.GLUtil
@@ -120,14 +124,18 @@
 
     private val context: Context = ApplicationProvider.getApplicationContext()
     private val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
-    // TODO(b/278168212): Only SDR is checked by now. Need to extend to HDR dynamic ranges.
-    private val dynamicRange = DynamicRange.SDR
     private val supportedResolutionMap = mapOf(
-        DynamicRange.SDR to linkedMapOf(
+        SDR to mapOf(
             Quality.HIGHEST to RESOLUTION_2160P,
             Quality.UHD to RESOLUTION_2160P,
             Quality.HD to RESOLUTION_720P,
             Quality.LOWEST to RESOLUTION_720P
+        ),
+        HDR10_10_BIT to mapOf(
+            Quality.HIGHEST to RESOLUTION_1080P,
+            Quality.FHD to RESOLUTION_1080P,
+            Quality.SD to RESOLUTION_480P,
+            Quality.LOWEST to RESOLUTION_480P
         )
     )
 
@@ -230,43 +238,46 @@
         assumeExtraCroppingQuirk(implName)
 
         val videoCapabilities = createFakeVideoCapabilities(supportedResolutionMap)
-        assumeTrue(videoCapabilities.getSupportedQualities(dynamicRange).isNotEmpty())
-        // Cuttlefish API 29 has inconsistent resolution issue. See b/184015059.
-        assumeFalse(Build.MODEL.contains("Cuttlefish") && Build.VERSION.SDK_INT == 29)
+        videoCapabilities.supportedDynamicRanges.forEach { dynamicRange ->
+            assumeTrue(videoCapabilities.getSupportedQualities(dynamicRange).isNotEmpty())
+            // Cuttlefish API 29 has inconsistent resolution issue. See b/184015059.
+            assumeFalse(Build.MODEL.contains("Cuttlefish") && Build.VERSION.SDK_INT == 29)
 
-        // Arrange.
-        val qualityList = videoCapabilities.getSupportedQualities(dynamicRange)
-        qualityList.forEach loop@{ quality ->
-            val profile = videoCapabilities.getProfiles(quality, dynamicRange)!!.defaultVideoProfile
-            val targetResolution = Size(profile.width, profile.height)
-            val videoOutput = createTestVideoOutput(
-                mediaSpec = MediaSpec.builder().configureVideo {
-                    it.setQualitySelector(QualitySelector.from(quality))
-                }.build(),
-                videoCapabilities = videoCapabilities
-            )
+            // Arrange.
+            val qualityList = videoCapabilities.getSupportedQualities(dynamicRange)
+            qualityList.forEach loop@{ quality ->
+                val profile =
+                    videoCapabilities.getProfiles(quality, dynamicRange)!!.defaultVideoProfile
+                val targetResolution = Size(profile.width, profile.height)
+                val videoOutput = createTestVideoOutput(
+                    mediaSpec = MediaSpec.builder().configureVideo {
+                        it.setQualitySelector(QualitySelector.from(quality))
+                    }.build(),
+                    videoCapabilities = videoCapabilities
+                )
 
-            // Use custom VideoEncoderInfoFinder which always returns default FakeVideoEncoderInfo,
-            // which tolerance typical resolutions.
-            val videoCapture = VideoCapture.Builder(videoOutput)
-                .setVideoEncoderInfoFinder { FakeVideoEncoderInfo() }.build()
+                // Use custom VideoEncoderInfoFinder which always returns default
+                // FakeVideoEncoderInfo, which tolerance typical resolutions.
+                val videoCapture = VideoCapture.Builder(videoOutput)
+                    .setVideoEncoderInfoFinder { FakeVideoEncoderInfo() }.build()
 
-            // Act.
-            if (!cameraUseCaseAdapter.isUseCasesCombinationSupported(videoCapture)) {
-                return@loop
-            }
-            withContext(Dispatchers.Main) {
-                cameraUseCaseAdapter.addUseCases(listOf(videoCapture))
-            }
+                // Act.
+                if (!cameraUseCaseAdapter.isUseCasesCombinationSupported(videoCapture)) {
+                    return@loop
+                }
+                withContext(Dispatchers.Main) {
+                    cameraUseCaseAdapter.addUseCases(listOf(videoCapture))
+                }
 
-            // Assert.
-            assertWithMessage("Set quality value by $quality")
-                .that(videoCapture.attachedSurfaceResolution).isEqualTo(targetResolution)
+                // Assert.
+                assertWithMessage("Set quality value by $quality")
+                    .that(videoCapture.attachedSurfaceResolution).isEqualTo(targetResolution)
 
-            // Cleanup.
-            withContext(Dispatchers.Main) {
-                cameraUseCaseAdapter.apply {
-                    removeUseCases(listOf(videoCapture))
+                // Cleanup.
+                withContext(Dispatchers.Main) {
+                    cameraUseCaseAdapter.apply {
+                        removeUseCases(listOf(videoCapture))
+                    }
                 }
             }
         }
@@ -384,7 +395,7 @@
     @Test
     fun defaultDynamicRange_isSdr(): Unit = runBlocking {
         testDynamicRangeSelection { selectedDynamicRange ->
-            assertThat(selectedDynamicRange).isEqualTo(DynamicRange.SDR)
+            assertThat(selectedDynamicRange).isEqualTo(SDR)
         }
     }
 
@@ -527,7 +538,7 @@
      * Create a fake VideoCapabilities.
      */
     private fun createFakeVideoCapabilities(
-        resolutionMap: Map<DynamicRange, LinkedHashMap<Quality, Size>>
+        resolutionMap: Map<DynamicRange, Map<Quality, Size>>
     ): VideoCapabilities {
         return object : VideoCapabilities {
 
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/ExperimentalPersistentRecording.java b/camera/camera-video/src/main/java/androidx/camera/video/ExperimentalPersistentRecording.java
new file mode 100644
index 0000000..08a702c
--- /dev/null
+++ b/camera/camera-video/src/main/java/androidx/camera/video/ExperimentalPersistentRecording.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.video;
+
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import androidx.annotation.RequiresOptIn;
+
+import java.lang.annotation.Retention;
+
+/**
+ * Denotes that the annotated method uses the experimental methods which allows a recording to be
+ * persistent.
+ *
+ * <p>A persistent recording will only be stopped by explicitly calling {@link Recording#stop()}
+ * or {@link Recording#close()} and will ignore events that would normally cause recording to
+ * stop, such as lifecycle events or explicit unbinding of a {@link VideoCapture} use case that
+ * the recording's {@link Recorder} is attached to.
+ */
+@Retention(CLASS)
+@RequiresOptIn
+public @interface ExperimentalPersistentRecording {
+}
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/PendingRecording.java b/camera/camera-video/src/main/java/androidx/camera/video/PendingRecording.java
index a242264..2307d71 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/PendingRecording.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/PendingRecording.java
@@ -24,8 +24,6 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RequiresPermission;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.RestrictTo.Scope;
 import androidx.camera.core.impl.utils.ContextUtil;
 import androidx.core.content.PermissionChecker;
 import androidx.core.util.Consumer;
@@ -151,13 +149,55 @@
      * normally cause recording to stop, such as lifecycle events or explicit unbinding of a
      * {@link VideoCapture} use case that the recording's {@link Recorder} is attached to.
      *
+     * <p>Even though lifecycle events or explicit unbinding use cases won't stop a persistent
+     * recording, it will still stop the camera from producing data, resulting in the in-progress
+     * persistent recording stopping getting data until the camera stream is activated again. For
+     * example, when the activity goes into background, the recording will keep waiting for new
+     * data to be recorded until the activity is back to foreground.
+     *
      * <p>A {@link Recorder} instance is recommended to be associated with a single
      * {@link VideoCapture} instance, especially when using persistent recording. Otherwise, there
      * might be unexpected behavior. Any in-progress persistent recording created from the same
      * {@link Recorder} should be stopped before starting a new recording, even if the
      * {@link Recorder} is associated with a different {@link VideoCapture}.
+     *
+     * <p>To switch to a different camera stream while a recording is in progress, first create
+     * the recording as persistent recording, then rebind the {@link VideoCapture} it's
+     * associated with to a different camera. The implementation may be like:
+     * <pre>{@code
+     * // Prepare the Recorder and VideoCapture, then bind the VideoCapture to the back camera.
+     * Recorder recorder = Recorder.Builder().build();
+     * VideoCapture videoCapture = VideoCapture.withOutput(recorder);
+     * cameraProvider.bindToLifecycle(
+     *         lifecycleOwner, CameraSelector.DEFAULT_BACK_CAMERA, videoCapture);
+     *
+     * // Prepare the persistent recording and start it.
+     * Recording recording = recorder
+     *         .prepareRecording(context, outputOptions)
+     *         .asPersistentRecording()
+     *         .start(eventExecutor, eventListener);
+     *
+     * // Record from the back camera for a period of time.
+     *
+     * // Rebind the VideoCapture to the front camera.
+     * cameraProvider.unbindAll();
+     * cameraProvider.bindToLifecycle(
+     *         lifecycleOwner, CameraSelector.DEFAULT_FRONT_CAMERA, videoCapture);
+     *
+     * // Record from the front camera for a period of time.
+     *
+     * // Stop the recording explicitly.
+     * recording.stop();
+     * }</pre>
+     *
+     * <p>The audio data will still be recorded after the {@link VideoCapture} is unbound.
+     * {@link Recording#pause() Pause} the recording first and {@link Recording#resume() resume} it
+     * later to stop recording audio while rebinding use cases.
+     *
+     * <p>If the recording is unable to receive data from the new camera, possibly because of
+     * incompatible surface combination, an exception will be thrown when binding to lifecycle.
      */
-    @RestrictTo(Scope.LIBRARY_GROUP)
+    @ExperimentalPersistentRecording
     @NonNull
     public PendingRecording asPersistentRecording() {
         mIsPersistent = true;
@@ -188,6 +228,10 @@
      * {@link VideoRecordEvent.Finalize} event will contain error
      * {@link VideoRecordEvent.Finalize#ERROR_RECORDING_GARBAGE_COLLECTED}.
      *
+     * <p>The {@link Recording} will be stopped automatically if the {@link VideoCapture} its
+     * {@link Recorder} is attached to is unbound unless it's created
+     * {@link #asPersistentRecording() as a persistent recording}.
+     *
      * @throws IllegalStateException if the associated Recorder currently has an unfinished
      * active recording.
      * @param listenerExecutor the executor that the event listener will be run on.
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/Recording.java b/camera/camera-video/src/main/java/androidx/camera/video/Recording.java
index 297935b..5a603a0 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/Recording.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/Recording.java
@@ -122,7 +122,7 @@
      *
      * @return {@code true} if the recording is a persistent recording, otherwise {@code false}.
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @ExperimentalPersistentRecording
     public boolean isPersistent() {
         return mIsPersistent;
     }
diff --git a/camera/camera-view/api/1.3.0-beta02.txt b/camera/camera-view/api/1.3.0-beta02.txt
new file mode 100644
index 0000000..6184aeb
--- /dev/null
+++ b/camera/camera-view/api/1.3.0-beta02.txt
@@ -0,0 +1,173 @@
+// Signature format: 4.0
+package androidx.camera.view {
+
+  @RequiresApi(21) public abstract class CameraController {
+    method @MainThread public void clearEffects();
+    method @MainThread public void clearImageAnalysisAnalyzer();
+    method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enableTorch(boolean);
+    method @MainThread public androidx.camera.core.CameraControl? getCameraControl();
+    method @MainThread public androidx.camera.core.CameraInfo? getCameraInfo();
+    method @MainThread public androidx.camera.core.CameraSelector getCameraSelector();
+    method @MainThread public java.util.concurrent.Executor? getImageAnalysisBackgroundExecutor();
+    method @MainThread public int getImageAnalysisBackpressureStrategy();
+    method @MainThread public int getImageAnalysisImageQueueDepth();
+    method @MainThread public androidx.camera.view.CameraController.OutputSize? getImageAnalysisTargetSize();
+    method @MainThread public int getImageCaptureFlashMode();
+    method @MainThread public java.util.concurrent.Executor? getImageCaptureIoExecutor();
+    method @MainThread public int getImageCaptureMode();
+    method @MainThread public androidx.camera.view.CameraController.OutputSize? getImageCaptureTargetSize();
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> getInitializationFuture();
+    method @MainThread public androidx.camera.view.CameraController.OutputSize? getPreviewTargetSize();
+    method @MainThread public androidx.lifecycle.LiveData<java.lang.Integer!> getTapToFocusState();
+    method @MainThread public androidx.lifecycle.LiveData<java.lang.Integer!> getTorchState();
+    method @MainThread public androidx.camera.video.QualitySelector getVideoCaptureQualitySelector();
+    method @MainThread public androidx.lifecycle.LiveData<androidx.camera.core.ZoomState!> getZoomState();
+    method @MainThread public boolean hasCamera(androidx.camera.core.CameraSelector);
+    method @MainThread public boolean isImageAnalysisEnabled();
+    method @MainThread public boolean isImageCaptureEnabled();
+    method @MainThread public boolean isPinchToZoomEnabled();
+    method @MainThread public boolean isRecording();
+    method @MainThread public boolean isTapToFocusEnabled();
+    method @MainThread public boolean isVideoCaptureEnabled();
+    method @MainThread public void setCameraSelector(androidx.camera.core.CameraSelector);
+    method @MainThread public void setEffects(java.util.Set<androidx.camera.core.CameraEffect!>);
+    method @MainThread public void setEnabledUseCases(int);
+    method @MainThread public void setImageAnalysisAnalyzer(java.util.concurrent.Executor, androidx.camera.core.ImageAnalysis.Analyzer);
+    method @MainThread public void setImageAnalysisBackgroundExecutor(java.util.concurrent.Executor?);
+    method @MainThread public void setImageAnalysisBackpressureStrategy(int);
+    method @MainThread public void setImageAnalysisImageQueueDepth(int);
+    method @MainThread public void setImageAnalysisTargetSize(androidx.camera.view.CameraController.OutputSize?);
+    method @MainThread public void setImageCaptureFlashMode(int);
+    method @MainThread public void setImageCaptureIoExecutor(java.util.concurrent.Executor?);
+    method @MainThread public void setImageCaptureMode(int);
+    method @MainThread public void setImageCaptureTargetSize(androidx.camera.view.CameraController.OutputSize?);
+    method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setLinearZoom(@FloatRange(from=0.0f, to=1.0f) float);
+    method @MainThread public void setPinchToZoomEnabled(boolean);
+    method @MainThread public void setPreviewTargetSize(androidx.camera.view.CameraController.OutputSize?);
+    method @MainThread public void setTapToFocusEnabled(boolean);
+    method @MainThread public void setVideoCaptureQualitySelector(androidx.camera.video.QualitySelector);
+    method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setZoomRatio(float);
+    method @MainThread @RequiresApi(26) public androidx.camera.video.Recording startRecording(androidx.camera.video.FileDescriptorOutputOptions, androidx.camera.view.video.AudioConfig, java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.camera.video.VideoRecordEvent!>);
+    method @MainThread public androidx.camera.video.Recording startRecording(androidx.camera.video.FileOutputOptions, androidx.camera.view.video.AudioConfig, java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.camera.video.VideoRecordEvent!>);
+    method @MainThread public androidx.camera.video.Recording startRecording(androidx.camera.video.MediaStoreOutputOptions, androidx.camera.view.video.AudioConfig, java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.camera.video.VideoRecordEvent!>);
+    method @MainThread public void takePicture(androidx.camera.core.ImageCapture.OutputFileOptions, java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageSavedCallback);
+    method @MainThread public void takePicture(java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageCapturedCallback);
+    field public static final int COORDINATE_SYSTEM_VIEW_REFERENCED = 1; // 0x1
+    field public static final int IMAGE_ANALYSIS = 2; // 0x2
+    field public static final int IMAGE_CAPTURE = 1; // 0x1
+    field public static final int TAP_TO_FOCUS_FAILED = 4; // 0x4
+    field public static final int TAP_TO_FOCUS_FOCUSED = 2; // 0x2
+    field public static final int TAP_TO_FOCUS_NOT_FOCUSED = 3; // 0x3
+    field public static final int TAP_TO_FOCUS_NOT_STARTED = 0; // 0x0
+    field public static final int TAP_TO_FOCUS_STARTED = 1; // 0x1
+    field public static final int VIDEO_CAPTURE = 4; // 0x4
+  }
+
+  @RequiresApi(21) public static final class CameraController.OutputSize {
+    ctor public CameraController.OutputSize(android.util.Size);
+    ctor public CameraController.OutputSize(int);
+    method public int getAspectRatio();
+    method public android.util.Size? getResolution();
+    field public static final int UNASSIGNED_ASPECT_RATIO = -1; // 0xffffffff
+  }
+
+  @RequiresApi(21) public final class LifecycleCameraController extends androidx.camera.view.CameraController {
+    ctor public LifecycleCameraController(android.content.Context);
+    method @MainThread public void bindToLifecycle(androidx.lifecycle.LifecycleOwner);
+    method @MainThread public void unbind();
+  }
+
+  @RequiresApi(21) public final class PreviewView extends android.widget.FrameLayout {
+    ctor @UiThread public PreviewView(android.content.Context);
+    ctor @UiThread public PreviewView(android.content.Context, android.util.AttributeSet?);
+    ctor @UiThread public PreviewView(android.content.Context, android.util.AttributeSet?, int);
+    ctor @UiThread public PreviewView(android.content.Context, android.util.AttributeSet?, int, int);
+    method @UiThread public android.graphics.Bitmap? getBitmap();
+    method @UiThread public androidx.camera.view.CameraController? getController();
+    method @UiThread public androidx.camera.view.PreviewView.ImplementationMode getImplementationMode();
+    method @UiThread public androidx.camera.core.MeteringPointFactory getMeteringPointFactory();
+    method @SuppressCompatibility public androidx.camera.view.transform.OutputTransform? getOutputTransform();
+    method public androidx.lifecycle.LiveData<androidx.camera.view.PreviewView.StreamState!> getPreviewStreamState();
+    method @UiThread public androidx.camera.view.PreviewView.ScaleType getScaleType();
+    method @UiThread public androidx.camera.core.Preview.SurfaceProvider getSurfaceProvider();
+    method @UiThread public androidx.camera.core.ViewPort? getViewPort();
+    method @UiThread public androidx.camera.core.ViewPort? getViewPort(int);
+    method @UiThread public void setController(androidx.camera.view.CameraController?);
+    method @UiThread public void setImplementationMode(androidx.camera.view.PreviewView.ImplementationMode);
+    method @UiThread public void setScaleType(androidx.camera.view.PreviewView.ScaleType);
+  }
+
+  @RequiresApi(21) public enum PreviewView.ImplementationMode {
+    enum_constant public static final androidx.camera.view.PreviewView.ImplementationMode COMPATIBLE;
+    enum_constant public static final androidx.camera.view.PreviewView.ImplementationMode PERFORMANCE;
+  }
+
+  @RequiresApi(21) public enum PreviewView.ScaleType {
+    enum_constant public static final androidx.camera.view.PreviewView.ScaleType FILL_CENTER;
+    enum_constant public static final androidx.camera.view.PreviewView.ScaleType FILL_END;
+    enum_constant public static final androidx.camera.view.PreviewView.ScaleType FILL_START;
+    enum_constant public static final androidx.camera.view.PreviewView.ScaleType FIT_CENTER;
+    enum_constant public static final androidx.camera.view.PreviewView.ScaleType FIT_END;
+    enum_constant public static final androidx.camera.view.PreviewView.ScaleType FIT_START;
+  }
+
+  public enum PreviewView.StreamState {
+    enum_constant public static final androidx.camera.view.PreviewView.StreamState IDLE;
+    enum_constant public static final androidx.camera.view.PreviewView.StreamState STREAMING;
+  }
+
+  @RequiresApi(21) public final class RotationProvider {
+    ctor public RotationProvider(android.content.Context);
+    method @CheckResult public boolean addListener(java.util.concurrent.Executor, androidx.camera.view.RotationProvider.Listener);
+    method public void removeListener(androidx.camera.view.RotationProvider.Listener);
+  }
+
+  public static interface RotationProvider.Listener {
+    method public void onRotationChanged(int);
+  }
+
+}
+
+package androidx.camera.view.transform {
+
+  @SuppressCompatibility @RequiresApi(21) public final class CoordinateTransform {
+    ctor public CoordinateTransform(androidx.camera.view.transform.OutputTransform, androidx.camera.view.transform.OutputTransform);
+    method public void mapPoint(android.graphics.PointF);
+    method public void mapPoints(float[]);
+    method public void mapRect(android.graphics.RectF);
+    method public void transform(android.graphics.Matrix);
+  }
+
+  @SuppressCompatibility @RequiresApi(21) public final class FileTransformFactory {
+    ctor public FileTransformFactory();
+    method public androidx.camera.view.transform.OutputTransform getOutputTransform(android.content.ContentResolver, android.net.Uri) throws java.io.IOException;
+    method public androidx.camera.view.transform.OutputTransform getOutputTransform(java.io.File) throws java.io.IOException;
+    method public androidx.camera.view.transform.OutputTransform getOutputTransform(java.io.InputStream) throws java.io.IOException;
+    method public boolean isUsingExifOrientation();
+    method public void setUsingExifOrientation(boolean);
+  }
+
+  @SuppressCompatibility @RequiresApi(21) public final class ImageProxyTransformFactory {
+    ctor public ImageProxyTransformFactory();
+    method public androidx.camera.view.transform.OutputTransform getOutputTransform(androidx.camera.core.ImageProxy);
+    method public boolean isUsingCropRect();
+    method public boolean isUsingRotationDegrees();
+    method public void setUsingCropRect(boolean);
+    method public void setUsingRotationDegrees(boolean);
+  }
+
+  @SuppressCompatibility @RequiresApi(21) public final class OutputTransform {
+  }
+
+}
+
+package androidx.camera.view.video {
+
+  @RequiresApi(21) public class AudioConfig {
+    method @RequiresPermission(android.Manifest.permission.RECORD_AUDIO) public static androidx.camera.view.video.AudioConfig create(boolean);
+    method public boolean getAudioEnabled();
+    field public static final androidx.camera.view.video.AudioConfig AUDIO_DISABLED;
+  }
+
+}
+
diff --git a/camera/camera-view/api/res-1.3.0-beta02.txt b/camera/camera-view/api/res-1.3.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/camera/camera-view/api/res-1.3.0-beta02.txt
diff --git a/camera/camera-view/api/restricted_1.3.0-beta02.txt b/camera/camera-view/api/restricted_1.3.0-beta02.txt
new file mode 100644
index 0000000..6184aeb
--- /dev/null
+++ b/camera/camera-view/api/restricted_1.3.0-beta02.txt
@@ -0,0 +1,173 @@
+// Signature format: 4.0
+package androidx.camera.view {
+
+  @RequiresApi(21) public abstract class CameraController {
+    method @MainThread public void clearEffects();
+    method @MainThread public void clearImageAnalysisAnalyzer();
+    method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enableTorch(boolean);
+    method @MainThread public androidx.camera.core.CameraControl? getCameraControl();
+    method @MainThread public androidx.camera.core.CameraInfo? getCameraInfo();
+    method @MainThread public androidx.camera.core.CameraSelector getCameraSelector();
+    method @MainThread public java.util.concurrent.Executor? getImageAnalysisBackgroundExecutor();
+    method @MainThread public int getImageAnalysisBackpressureStrategy();
+    method @MainThread public int getImageAnalysisImageQueueDepth();
+    method @MainThread public androidx.camera.view.CameraController.OutputSize? getImageAnalysisTargetSize();
+    method @MainThread public int getImageCaptureFlashMode();
+    method @MainThread public java.util.concurrent.Executor? getImageCaptureIoExecutor();
+    method @MainThread public int getImageCaptureMode();
+    method @MainThread public androidx.camera.view.CameraController.OutputSize? getImageCaptureTargetSize();
+    method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> getInitializationFuture();
+    method @MainThread public androidx.camera.view.CameraController.OutputSize? getPreviewTargetSize();
+    method @MainThread public androidx.lifecycle.LiveData<java.lang.Integer!> getTapToFocusState();
+    method @MainThread public androidx.lifecycle.LiveData<java.lang.Integer!> getTorchState();
+    method @MainThread public androidx.camera.video.QualitySelector getVideoCaptureQualitySelector();
+    method @MainThread public androidx.lifecycle.LiveData<androidx.camera.core.ZoomState!> getZoomState();
+    method @MainThread public boolean hasCamera(androidx.camera.core.CameraSelector);
+    method @MainThread public boolean isImageAnalysisEnabled();
+    method @MainThread public boolean isImageCaptureEnabled();
+    method @MainThread public boolean isPinchToZoomEnabled();
+    method @MainThread public boolean isRecording();
+    method @MainThread public boolean isTapToFocusEnabled();
+    method @MainThread public boolean isVideoCaptureEnabled();
+    method @MainThread public void setCameraSelector(androidx.camera.core.CameraSelector);
+    method @MainThread public void setEffects(java.util.Set<androidx.camera.core.CameraEffect!>);
+    method @MainThread public void setEnabledUseCases(int);
+    method @MainThread public void setImageAnalysisAnalyzer(java.util.concurrent.Executor, androidx.camera.core.ImageAnalysis.Analyzer);
+    method @MainThread public void setImageAnalysisBackgroundExecutor(java.util.concurrent.Executor?);
+    method @MainThread public void setImageAnalysisBackpressureStrategy(int);
+    method @MainThread public void setImageAnalysisImageQueueDepth(int);
+    method @MainThread public void setImageAnalysisTargetSize(androidx.camera.view.CameraController.OutputSize?);
+    method @MainThread public void setImageCaptureFlashMode(int);
+    method @MainThread public void setImageCaptureIoExecutor(java.util.concurrent.Executor?);
+    method @MainThread public void setImageCaptureMode(int);
+    method @MainThread public void setImageCaptureTargetSize(androidx.camera.view.CameraController.OutputSize?);
+    method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setLinearZoom(@FloatRange(from=0.0f, to=1.0f) float);
+    method @MainThread public void setPinchToZoomEnabled(boolean);
+    method @MainThread public void setPreviewTargetSize(androidx.camera.view.CameraController.OutputSize?);
+    method @MainThread public void setTapToFocusEnabled(boolean);
+    method @MainThread public void setVideoCaptureQualitySelector(androidx.camera.video.QualitySelector);
+    method @MainThread public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setZoomRatio(float);
+    method @MainThread @RequiresApi(26) public androidx.camera.video.Recording startRecording(androidx.camera.video.FileDescriptorOutputOptions, androidx.camera.view.video.AudioConfig, java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.camera.video.VideoRecordEvent!>);
+    method @MainThread public androidx.camera.video.Recording startRecording(androidx.camera.video.FileOutputOptions, androidx.camera.view.video.AudioConfig, java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.camera.video.VideoRecordEvent!>);
+    method @MainThread public androidx.camera.video.Recording startRecording(androidx.camera.video.MediaStoreOutputOptions, androidx.camera.view.video.AudioConfig, java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.camera.video.VideoRecordEvent!>);
+    method @MainThread public void takePicture(androidx.camera.core.ImageCapture.OutputFileOptions, java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageSavedCallback);
+    method @MainThread public void takePicture(java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageCapturedCallback);
+    field public static final int COORDINATE_SYSTEM_VIEW_REFERENCED = 1; // 0x1
+    field public static final int IMAGE_ANALYSIS = 2; // 0x2
+    field public static final int IMAGE_CAPTURE = 1; // 0x1
+    field public static final int TAP_TO_FOCUS_FAILED = 4; // 0x4
+    field public static final int TAP_TO_FOCUS_FOCUSED = 2; // 0x2
+    field public static final int TAP_TO_FOCUS_NOT_FOCUSED = 3; // 0x3
+    field public static final int TAP_TO_FOCUS_NOT_STARTED = 0; // 0x0
+    field public static final int TAP_TO_FOCUS_STARTED = 1; // 0x1
+    field public static final int VIDEO_CAPTURE = 4; // 0x4
+  }
+
+  @RequiresApi(21) public static final class CameraController.OutputSize {
+    ctor public CameraController.OutputSize(android.util.Size);
+    ctor public CameraController.OutputSize(int);
+    method public int getAspectRatio();
+    method public android.util.Size? getResolution();
+    field public static final int UNASSIGNED_ASPECT_RATIO = -1; // 0xffffffff
+  }
+
+  @RequiresApi(21) public final class LifecycleCameraController extends androidx.camera.view.CameraController {
+    ctor public LifecycleCameraController(android.content.Context);
+    method @MainThread public void bindToLifecycle(androidx.lifecycle.LifecycleOwner);
+    method @MainThread public void unbind();
+  }
+
+  @RequiresApi(21) public final class PreviewView extends android.widget.FrameLayout {
+    ctor @UiThread public PreviewView(android.content.Context);
+    ctor @UiThread public PreviewView(android.content.Context, android.util.AttributeSet?);
+    ctor @UiThread public PreviewView(android.content.Context, android.util.AttributeSet?, int);
+    ctor @UiThread public PreviewView(android.content.Context, android.util.AttributeSet?, int, int);
+    method @UiThread public android.graphics.Bitmap? getBitmap();
+    method @UiThread public androidx.camera.view.CameraController? getController();
+    method @UiThread public androidx.camera.view.PreviewView.ImplementationMode getImplementationMode();
+    method @UiThread public androidx.camera.core.MeteringPointFactory getMeteringPointFactory();
+    method @SuppressCompatibility public androidx.camera.view.transform.OutputTransform? getOutputTransform();
+    method public androidx.lifecycle.LiveData<androidx.camera.view.PreviewView.StreamState!> getPreviewStreamState();
+    method @UiThread public androidx.camera.view.PreviewView.ScaleType getScaleType();
+    method @UiThread public androidx.camera.core.Preview.SurfaceProvider getSurfaceProvider();
+    method @UiThread public androidx.camera.core.ViewPort? getViewPort();
+    method @UiThread public androidx.camera.core.ViewPort? getViewPort(int);
+    method @UiThread public void setController(androidx.camera.view.CameraController?);
+    method @UiThread public void setImplementationMode(androidx.camera.view.PreviewView.ImplementationMode);
+    method @UiThread public void setScaleType(androidx.camera.view.PreviewView.ScaleType);
+  }
+
+  @RequiresApi(21) public enum PreviewView.ImplementationMode {
+    enum_constant public static final androidx.camera.view.PreviewView.ImplementationMode COMPATIBLE;
+    enum_constant public static final androidx.camera.view.PreviewView.ImplementationMode PERFORMANCE;
+  }
+
+  @RequiresApi(21) public enum PreviewView.ScaleType {
+    enum_constant public static final androidx.camera.view.PreviewView.ScaleType FILL_CENTER;
+    enum_constant public static final androidx.camera.view.PreviewView.ScaleType FILL_END;
+    enum_constant public static final androidx.camera.view.PreviewView.ScaleType FILL_START;
+    enum_constant public static final androidx.camera.view.PreviewView.ScaleType FIT_CENTER;
+    enum_constant public static final androidx.camera.view.PreviewView.ScaleType FIT_END;
+    enum_constant public static final androidx.camera.view.PreviewView.ScaleType FIT_START;
+  }
+
+  public enum PreviewView.StreamState {
+    enum_constant public static final androidx.camera.view.PreviewView.StreamState IDLE;
+    enum_constant public static final androidx.camera.view.PreviewView.StreamState STREAMING;
+  }
+
+  @RequiresApi(21) public final class RotationProvider {
+    ctor public RotationProvider(android.content.Context);
+    method @CheckResult public boolean addListener(java.util.concurrent.Executor, androidx.camera.view.RotationProvider.Listener);
+    method public void removeListener(androidx.camera.view.RotationProvider.Listener);
+  }
+
+  public static interface RotationProvider.Listener {
+    method public void onRotationChanged(int);
+  }
+
+}
+
+package androidx.camera.view.transform {
+
+  @SuppressCompatibility @RequiresApi(21) public final class CoordinateTransform {
+    ctor public CoordinateTransform(androidx.camera.view.transform.OutputTransform, androidx.camera.view.transform.OutputTransform);
+    method public void mapPoint(android.graphics.PointF);
+    method public void mapPoints(float[]);
+    method public void mapRect(android.graphics.RectF);
+    method public void transform(android.graphics.Matrix);
+  }
+
+  @SuppressCompatibility @RequiresApi(21) public final class FileTransformFactory {
+    ctor public FileTransformFactory();
+    method public androidx.camera.view.transform.OutputTransform getOutputTransform(android.content.ContentResolver, android.net.Uri) throws java.io.IOException;
+    method public androidx.camera.view.transform.OutputTransform getOutputTransform(java.io.File) throws java.io.IOException;
+    method public androidx.camera.view.transform.OutputTransform getOutputTransform(java.io.InputStream) throws java.io.IOException;
+    method public boolean isUsingExifOrientation();
+    method public void setUsingExifOrientation(boolean);
+  }
+
+  @SuppressCompatibility @RequiresApi(21) public final class ImageProxyTransformFactory {
+    ctor public ImageProxyTransformFactory();
+    method public androidx.camera.view.transform.OutputTransform getOutputTransform(androidx.camera.core.ImageProxy);
+    method public boolean isUsingCropRect();
+    method public boolean isUsingRotationDegrees();
+    method public void setUsingCropRect(boolean);
+    method public void setUsingRotationDegrees(boolean);
+  }
+
+  @SuppressCompatibility @RequiresApi(21) public final class OutputTransform {
+  }
+
+}
+
+package androidx.camera.view.video {
+
+  @RequiresApi(21) public class AudioConfig {
+    method @RequiresPermission(android.Manifest.permission.RECORD_AUDIO) public static androidx.camera.view.video.AudioConfig create(boolean);
+    method public boolean getAudioEnabled();
+    field public static final androidx.camera.view.video.AudioConfig AUDIO_DISABLED;
+  }
+
+}
+
diff --git a/camera/camera-viewfinder-compose/api/1.3.0-beta02.txt b/camera/camera-viewfinder-compose/api/1.3.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/camera/camera-viewfinder-compose/api/1.3.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/camera/camera-viewfinder-compose/api/res-1.3.0-beta02.txt b/camera/camera-viewfinder-compose/api/res-1.3.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/camera/camera-viewfinder-compose/api/res-1.3.0-beta02.txt
diff --git a/camera/camera-viewfinder-compose/api/restricted_1.3.0-beta02.txt b/camera/camera-viewfinder-compose/api/restricted_1.3.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/camera/camera-viewfinder-compose/api/restricted_1.3.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/camera/camera-viewfinder-core/api/1.3.0-beta02.txt b/camera/camera-viewfinder-core/api/1.3.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/camera/camera-viewfinder-core/api/1.3.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/camera/camera-viewfinder-core/api/res-1.3.0-beta02.txt b/camera/camera-viewfinder-core/api/res-1.3.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/camera/camera-viewfinder-core/api/res-1.3.0-beta02.txt
diff --git a/camera/camera-viewfinder-core/api/restricted_1.3.0-beta02.txt b/camera/camera-viewfinder-core/api/restricted_1.3.0-beta02.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/camera/camera-viewfinder-core/api/restricted_1.3.0-beta02.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/camera/camera-viewfinder/api/1.3.0-beta02.txt b/camera/camera-viewfinder/api/1.3.0-beta02.txt
new file mode 100644
index 0000000..a7333d7
--- /dev/null
+++ b/camera/camera-viewfinder/api/1.3.0-beta02.txt
@@ -0,0 +1,58 @@
+// Signature format: 4.0
+package androidx.camera.viewfinder {
+
+  @RequiresApi(21) public final class CameraViewfinder extends android.widget.FrameLayout {
+    ctor @UiThread public CameraViewfinder(android.content.Context);
+    ctor @UiThread public CameraViewfinder(android.content.Context, android.util.AttributeSet?);
+    ctor @UiThread public CameraViewfinder(android.content.Context, android.util.AttributeSet?, int);
+    ctor @UiThread public CameraViewfinder(android.content.Context, android.util.AttributeSet?, int, int);
+    method @UiThread public android.graphics.Bitmap? getBitmap();
+    method @UiThread public androidx.camera.viewfinder.CameraViewfinder.ImplementationMode getImplementationMode();
+    method @UiThread public androidx.camera.viewfinder.CameraViewfinder.ScaleType getScaleType();
+    method @UiThread public com.google.common.util.concurrent.ListenableFuture<android.view.Surface!> requestSurfaceAsync(androidx.camera.viewfinder.ViewfinderSurfaceRequest);
+    method @UiThread public void setScaleType(androidx.camera.viewfinder.CameraViewfinder.ScaleType);
+  }
+
+  @RequiresApi(21) public enum CameraViewfinder.ImplementationMode {
+    enum_constant public static final androidx.camera.viewfinder.CameraViewfinder.ImplementationMode COMPATIBLE;
+    enum_constant public static final androidx.camera.viewfinder.CameraViewfinder.ImplementationMode PERFORMANCE;
+  }
+
+  @RequiresApi(21) public enum CameraViewfinder.ScaleType {
+    enum_constant public static final androidx.camera.viewfinder.CameraViewfinder.ScaleType FILL_CENTER;
+    enum_constant public static final androidx.camera.viewfinder.CameraViewfinder.ScaleType FILL_END;
+    enum_constant public static final androidx.camera.viewfinder.CameraViewfinder.ScaleType FILL_START;
+    enum_constant public static final androidx.camera.viewfinder.CameraViewfinder.ScaleType FIT_CENTER;
+    enum_constant public static final androidx.camera.viewfinder.CameraViewfinder.ScaleType FIT_END;
+    enum_constant public static final androidx.camera.viewfinder.CameraViewfinder.ScaleType FIT_START;
+  }
+
+  @RequiresApi(21) public final class CameraViewfinderExt {
+    method public suspend Object? requestSurface(androidx.camera.viewfinder.CameraViewfinder, androidx.camera.viewfinder.ViewfinderSurfaceRequest viewfinderSurfaceRequest, kotlin.coroutines.Continuation<? super android.view.Surface>);
+    field public static final androidx.camera.viewfinder.CameraViewfinderExt INSTANCE;
+  }
+
+  @RequiresApi(21) public class ViewfinderSurfaceRequest {
+    method public androidx.camera.viewfinder.CameraViewfinder.ImplementationMode? getImplementationMode();
+    method public int getLensFacing();
+    method public android.util.Size getResolution();
+    method public int getSensorOrientation();
+    method public void markSurfaceSafeToRelease();
+  }
+
+  public static final class ViewfinderSurfaceRequest.Builder {
+    ctor public ViewfinderSurfaceRequest.Builder(android.util.Size);
+    ctor public ViewfinderSurfaceRequest.Builder(androidx.camera.viewfinder.ViewfinderSurfaceRequest);
+    ctor public ViewfinderSurfaceRequest.Builder(androidx.camera.viewfinder.ViewfinderSurfaceRequest.Builder);
+    method public androidx.camera.viewfinder.ViewfinderSurfaceRequest build();
+    method public androidx.camera.viewfinder.ViewfinderSurfaceRequest.Builder setImplementationMode(androidx.camera.viewfinder.CameraViewfinder.ImplementationMode?);
+    method public androidx.camera.viewfinder.ViewfinderSurfaceRequest.Builder setLensFacing(int);
+    method public androidx.camera.viewfinder.ViewfinderSurfaceRequest.Builder setSensorOrientation(int);
+  }
+
+  public final class ViewfinderSurfaceRequestUtil {
+    method @RequiresApi(21) public static androidx.camera.viewfinder.ViewfinderSurfaceRequest.Builder populateFromCharacteristics(androidx.camera.viewfinder.ViewfinderSurfaceRequest.Builder, android.hardware.camera2.CameraCharacteristics cameraCharacteristics);
+  }
+
+}
+
diff --git a/camera/camera-viewfinder/api/res-1.3.0-beta02.txt b/camera/camera-viewfinder/api/res-1.3.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/camera/camera-viewfinder/api/res-1.3.0-beta02.txt
diff --git a/camera/camera-viewfinder/api/restricted_1.3.0-beta02.txt b/camera/camera-viewfinder/api/restricted_1.3.0-beta02.txt
new file mode 100644
index 0000000..a7333d7
--- /dev/null
+++ b/camera/camera-viewfinder/api/restricted_1.3.0-beta02.txt
@@ -0,0 +1,58 @@
+// Signature format: 4.0
+package androidx.camera.viewfinder {
+
+  @RequiresApi(21) public final class CameraViewfinder extends android.widget.FrameLayout {
+    ctor @UiThread public CameraViewfinder(android.content.Context);
+    ctor @UiThread public CameraViewfinder(android.content.Context, android.util.AttributeSet?);
+    ctor @UiThread public CameraViewfinder(android.content.Context, android.util.AttributeSet?, int);
+    ctor @UiThread public CameraViewfinder(android.content.Context, android.util.AttributeSet?, int, int);
+    method @UiThread public android.graphics.Bitmap? getBitmap();
+    method @UiThread public androidx.camera.viewfinder.CameraViewfinder.ImplementationMode getImplementationMode();
+    method @UiThread public androidx.camera.viewfinder.CameraViewfinder.ScaleType getScaleType();
+    method @UiThread public com.google.common.util.concurrent.ListenableFuture<android.view.Surface!> requestSurfaceAsync(androidx.camera.viewfinder.ViewfinderSurfaceRequest);
+    method @UiThread public void setScaleType(androidx.camera.viewfinder.CameraViewfinder.ScaleType);
+  }
+
+  @RequiresApi(21) public enum CameraViewfinder.ImplementationMode {
+    enum_constant public static final androidx.camera.viewfinder.CameraViewfinder.ImplementationMode COMPATIBLE;
+    enum_constant public static final androidx.camera.viewfinder.CameraViewfinder.ImplementationMode PERFORMANCE;
+  }
+
+  @RequiresApi(21) public enum CameraViewfinder.ScaleType {
+    enum_constant public static final androidx.camera.viewfinder.CameraViewfinder.ScaleType FILL_CENTER;
+    enum_constant public static final androidx.camera.viewfinder.CameraViewfinder.ScaleType FILL_END;
+    enum_constant public static final androidx.camera.viewfinder.CameraViewfinder.ScaleType FILL_START;
+    enum_constant public static final androidx.camera.viewfinder.CameraViewfinder.ScaleType FIT_CENTER;
+    enum_constant public static final androidx.camera.viewfinder.CameraViewfinder.ScaleType FIT_END;
+    enum_constant public static final androidx.camera.viewfinder.CameraViewfinder.ScaleType FIT_START;
+  }
+
+  @RequiresApi(21) public final class CameraViewfinderExt {
+    method public suspend Object? requestSurface(androidx.camera.viewfinder.CameraViewfinder, androidx.camera.viewfinder.ViewfinderSurfaceRequest viewfinderSurfaceRequest, kotlin.coroutines.Continuation<? super android.view.Surface>);
+    field public static final androidx.camera.viewfinder.CameraViewfinderExt INSTANCE;
+  }
+
+  @RequiresApi(21) public class ViewfinderSurfaceRequest {
+    method public androidx.camera.viewfinder.CameraViewfinder.ImplementationMode? getImplementationMode();
+    method public int getLensFacing();
+    method public android.util.Size getResolution();
+    method public int getSensorOrientation();
+    method public void markSurfaceSafeToRelease();
+  }
+
+  public static final class ViewfinderSurfaceRequest.Builder {
+    ctor public ViewfinderSurfaceRequest.Builder(android.util.Size);
+    ctor public ViewfinderSurfaceRequest.Builder(androidx.camera.viewfinder.ViewfinderSurfaceRequest);
+    ctor public ViewfinderSurfaceRequest.Builder(androidx.camera.viewfinder.ViewfinderSurfaceRequest.Builder);
+    method public androidx.camera.viewfinder.ViewfinderSurfaceRequest build();
+    method public androidx.camera.viewfinder.ViewfinderSurfaceRequest.Builder setImplementationMode(androidx.camera.viewfinder.CameraViewfinder.ImplementationMode?);
+    method public androidx.camera.viewfinder.ViewfinderSurfaceRequest.Builder setLensFacing(int);
+    method public androidx.camera.viewfinder.ViewfinderSurfaceRequest.Builder setSensorOrientation(int);
+  }
+
+  public final class ViewfinderSurfaceRequestUtil {
+    method @RequiresApi(21) public static androidx.camera.viewfinder.ViewfinderSurfaceRequest.Builder populateFromCharacteristics(androidx.camera.viewfinder.ViewfinderSurfaceRequest.Builder, android.hardware.camera2.CameraCharacteristics cameraCharacteristics);
+  }
+
+}
+
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageAnalysisTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageAnalysisTest.kt
index 225e0a5..01e996f 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageAnalysisTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageAnalysisTest.kt
@@ -33,9 +33,14 @@
 import androidx.camera.core.ImageCapture
 import androidx.camera.core.ImageProxy
 import androidx.camera.core.impl.ImageOutputConfig
+import androidx.camera.core.impl.ImageOutputConfig.OPTION_RESOLUTION_SELECTOR
 import androidx.camera.core.impl.utils.executor.CameraXExecutors
+import androidx.camera.core.internal.utils.SizeUtil
+import androidx.camera.core.resolutionselector.AspectRatioStrategy
+import androidx.camera.core.resolutionselector.ResolutionFilter
 import androidx.camera.core.resolutionselector.ResolutionSelector
 import androidx.camera.core.resolutionselector.ResolutionSelector.PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE
+import androidx.camera.core.resolutionselector.ResolutionStrategy
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.CameraPipeConfigTestRule
 import androidx.camera.testing.CameraUtil
@@ -461,6 +466,36 @@
         }
     }
 
+    @Test
+    fun resolutionSelectorConfigCorrectlyMerged_afterBindToLifecycle() = runBlocking {
+        val resolutionFilter = ResolutionFilter { supportedSizes, _ -> supportedSizes }
+        val useCase = ImageAnalysis.Builder().setResolutionSelector(
+            ResolutionSelector.Builder().setResolutionFilter(resolutionFilter)
+                .setAllowedResolutionMode(PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE).build()
+        ).build()
+        withContext(Dispatchers.Main) {
+            cameraProvider.bindToLifecycle(fakeLifecycleOwner, DEFAULT_CAMERA_SELECTOR, useCase)
+        }
+        val resolutionSelector = useCase.currentConfig.retrieveOption(OPTION_RESOLUTION_SELECTOR)
+        // The default 4:3 AspectRatioStrategy is kept
+        assertThat(resolutionSelector!!.aspectRatioStrategy).isEqualTo(
+            AspectRatioStrategy.RATIO_4_3_FALLBACK_AUTO_STRATEGY
+        )
+        // The default ResolutionStrategy with VGA bound size is kept
+        assertThat(resolutionSelector.resolutionStrategy!!.boundSize).isEqualTo(
+            SizeUtil.RESOLUTION_VGA
+        )
+        assertThat(resolutionSelector.resolutionStrategy!!.fallbackRule).isEqualTo(
+            ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER
+        )
+        // The set resolutionFilter is kept
+        assertThat(resolutionSelector.resolutionFilter).isEqualTo(resolutionFilter)
+        // The set allowedResolutionMode is kept
+        assertThat(resolutionSelector.allowedResolutionMode).isEqualTo(
+            PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE
+        )
+    }
+
     private data class ImageProperties(
         val resolution: Size,
         val format: Int,
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt
index 066282a..ff11f0f 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt
@@ -58,13 +58,17 @@
 import androidx.camera.core.impl.Identifier
 import androidx.camera.core.impl.ImageCaptureConfig
 import androidx.camera.core.impl.ImageOutputConfig
+import androidx.camera.core.impl.ImageOutputConfig.OPTION_RESOLUTION_SELECTOR
 import androidx.camera.core.impl.MutableOptionsBundle
 import androidx.camera.core.impl.SessionProcessor
 import androidx.camera.core.impl.utils.CameraOrientationUtil
 import androidx.camera.core.impl.utils.Exif
 import androidx.camera.core.internal.compat.workaround.ExifRotationAvailability
+import androidx.camera.core.resolutionselector.AspectRatioStrategy
+import androidx.camera.core.resolutionselector.ResolutionFilter
 import androidx.camera.core.resolutionselector.ResolutionSelector
 import androidx.camera.core.resolutionselector.ResolutionSelector.PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE
+import androidx.camera.core.resolutionselector.ResolutionStrategy
 import androidx.camera.integration.core.util.CameraPipeUtil
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.camera.testing.CameraPipeConfigTestRule
@@ -1669,6 +1673,33 @@
             )
         }
 
+    @Test
+    fun resolutionSelectorConfigCorrectlyMerged_afterBindToLifecycle() = runBlocking {
+        val resolutionFilter = ResolutionFilter { supportedSizes, _ -> supportedSizes }
+        val useCase = ImageCapture.Builder().setResolutionSelector(
+            ResolutionSelector.Builder().setResolutionFilter(resolutionFilter)
+                .setAllowedResolutionMode(PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE).build()
+        ).build()
+        withContext(Dispatchers.Main) {
+            cameraProvider.bindToLifecycle(fakeLifecycleOwner, BACK_SELECTOR, useCase)
+        }
+        val resolutionSelector = useCase.currentConfig.retrieveOption(OPTION_RESOLUTION_SELECTOR)
+        // The default 4:3 AspectRatioStrategy is kept
+        assertThat(resolutionSelector!!.aspectRatioStrategy).isEqualTo(
+            AspectRatioStrategy.RATIO_4_3_FALLBACK_AUTO_STRATEGY
+        )
+        // The default highest available ResolutionStrategy is kept
+        assertThat(resolutionSelector.resolutionStrategy).isEqualTo(
+            ResolutionStrategy.HIGHEST_AVAILABLE_STRATEGY
+        )
+        // The set resolutionFilter is kept
+        assertThat(resolutionSelector.resolutionFilter).isEqualTo(resolutionFilter)
+        // The set allowedResolutionMode is kept
+        assertThat(resolutionSelector.allowedResolutionMode).isEqualTo(
+            PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE
+        )
+    }
+
     private fun capturedImage_withHighResolutionEnabled(
         preview: Preview? = null,
         imageAnalysis: ImageAnalysis? = null
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/UseCaseCombinationTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/UseCaseCombinationTest.kt
index 827363f..619337d 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/UseCaseCombinationTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/UseCaseCombinationTest.kt
@@ -44,6 +44,7 @@
 import java.util.concurrent.TimeUnit
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.asExecutor
+import kotlinx.coroutines.delay
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.withContext
 import org.junit.After
@@ -399,7 +400,8 @@
         withContext(Dispatchers.Main) {
             cameraProvider.unbind(preview)
         }
-        previewMonitor.waitForStreamIdle()
+        delay(1000) // Unbind and stop the output stream should be done within 1 sec.
+        previewMonitor.waitForStreamIdle(count = 1, timeMillis = TimeUnit.SECONDS.toMillis(2))
 
         // Assert
         imageCapture.waitForCapturing()
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/camera2/PreviewTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/camera2/PreviewTest.kt
index deb520d..1895c58 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/camera2/PreviewTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/camera2/PreviewTest.kt
@@ -31,8 +31,11 @@
 import androidx.camera.core.Preview
 import androidx.camera.core.UseCase
 import androidx.camera.core.impl.ImageOutputConfig
+import androidx.camera.core.impl.ImageOutputConfig.OPTION_RESOLUTION_SELECTOR
 import androidx.camera.core.impl.utils.executor.CameraXExecutors
 import androidx.camera.core.internal.CameraUseCaseAdapter
+import androidx.camera.core.resolutionselector.AspectRatioStrategy
+import androidx.camera.core.resolutionselector.ResolutionFilter
 import androidx.camera.core.resolutionselector.ResolutionSelector
 import androidx.camera.core.resolutionselector.ResolutionSelector.PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE
 import androidx.camera.core.resolutionselector.ResolutionStrategy
@@ -618,6 +621,34 @@
         ).isFalse()
     }
 
+    @Test
+    fun resolutionSelectorConfigCorrectlyMerged_afterBindToLifecycle() {
+        val resolutionFilter = ResolutionFilter { supportedSizes, _ -> supportedSizes }
+        val useCase = Preview.Builder().setResolutionSelector(
+            ResolutionSelector.Builder().setResolutionFilter(resolutionFilter)
+                .setAllowedResolutionMode(PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE).build()
+        ).build()
+        camera = CameraUtil.createCameraAndAttachUseCase(
+            context!!,
+            CameraSelector.DEFAULT_BACK_CAMERA, useCase
+        )
+        val resolutionSelector = useCase.currentConfig.retrieveOption(OPTION_RESOLUTION_SELECTOR)
+        // The default 4:3 AspectRatioStrategy is kept
+        Truth.assertThat(resolutionSelector!!.aspectRatioStrategy).isEqualTo(
+            AspectRatioStrategy.RATIO_4_3_FALLBACK_AUTO_STRATEGY
+        )
+        // The default highest available ResolutionStrategy is kept
+        Truth.assertThat(resolutionSelector.resolutionStrategy).isEqualTo(
+            ResolutionStrategy.HIGHEST_AVAILABLE_STRATEGY
+        )
+        // The set resolutionFilter is kept
+        Truth.assertThat(resolutionSelector.resolutionFilter).isEqualTo(resolutionFilter)
+        // The set allowedResolutionMode is kept
+        Truth.assertThat(resolutionSelector.allowedResolutionMode).isEqualTo(
+            PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE
+        )
+    }
+
     private val workExecutorWithNamedThread: Executor
         get() {
             val threadFactory =
diff --git a/camera/integration-tests/coretestapp/src/main/AndroidManifest.xml b/camera/integration-tests/coretestapp/src/main/AndroidManifest.xml
index 5e483e9..425ca58 100644
--- a/camera/integration-tests/coretestapp/src/main/AndroidManifest.xml
+++ b/camera/integration-tests/coretestapp/src/main/AndroidManifest.xml
@@ -65,6 +65,17 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+        <activity
+            android:name=".VideoCameraSwitchingActivity"
+            android:exported="true"
+            android:label="Video Camera Switching Test App"
+            android:taskAffinity="androidx.camera.integration.core.VideoCameraSwitchingActivity"
+            android:theme="@style/NoTitleTheme">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
     </application>
 
     <uses-feature android:glEsVersion="0x00020000" />
diff --git a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
index af3c564..b3e6e80 100644
--- a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
+++ b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
@@ -113,6 +113,7 @@
 import androidx.camera.core.impl.utils.AspectRatioUtil;
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.lifecycle.ProcessCameraProvider;
+import androidx.camera.video.ExperimentalPersistentRecording;
 import androidx.camera.video.FileOutputOptions;
 import androidx.camera.video.MediaStoreOutputOptions;
 import androidx.camera.video.OutputOptions;
@@ -581,7 +582,8 @@
         });
     }
 
-    @SuppressLint("MissingPermission")
+    @SuppressLint({"MissingPermission", "NullAnnotationGroup"})
+    @OptIn(markerClass = ExperimentalPersistentRecording.class)
     private void setUpRecordButton() {
         mRecordUi.getButtonRecord().setOnClickListener((view) -> {
             RecordUi.State state = mRecordUi.getState();
diff --git a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/VideoCameraSwitchingActivity.java b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/VideoCameraSwitchingActivity.java
new file mode 100644
index 0000000..431b3f6
--- /dev/null
+++ b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/VideoCameraSwitchingActivity.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.integration.core;
+
+import android.content.ContentValues;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.Environment;
+import android.provider.MediaStore;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Toast;
+
+import androidx.annotation.IntRange;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.camera.core.Camera;
+import androidx.camera.core.CameraSelector;
+import androidx.camera.core.Logger;
+import androidx.camera.core.Preview;
+import androidx.camera.core.impl.utils.executor.CameraXExecutors;
+import androidx.camera.lifecycle.ProcessCameraProvider;
+import androidx.camera.video.FileOutputOptions;
+import androidx.camera.video.MediaStoreOutputOptions;
+import androidx.camera.video.PendingRecording;
+import androidx.camera.video.Recorder;
+import androidx.camera.video.VideoCapture;
+import androidx.camera.video.VideoRecordEvent;
+import androidx.camera.video.internal.compat.quirk.DeviceQuirks;
+import androidx.camera.video.internal.compat.quirk.MediaStoreVideoCannotWrite;
+import androidx.camera.view.PreviewView;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+import androidx.core.util.Preconditions;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.io.File;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Activity for verifying behavior of switching camera during a recording.
+ */
+public class VideoCameraSwitchingActivity extends AppCompatActivity {
+
+    private static final String TAG = "VideoCameraSwitchingActivity";
+    // Possible values for this intent key: "BACKWARD" or "FORWARD".
+    private static final String INTENT_EXTRA_CAMERA_DIRECTION = "camera_direction";
+    // Launch the activity with the specified recording duration.
+    private static final String INTENT_EXTRA_DURATION = "recording_duration";
+    // Launch the activity with the specified camera switching time.
+    private static final String INTENT_EXTRA_SWITCH_TIME = "recording_switch_time";
+    private static final long INVALID_TIME_VALUE = -1;
+    private static final int REQUEST_CODE_PERMISSIONS = 1001;
+    private static final String[] REQUIRED_PERMISSIONS = new String[] {
+            "android.permission.CAMERA"
+    };
+    private static final long DEFAULT_DURATION_MILLIS = 10000;
+    private static final long DEFAULT_SWITCH_TIME_MILLIS = 5000;
+
+    @NonNull
+    private CameraSelector mCameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;
+    @Nullable
+    private ProcessCameraProvider mCameraProvider;
+    @Nullable
+    private PreviewView mPreviewView;
+    @Nullable
+    private EditText mDurationText;
+    @Nullable
+    private EditText mSwitchTimeText;
+    @Nullable
+    private Button mStartButton;
+    @Nullable
+    private Preview mPreview;
+    @Nullable
+    private VideoCapture<Recorder> mVideoCapture;
+    @Nullable
+    private Camera mCamera;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_video_camera_switching);
+
+        Bundle bundle = this.getIntent().getExtras();
+        long extraDurationMillis = INVALID_TIME_VALUE;
+        long extraSwitchTimeMillis = INVALID_TIME_VALUE;
+        if (bundle != null) {
+            String extraCameraDirection = bundle.getString(INTENT_EXTRA_CAMERA_DIRECTION);
+            if (extraCameraDirection != null) {
+                if (extraCameraDirection.equals("FORWARD")) {
+                    mCameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA;
+                } else {
+                    mCameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;
+                }
+            }
+            extraDurationMillis = bundle.getLong(INTENT_EXTRA_DURATION, INVALID_TIME_VALUE);
+            extraSwitchTimeMillis = bundle.getLong(INTENT_EXTRA_SWITCH_TIME, INVALID_TIME_VALUE);
+        }
+
+        mPreviewView = findViewById(R.id.camera_preview);
+        mDurationText = findViewById(R.id.duration);
+        mSwitchTimeText = findViewById(R.id.switch_time);
+        mStartButton = findViewById(R.id.start_recording);
+
+        mDurationText.setText(Long.toString(
+                extraDurationMillis == INVALID_TIME_VALUE ? DEFAULT_DURATION_MILLIS
+                        : extraDurationMillis));
+        mSwitchTimeText.setText(Long.toString(
+                extraSwitchTimeMillis == INVALID_TIME_VALUE ? DEFAULT_SWITCH_TIME_MILLIS
+                        : extraSwitchTimeMillis));
+        mStartButton.setOnClickListener(view -> startRecording());
+
+        if (allPermissionsGranted()) {
+            if (mCameraProvider != null) {
+                mCameraProvider.unbindAll();
+            }
+            prepareCamera();
+        } else {
+            ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS);
+        }
+    }
+
+    private void prepareCamera() {
+        final ListenableFuture<ProcessCameraProvider> cameraProviderFuture =
+                ProcessCameraProvider.getInstance(this);
+        cameraProviderFuture.addListener(() -> {
+            try {
+                mCameraProvider = cameraProviderFuture.get();
+                bindUseCases(mCameraProvider);
+            } catch (ExecutionException | InterruptedException e) {
+                String msg = "Fail to bind the use cases with the camera.";
+                Logger.d(TAG, msg);
+                Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
+            }
+        }, ContextCompat.getMainExecutor(this));
+    }
+
+    private void bindUseCases(@NonNull ProcessCameraProvider cameraProvider) {
+        cameraProvider.unbindAll();
+        mPreviewView.setImplementationMode(PreviewView.ImplementationMode.COMPATIBLE);
+        mPreview = new Preview.Builder().build();
+        mPreview.setSurfaceProvider(mPreviewView.getSurfaceProvider());
+        mVideoCapture = VideoCapture.withOutput(new Recorder.Builder().build());
+        mCamera = cameraProvider.bindToLifecycle(this, mCameraSelector, mPreview, mVideoCapture);
+    }
+
+    private void switchCamera() {
+        Preconditions.checkNotNull(mCamera, "The camera instance should not be null.");
+        Preconditions.checkNotNull(mCameraProvider, "The camera provider should not be null");
+        Preconditions.checkNotNull(mPreview, "The preview use case should not be null.");
+        Preconditions.checkNotNull(mVideoCapture, "The video capture use case should not be null.");
+        mCameraProvider.unbindAll();
+        final CameraSelector newLensFacing;
+        switch (mCamera.getCameraInfo().getLensFacing()) {
+            case CameraSelector.LENS_FACING_BACK:
+                newLensFacing = CameraSelector.DEFAULT_FRONT_CAMERA;
+                break;
+            case CameraSelector.LENS_FACING_FRONT:
+                newLensFacing = CameraSelector.DEFAULT_BACK_CAMERA;
+                break;
+            default:
+                throw new IllegalStateException("Invalid camera lens facing.");
+        }
+        mCamera = mCameraProvider.bindToLifecycle(this, newLensFacing, mPreview, mVideoCapture);
+    }
+
+    @SuppressWarnings("FutureReturnValueIgnored")
+    private void startRecording() {
+        Preconditions.checkNotNull(mVideoCapture, "The video capture use case should not be null.");
+        final long durationMillis = Long.parseLong(mDurationText.getText().toString());
+        final long switchTimeMillis = Long.parseLong(mSwitchTimeText.getText().toString());
+        if (switchTimeMillis >= durationMillis) {
+            String msg = "The switch time should be less than the duration.";
+            Logger.d(TAG, msg);
+            Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
+            return;
+        }
+        mStartButton.setClickable(false);
+        mStartButton.setText(R.string.record_button_recording);
+        mDurationText.setClickable(false);
+        mSwitchTimeText.setClickable(false);
+
+        final PendingRecording pendingRecording;
+        if (DeviceQuirks.get(MediaStoreVideoCannotWrite.class) != null) {
+            // Use FileOutputOption for devices in MediaStoreVideoCannotWrite Quirk.
+            pendingRecording = mVideoCapture.getOutput().prepareRecording(
+                    this, generateFileOutputOptions(durationMillis));
+        } else {
+            // Use MediaStoreOutputOptions for public share media storage.
+            pendingRecording = mVideoCapture.getOutput().prepareRecording(
+                    this, generateMediaStoreOutputOptions(durationMillis));
+        }
+        pendingRecording
+                .asPersistentRecording() // Perform the recording as a persistent recording.
+                .start(ContextCompat.getMainExecutor(this),
+                        videoRecordEvent -> {
+                            if (videoRecordEvent instanceof VideoRecordEvent.Finalize) {
+                                mStartButton.setClickable(true);
+                                mStartButton.setText(R.string.record_button_idling);
+                                mDurationText.setClickable(true);
+                                mSwitchTimeText.setClickable(true);
+                            }
+                        });
+
+        // Switch camera after the specified time.
+        CameraXExecutors.mainThreadExecutor().schedule(this::switchCamera, switchTimeMillis,
+                TimeUnit.MILLISECONDS);
+    }
+
+    @NonNull
+    private FileOutputOptions generateFileOutputOptions(@IntRange(from = 0) long durationMillis) {
+        String videoFileName = "video_" + System.currentTimeMillis() + ".mp4";
+        File videoFolder = Environment.getExternalStoragePublicDirectory(
+                Environment.DIRECTORY_MOVIES);
+        if (!videoFolder.exists() && !videoFolder.mkdirs()) {
+            Logger.e(TAG, "Failed to create directory: " + videoFolder);
+        }
+        return new FileOutputOptions.Builder(
+                new File(videoFolder, videoFileName)).setDurationLimitMillis(
+                durationMillis).build();
+    }
+
+    @NonNull
+    private MediaStoreOutputOptions generateMediaStoreOutputOptions(
+            @IntRange(from = 0) long durationMillis) {
+        String videoFileName = "video_" + System.currentTimeMillis();
+        ContentValues contentValues = new ContentValues();
+        contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4");
+        contentValues.put(MediaStore.Video.Media.TITLE, videoFileName);
+        contentValues.put(MediaStore.Video.Media.DISPLAY_NAME, videoFileName);
+        contentValues.put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis() / 1000);
+        contentValues.put(MediaStore.Video.Media.DATE_TAKEN, System.currentTimeMillis());
+        return new MediaStoreOutputOptions.Builder(getContentResolver(),
+                MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
+                .setContentValues(contentValues)
+                .setDurationLimitMillis(durationMillis)
+                .build();
+    }
+
+    private boolean allPermissionsGranted() {
+        for (String permission : REQUIRED_PERMISSIONS) {
+            if (ContextCompat.checkSelfPermission(this, permission)
+                    != PackageManager.PERMISSION_GRANTED) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode,
+            @NonNull String[] permissions,
+            @NonNull int[] grantResults) {
+        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+        if (requestCode == REQUEST_CODE_PERMISSIONS) {
+            if (allPermissionsGranted()) {
+                prepareCamera();
+            } else {
+                Toast.makeText(this, getString(R.string.permission_warning),
+                        Toast.LENGTH_SHORT).show();
+                this.finish();
+            }
+        }
+    }
+}
diff --git a/camera/integration-tests/coretestapp/src/main/res/layout/activity_video_camera_switching.xml b/camera/integration-tests/coretestapp/src/main/res/layout/activity_video_camera_switching.xml
new file mode 100644
index 0000000..cfc8fe9
--- /dev/null
+++ b/camera/integration-tests/coretestapp/src/main/res/layout/activity_video_camera_switching.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="vertical">
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal">
+
+                <LinearLayout
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:orientation="vertical">
+
+                    <LinearLayout
+                        android:layout_width="match_parent"
+                        android:layout_height="match_parent"
+                        android:layout_weight="1"
+                        android:orientation="horizontal">
+
+                        <TextView
+                            android:id="@+id/duration_label"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_weight="1"
+                            android:text="@string/duration_label" />
+
+                        <EditText
+                            android:id="@+id/duration"
+                            android:layout_width="wrap_content"
+                            android:layout_height="50dp"
+                            android:layout_weight="1"
+                            android:ems="10"
+                            android:hint="@string/duration_label"
+                            android:inputType="number" />
+                    </LinearLayout>
+
+                    <LinearLayout
+                        android:layout_width="match_parent"
+                        android:layout_height="match_parent"
+                        android:layout_weight="1"
+                        android:orientation="horizontal">
+
+                        <TextView
+                            android:id="@+id/switch_time_label"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_weight="1"
+                            android:text="@string/switch_time_label" />
+
+                        <EditText
+                            android:id="@+id/switch_time"
+                            android:layout_width="wrap_content"
+                            android:layout_height="50dp"
+                            android:layout_weight="1"
+                            android:ems="10"
+                            android:hint="@string/switch_time_label"
+                            android:inputType="number" />
+                    </LinearLayout>
+
+                </LinearLayout>
+
+                <Button
+                    android:id="@+id/start_recording"
+                    android:layout_width="wrap_content"
+                    android:layout_height="match_parent"
+                    android:layout_weight="1"
+                    android:text="@string/record_button_idling" />
+
+            </LinearLayout>
+
+            <FrameLayout
+                android:layout_width="match_parent"
+                android:layout_height="match_parent">
+
+                <androidx.camera.view.PreviewView
+                    android:id="@+id/camera_preview"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent" />
+            </FrameLayout>
+
+        </LinearLayout>
+
+    </FrameLayout>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/camera/integration-tests/coretestapp/src/main/res/values/strings.xml b/camera/integration-tests/coretestapp/src/main/res/values/strings.xml
index d54e0d1..b4fd308 100644
--- a/camera/integration-tests/coretestapp/src/main/res/values/strings.xml
+++ b/camera/integration-tests/coretestapp/src/main/res/values/strings.xml
@@ -25,4 +25,8 @@
     <string name="is_front_primary">IsFrontPrimary</string>
     <string name="permission_warning">Permissions not granted by the user.</string>
     <string name="concurrent_not_supported_warning">Concurrent camera not supported</string>
+    <string name="record_button_idling">Start</string>
+    <string name="record_button_recording">Recording</string>
+    <string name="duration_label">Duration (ms)</string>
+    <string name="switch_time_label">Switch Time (ms)</string>
 </resources>
\ No newline at end of file
diff --git a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/MainActivity.java b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/MainActivity.java
index 6e40f89..1328c94 100644
--- a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/MainActivity.java
+++ b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/MainActivity.java
@@ -18,6 +18,7 @@
 
 import android.Manifest;
 import android.annotation.SuppressLint;
+import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.os.Build;
 import android.os.Bundle;
@@ -44,10 +45,17 @@
 public class MainActivity extends AppCompatActivity {
     private static final String TAG = "MainActivity";
 
-    // Possible values for this intent key (case-insensitive): "PreviewView", "ComposeUi".
+    // Possible values for this intent key (case-insensitive): "Portrait", "Landscape".
+    private static final String INTENT_SCREEN_ORIENTATION = "orientation";
+    private static final String SCREEN_ORIENTATION_PORTRAIT = "Portrait";
+    private static final String SCREEN_ORIENTATION_LANDSCAPE = "Landscape";
+
+    // Possible values for this intent key (case-insensitive): "PreviewView", "ComposeUi",
+    // "StreamSharing".
     private static final String INTENT_FRAGMENT_TYPE = "fragment_type";
     private static final String PREVIEW_VIEW_FRAGMENT = "PreviewView";
     private static final String COMPOSE_UI_FRAGMENT = "ComposeUi";
+    private static final String STREAM_SHARING_FRAGMENT = "StreamSharing";
 
     private static final String[] REQUIRED_PERMISSIONS;
     static {
@@ -103,12 +111,8 @@
         // Get extra option for checking whether it needs to be implemented with PreviewView
         Bundle bundle = getIntent().getExtras();
         if (bundle != null) {
-            final String viewTypeString = bundle.getString(INTENT_FRAGMENT_TYPE);
-            if (PREVIEW_VIEW_FRAGMENT.equalsIgnoreCase(viewTypeString)) {
-                mFragmentType = FragmentType.PREVIEW_VIEW;
-            } else if (COMPOSE_UI_FRAGMENT.equalsIgnoreCase(viewTypeString)) {
-                mFragmentType = FragmentType.COMPOSE_UI;
-            }
+            parseScreenOrientationAndSetValueIfNeed(bundle);
+            parseFragmentType(bundle);
             // Update the app UI according to the e2e test case.
             String testItem = bundle.getString(INTENT_EXTRA_E2E_TEST_CASE);
             if (testItem != null) {
@@ -193,6 +197,9 @@
             case R.id.effects:
                 mFragmentType = FragmentType.EFFECTS;
                 break;
+            case R.id.stream_sharing:
+                mFragmentType = FragmentType.STREAM_SHARING;
+                break;
         }
         startFragment();
         return true;
@@ -208,6 +215,26 @@
         return true;
     }
 
+    private void parseScreenOrientationAndSetValueIfNeed(@NonNull Bundle bundle) {
+        final String orientationString = bundle.getString(INTENT_SCREEN_ORIENTATION);
+        if (SCREEN_ORIENTATION_PORTRAIT.equalsIgnoreCase(orientationString)) {
+            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+        } else if (SCREEN_ORIENTATION_LANDSCAPE.equalsIgnoreCase(orientationString)) {
+            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+        }
+    }
+
+    private void parseFragmentType(@NonNull Bundle bundle) {
+        final String viewTypeString = bundle.getString(INTENT_FRAGMENT_TYPE);
+        if (PREVIEW_VIEW_FRAGMENT.equalsIgnoreCase(viewTypeString)) {
+            mFragmentType = FragmentType.PREVIEW_VIEW;
+        } else if (COMPOSE_UI_FRAGMENT.equalsIgnoreCase(viewTypeString)) {
+            mFragmentType = FragmentType.COMPOSE_UI;
+        } else if (STREAM_SHARING_FRAGMENT.equalsIgnoreCase(viewTypeString)) {
+            mFragmentType = FragmentType.STREAM_SHARING;
+        }
+    }
+
     private void startFragment() {
         switch (mFragmentType) {
             case PREVIEW_VIEW:
@@ -228,6 +255,9 @@
             case EFFECTS:
                 startFragment(R.string.effects, new EffectsFragment());
                 break;
+            case STREAM_SHARING:
+                startFragment(R.string.stream_sharing, new StreamSharingFragment());
+                break;
         }
     }
 
@@ -249,6 +279,6 @@
     }
 
     private enum FragmentType {
-        PREVIEW_VIEW, CAMERA_CONTROLLER, TRANSFORM, COMPOSE_UI, MLKIT, EFFECTS
+        PREVIEW_VIEW, CAMERA_CONTROLLER, TRANSFORM, COMPOSE_UI, MLKIT, EFFECTS, STREAM_SHARING
     }
 }
diff --git a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/StreamSharingFragment.kt b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/StreamSharingFragment.kt
new file mode 100644
index 0000000..26564bb
--- /dev/null
+++ b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/StreamSharingFragment.kt
@@ -0,0 +1,267 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.integration.view
+
+import android.annotation.SuppressLint
+import android.content.ContentResolver
+import android.content.ContentValues
+import android.content.Context
+import android.os.Bundle
+import android.os.Environment
+import android.provider.MediaStore
+import android.view.LayoutInflater
+import android.view.View
+import android.view.View.GONE
+import android.view.View.VISIBLE
+import android.view.ViewGroup
+import android.widget.Button
+import android.widget.TextView
+import androidx.camera.core.Camera
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.ImageAnalysis
+import androidx.camera.core.ImageCapture
+import androidx.camera.core.Logger
+import androidx.camera.core.Preview
+import androidx.camera.core.UseCase
+import androidx.camera.core.impl.utils.executor.CameraXExecutors
+import androidx.camera.lifecycle.ProcessCameraProvider
+import androidx.camera.video.FileOutputOptions
+import androidx.camera.video.MediaStoreOutputOptions
+import androidx.camera.video.PendingRecording
+import androidx.camera.video.Recorder
+import androidx.camera.video.Recording
+import androidx.camera.video.VideoCapture
+import androidx.camera.video.VideoRecordEvent
+import androidx.camera.video.internal.compat.quirk.DeviceQuirks
+import androidx.camera.video.internal.compat.quirk.MediaStoreVideoCannotWrite
+import androidx.camera.view.PreviewView
+import androidx.core.content.ContextCompat
+import androidx.core.util.Consumer
+import androidx.fragment.app.Fragment
+import java.io.File
+
+private const val TAG = "StreamSharingFragment"
+
+class StreamSharingFragment : Fragment() {
+
+    private lateinit var previewView: PreviewView
+    private lateinit var recordButton: Button
+    private lateinit var streamSharingStateText: TextView
+    private lateinit var useCases: Array<UseCase>
+    private var camera: Camera? = null
+    private var activeRecording: Recording? = null
+    private var isUseCasesBound: Boolean = false
+
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+        val view = inflater.inflate(R.layout.fragment_stream_sharing, container, false)
+        previewView = view.findViewById(R.id.preview_view)
+        streamSharingStateText = view.findViewById(R.id.stream_sharing_state)
+        recordButton = view.findViewById(R.id.record_button)
+        recordButton.setOnClickListener {
+            if (activeRecording == null) startRecording() else stopRecording()
+        }
+
+        startCamera()
+        return view
+    }
+
+    private fun startCamera() {
+        val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext())
+        cameraProviderFuture.addListener({
+            bindUseCases(cameraProviderFuture.get())
+        }, ContextCompat.getMainExecutor(requireContext()))
+    }
+
+    private fun bindUseCases(cameraProvider: ProcessCameraProvider) {
+        displayStreamSharingState(GONE)
+        enableRecording(false)
+        cameraProvider.unbindAll()
+        useCases = arrayOf(
+            createPreview(),
+            createImageCapture(),
+            createImageAnalysis(),
+            createVideoCapture()
+        )
+        isUseCasesBound = try {
+            camera = cameraProvider.bindToLifecycle(this, getCameraSelector(), *useCases)
+            enableRecording(true)
+            true
+        } catch (exception: Exception) {
+            Logger.e(TAG, "Failed to bind use cases.", exception)
+            false
+        }
+        if (isStreamSharingEnabled()) {
+            displayStreamSharingState(VISIBLE)
+        }
+    }
+
+    private fun createPreview(): Preview {
+        return Preview.Builder().build().apply {
+            setSurfaceProvider(previewView.surfaceProvider)
+        }
+    }
+
+    private fun createImageCapture(): ImageCapture {
+        return ImageCapture.Builder().build()
+    }
+
+    private fun createImageAnalysis(): ImageAnalysis {
+        return ImageAnalysis.Builder().build()
+    }
+
+    private fun createVideoCapture(): VideoCapture<Recorder> {
+        val recorder = Recorder.Builder().build()
+        return VideoCapture.withOutput(recorder)
+    }
+
+    @Suppress("UNCHECKED_CAST")
+    private fun getVideoCapture(): VideoCapture<Recorder>? {
+        return findUseCase(VideoCapture::class.java) as VideoCapture<Recorder>?
+    }
+
+    private fun <T : UseCase?> findUseCase(useCaseSubclass: Class<T>): T? {
+        for (useCase in useCases) {
+            if (useCaseSubclass.isInstance(useCase)) {
+                return useCaseSubclass.cast(useCase)
+            }
+        }
+        return null
+    }
+
+    private fun getCameraSelector(): CameraSelector {
+        val cameraDirection: String? = requireActivity().intent.extras?.getString(
+            MainActivity.INTENT_EXTRA_CAMERA_DIRECTION,
+            MainActivity.CAMERA_DIRECTION_BACK
+        )
+
+        return when (cameraDirection) {
+            MainActivity.CAMERA_DIRECTION_BACK -> CameraSelector.DEFAULT_BACK_CAMERA
+            MainActivity.CAMERA_DIRECTION_FRONT -> CameraSelector.DEFAULT_FRONT_CAMERA
+            else -> CameraSelector.DEFAULT_BACK_CAMERA
+        }
+    }
+
+    private fun isStreamSharingEnabled(): Boolean {
+        val isCombinationSupported =
+            camera != null && camera!!.isUseCasesCombinationSupported(*useCases)
+        return !isCombinationSupported && isUseCasesBound
+    }
+
+    private fun displayStreamSharingState(visibility: Int) {
+        streamSharingStateText.visibility = visibility
+    }
+
+    private fun enableRecording(enabled: Boolean) {
+        recordButton.isEnabled = enabled
+    }
+
+    @SuppressLint("MissingPermission")
+    private fun startRecording() {
+        recordButton.text = getString(R.string.btn_video_stop_recording)
+        activeRecording = getVideoCapture()!!.let {
+            prepareRecording(requireContext(), it.output).withAudioEnabled().start(
+                CameraXExecutors.directExecutor(),
+                generateVideoRecordEventListener()
+            )
+        }
+    }
+
+    private fun stopRecording() {
+        recordButton.text = getString(R.string.btn_video_record)
+        activeRecording!!.stop()
+        activeRecording = null
+    }
+
+    private fun prepareRecording(context: Context, recorder: Recorder): PendingRecording {
+        return if (canDeviceWriteToMediaStore()) {
+            recorder.prepareRecording(
+                context,
+                generateVideoMediaStoreOptions(context.contentResolver)
+            )
+        } else {
+            recorder.prepareRecording(context, generateVideoFileOutputOptions())
+        }
+    }
+
+    private fun canDeviceWriteToMediaStore(): Boolean {
+        return DeviceQuirks.get(MediaStoreVideoCannotWrite::class.java) == null
+    }
+
+    private fun generateVideoFileOutputOptions(): FileOutputOptions {
+        val videoFileName = "${generateVideoFileName()}.mp4"
+        val videoFolder = Environment.getExternalStoragePublicDirectory(
+            Environment.DIRECTORY_MOVIES
+        )
+        if (!videoFolder.exists() && !videoFolder.mkdirs()) {
+            Logger.e(TAG, "Failed to create directory: $videoFolder")
+        }
+        return FileOutputOptions.Builder(File(videoFolder, videoFileName)).build()
+    }
+
+    private fun generateVideoMediaStoreOptions(
+        contentResolver: ContentResolver
+    ): MediaStoreOutputOptions {
+        val contentValues = generateVideoContentValues(generateVideoFileName())
+
+        return MediaStoreOutputOptions.Builder(
+            contentResolver,
+            MediaStore.Video.Media.EXTERNAL_CONTENT_URI
+        ).setContentValues(contentValues).build()
+    }
+
+    private fun generateVideoFileName(): String {
+        return "video_" + System.currentTimeMillis()
+    }
+
+    private fun generateVideoContentValues(fileName: String): ContentValues {
+        val res = ContentValues()
+        res.put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4")
+        res.put(MediaStore.Video.Media.TITLE, fileName)
+        res.put(MediaStore.Video.Media.DISPLAY_NAME, fileName)
+        res.put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis() / 1000)
+        res.put(MediaStore.Video.Media.DATE_TAKEN, System.currentTimeMillis())
+
+        return res
+    }
+
+    private fun generateVideoRecordEventListener(): Consumer<VideoRecordEvent> {
+        return Consumer<VideoRecordEvent> { event ->
+            if (event is VideoRecordEvent.Finalize) {
+                val uri = event.outputResults.outputUri
+                when (event.error) {
+                    VideoRecordEvent.Finalize.ERROR_NONE,
+                    VideoRecordEvent.Finalize.ERROR_FILE_SIZE_LIMIT_REACHED,
+                    VideoRecordEvent.Finalize.ERROR_DURATION_LIMIT_REACHED,
+                    VideoRecordEvent.Finalize.ERROR_INSUFFICIENT_STORAGE,
+                    VideoRecordEvent.Finalize.ERROR_SOURCE_INACTIVE -> Logger.d(
+                        TAG,
+                        "Video saved to: $uri"
+                    )
+
+                    else -> Logger.e(
+                        TAG,
+                        "Failed to save video: uri $uri with code (${event.error})"
+                    )
+                }
+            }
+        }
+    }
+}
diff --git a/camera/integration-tests/viewtestapp/src/main/res/layout/fragment_stream_sharing.xml b/camera/integration-tests/viewtestapp/src/main/res/layout/fragment_stream_sharing.xml
new file mode 100644
index 0000000..ee31a58
--- /dev/null
+++ b/camera/integration-tests/viewtestapp/src/main/res/layout/fragment_stream_sharing.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/layout_camera"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <FrameLayout
+        android:id="@+id/container"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1">
+
+        <androidx.camera.view.PreviewView
+            android:id="@+id/preview_view"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent" />
+    </FrameLayout>
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content">
+
+        <LinearLayout
+            android:id="@+id/controller"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+
+            <Button
+                android:id="@+id/record_button"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:enabled="false"
+                android:text="@string/btn_video_record" />
+
+            <TextView
+                android:id="@+id/stream_sharing_state"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:gravity="center"
+                android:padding="8dp"
+                android:text="@string/text_stream_sharing_enabled"
+                android:textAlignment="center"
+                android:visibility="gone" />
+        </LinearLayout>
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/camera/integration-tests/viewtestapp/src/main/res/menu/actionbar_menu.xml b/camera/integration-tests/viewtestapp/src/main/res/menu/actionbar_menu.xml
index d16f41d..5bc8d7a 100644
--- a/camera/integration-tests/viewtestapp/src/main/res/menu/actionbar_menu.xml
+++ b/camera/integration-tests/viewtestapp/src/main/res/menu/actionbar_menu.xml
@@ -37,6 +37,10 @@
         android:title="@string/effects"
         app:showAsAction="never" />
     <item
+        android:id="@+id/stream_sharing"
+        android:title="@string/stream_sharing"
+        app:showAsAction="never" />
+    <item
         android:id="@+id/mlkit"
         android:title="@string/mlkit"
         app:showAsAction="never" />
diff --git a/camera/integration-tests/viewtestapp/src/main/res/values/donottranslate-strings.xml b/camera/integration-tests/viewtestapp/src/main/res/values/donottranslate-strings.xml
index ededb27..936b8d2 100644
--- a/camera/integration-tests/viewtestapp/src/main/res/values/donottranslate-strings.xml
+++ b/camera/integration-tests/viewtestapp/src/main/res/values/donottranslate-strings.xml
@@ -17,6 +17,7 @@
 
     <string name="app_name">CameraX Views Demo</string>
     <string name="effects">Effects</string>
+    <string name="stream_sharing">StreamSharing</string>
     <string name="preview">Preview</string>
     <string name="preview_and_video">Preview &amp; Video</string>
     <string name="surface_effect">Surface effect</string>
@@ -62,5 +63,6 @@
     <string name="mirror_on">mirror on</string>
     <string name="mirror_off">mirror off</string>
     <string name="mlkit">MLKit</string>
+    <string name="text_stream_sharing_enabled">SteamSharing enabled</string>
 
 </resources>
diff --git a/car/app/app-automotive/build.gradle b/car/app/app-automotive/build.gradle
index 7361f0e..7c764d5 100644
--- a/car/app/app-automotive/build.gradle
+++ b/car/app/app-automotive/build.gradle
@@ -24,18 +24,14 @@
 dependencies {
     api(project(":car:app:app"))
     api(libs.guavaListenableFuture)
+    api("androidx.annotation:annotation-experimental:1.3.1")
     implementation(libs.guavaAndroid)
-    implementation "androidx.concurrent:concurrent-futures:1.1.0"
+    implementation("androidx.concurrent:concurrent-futures:1.1.0")
     implementation("androidx.fragment:fragment:1.3.0")
     implementation("androidx.lifecycle:lifecycle-common-java8:2.2.0")
     implementation("androidx.annotation:annotation:1.1.0")
     implementation("androidx.core:core:1.7.0")
     implementation(libs.autoValueAnnotations)
-    api("androidx.annotation:annotation-experimental:1.1.0")
-    // There is an implicit compile-only dep due to :annotation-experimental
-    // Build will complain without this manual declaration.
-    api(libs.kotlinStdlib)
-    implementation "androidx.annotation:annotation-experimental:1.3.0"
 
     annotationProcessor(libs.nullaway)
     annotationProcessor(libs.autoValue)
diff --git a/car/app/app/api/current.txt b/car/app/app/api/current.txt
index ec7d0e1..261afe98 100644
--- a/car/app/app/api/current.txt
+++ b/car/app/app/api/current.txt
@@ -850,7 +850,14 @@
 package androidx.car.app.mediaextensions {
 
   public final class MetadataExtras {
+    field public static final String KEY_CONTENT_FORMAT_DARK_MODE_LARGE_ICON_URI = "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_DARK_MODE_LARGE_ICON_URI";
+    field public static final String KEY_CONTENT_FORMAT_DARK_MODE_SMALL_ICON_URI = "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_DARK_MODE_SMALL_ICON_URI";
+    field public static final String KEY_CONTENT_FORMAT_LIGHT_MODE_LARGE_ICON_URI = "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_LIGHT_MODE_LARGE_ICON_URI";
+    field public static final String KEY_CONTENT_FORMAT_LIGHT_MODE_SMALL_ICON_URI = "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_LIGHT_MODE_SMALL_ICON_URI";
+    field public static final String KEY_CONTENT_FORMAT_TINTABLE_LARGE_ICON_URI = "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_TINTABLE_LARGE_ICON_URI";
+    field public static final String KEY_CONTENT_FORMAT_TINTABLE_SMALL_ICON_URI = "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_TINTABLE_SMALL_ICON_URI";
     field public static final String KEY_DESCRIPTION_LINK_MEDIA_ID = "androidx.car.app.mediaextensions.KEY_DESCRIPTION_LINK_MEDIA_ID";
+    field public static final String KEY_IMMERSIVE_AUDIO = "androidx.car.app.mediaextensions.KEY_IMMERSIVE_AUDIO";
     field public static final String KEY_SUBTITLE_LINK_MEDIA_ID = "androidx.car.app.mediaextensions.KEY_SUBTITLE_LINK_MEDIA_ID";
   }
 
@@ -867,7 +874,9 @@
 package androidx.car.app.messaging.model {
 
   @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public class CarMessage {
-    method public androidx.car.app.model.CarText getBody();
+    method public androidx.car.app.model.CarText? getBody();
+    method public String? getMultimediaMimeType();
+    method public android.net.Uri? getMultimediaUri();
     method public long getReceivedTimeEpochMillis();
     method public androidx.core.app.Person? getSender();
     method public boolean isRead();
@@ -876,7 +885,9 @@
   public static final class CarMessage.Builder {
     ctor public CarMessage.Builder();
     method public androidx.car.app.messaging.model.CarMessage build();
-    method public androidx.car.app.messaging.model.CarMessage.Builder setBody(androidx.car.app.model.CarText);
+    method public androidx.car.app.messaging.model.CarMessage.Builder setBody(androidx.car.app.model.CarText?);
+    method public androidx.car.app.messaging.model.CarMessage.Builder setMultimediaMimeType(String?);
+    method public androidx.car.app.messaging.model.CarMessage.Builder setMultimediaUri(android.net.Uri?);
     method public androidx.car.app.messaging.model.CarMessage.Builder setRead(boolean);
     method public androidx.car.app.messaging.model.CarMessage.Builder setReceivedTimeEpochMillis(long);
     method public androidx.car.app.messaging.model.CarMessage.Builder setSender(androidx.core.app.Person?);
@@ -893,6 +904,7 @@
   }
 
   @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public class ConversationItem implements androidx.car.app.model.Item {
+    method public java.util.List<androidx.car.app.model.Action!> getActions();
     method public androidx.car.app.messaging.model.ConversationCallbackDelegate getConversationCallbackDelegate();
     method public androidx.car.app.model.CarIcon? getIcon();
     method public String getId();
@@ -905,6 +917,7 @@
   public static final class ConversationItem.Builder {
     ctor public ConversationItem.Builder();
     ctor public ConversationItem.Builder(androidx.car.app.messaging.model.ConversationItem);
+    method public androidx.car.app.messaging.model.ConversationItem.Builder addAction(androidx.car.app.model.Action);
     method public androidx.car.app.messaging.model.ConversationItem build();
     method public androidx.car.app.messaging.model.ConversationItem.Builder setConversationCallback(androidx.car.app.messaging.model.ConversationCallback);
     method public androidx.car.app.messaging.model.ConversationItem.Builder setGroupConversation(boolean);
@@ -931,12 +944,14 @@
     method public static String typeToString(int);
     field public static final androidx.car.app.model.Action APP_ICON;
     field public static final androidx.car.app.model.Action BACK;
+    field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public static final androidx.car.app.model.Action COMPOSE_MESSAGE;
     field @androidx.car.app.annotations.RequiresCarApi(5) public static final int FLAG_DEFAULT = 4; // 0x4
     field @androidx.car.app.annotations.RequiresCarApi(5) public static final int FLAG_IS_PERSISTENT = 2; // 0x2
     field @androidx.car.app.annotations.RequiresCarApi(4) public static final int FLAG_PRIMARY = 1; // 0x1
     field public static final androidx.car.app.model.Action PAN;
     field public static final int TYPE_APP_ICON = 65538; // 0x10002
     field public static final int TYPE_BACK = 65539; // 0x10003
+    field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public static final int TYPE_COMPOSE_MESSAGE = 65541; // 0x10005
     field public static final int TYPE_CUSTOM = 1; // 0x1
     field public static final int TYPE_PAN = 65540; // 0x10004
   }
diff --git a/car/app/app/api/restricted_current.txt b/car/app/app/api/restricted_current.txt
index ec7d0e1..261afe98 100644
--- a/car/app/app/api/restricted_current.txt
+++ b/car/app/app/api/restricted_current.txt
@@ -850,7 +850,14 @@
 package androidx.car.app.mediaextensions {
 
   public final class MetadataExtras {
+    field public static final String KEY_CONTENT_FORMAT_DARK_MODE_LARGE_ICON_URI = "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_DARK_MODE_LARGE_ICON_URI";
+    field public static final String KEY_CONTENT_FORMAT_DARK_MODE_SMALL_ICON_URI = "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_DARK_MODE_SMALL_ICON_URI";
+    field public static final String KEY_CONTENT_FORMAT_LIGHT_MODE_LARGE_ICON_URI = "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_LIGHT_MODE_LARGE_ICON_URI";
+    field public static final String KEY_CONTENT_FORMAT_LIGHT_MODE_SMALL_ICON_URI = "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_LIGHT_MODE_SMALL_ICON_URI";
+    field public static final String KEY_CONTENT_FORMAT_TINTABLE_LARGE_ICON_URI = "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_TINTABLE_LARGE_ICON_URI";
+    field public static final String KEY_CONTENT_FORMAT_TINTABLE_SMALL_ICON_URI = "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_TINTABLE_SMALL_ICON_URI";
     field public static final String KEY_DESCRIPTION_LINK_MEDIA_ID = "androidx.car.app.mediaextensions.KEY_DESCRIPTION_LINK_MEDIA_ID";
+    field public static final String KEY_IMMERSIVE_AUDIO = "androidx.car.app.mediaextensions.KEY_IMMERSIVE_AUDIO";
     field public static final String KEY_SUBTITLE_LINK_MEDIA_ID = "androidx.car.app.mediaextensions.KEY_SUBTITLE_LINK_MEDIA_ID";
   }
 
@@ -867,7 +874,9 @@
 package androidx.car.app.messaging.model {
 
   @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public class CarMessage {
-    method public androidx.car.app.model.CarText getBody();
+    method public androidx.car.app.model.CarText? getBody();
+    method public String? getMultimediaMimeType();
+    method public android.net.Uri? getMultimediaUri();
     method public long getReceivedTimeEpochMillis();
     method public androidx.core.app.Person? getSender();
     method public boolean isRead();
@@ -876,7 +885,9 @@
   public static final class CarMessage.Builder {
     ctor public CarMessage.Builder();
     method public androidx.car.app.messaging.model.CarMessage build();
-    method public androidx.car.app.messaging.model.CarMessage.Builder setBody(androidx.car.app.model.CarText);
+    method public androidx.car.app.messaging.model.CarMessage.Builder setBody(androidx.car.app.model.CarText?);
+    method public androidx.car.app.messaging.model.CarMessage.Builder setMultimediaMimeType(String?);
+    method public androidx.car.app.messaging.model.CarMessage.Builder setMultimediaUri(android.net.Uri?);
     method public androidx.car.app.messaging.model.CarMessage.Builder setRead(boolean);
     method public androidx.car.app.messaging.model.CarMessage.Builder setReceivedTimeEpochMillis(long);
     method public androidx.car.app.messaging.model.CarMessage.Builder setSender(androidx.core.app.Person?);
@@ -893,6 +904,7 @@
   }
 
   @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public class ConversationItem implements androidx.car.app.model.Item {
+    method public java.util.List<androidx.car.app.model.Action!> getActions();
     method public androidx.car.app.messaging.model.ConversationCallbackDelegate getConversationCallbackDelegate();
     method public androidx.car.app.model.CarIcon? getIcon();
     method public String getId();
@@ -905,6 +917,7 @@
   public static final class ConversationItem.Builder {
     ctor public ConversationItem.Builder();
     ctor public ConversationItem.Builder(androidx.car.app.messaging.model.ConversationItem);
+    method public androidx.car.app.messaging.model.ConversationItem.Builder addAction(androidx.car.app.model.Action);
     method public androidx.car.app.messaging.model.ConversationItem build();
     method public androidx.car.app.messaging.model.ConversationItem.Builder setConversationCallback(androidx.car.app.messaging.model.ConversationCallback);
     method public androidx.car.app.messaging.model.ConversationItem.Builder setGroupConversation(boolean);
@@ -931,12 +944,14 @@
     method public static String typeToString(int);
     field public static final androidx.car.app.model.Action APP_ICON;
     field public static final androidx.car.app.model.Action BACK;
+    field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public static final androidx.car.app.model.Action COMPOSE_MESSAGE;
     field @androidx.car.app.annotations.RequiresCarApi(5) public static final int FLAG_DEFAULT = 4; // 0x4
     field @androidx.car.app.annotations.RequiresCarApi(5) public static final int FLAG_IS_PERSISTENT = 2; // 0x2
     field @androidx.car.app.annotations.RequiresCarApi(4) public static final int FLAG_PRIMARY = 1; // 0x1
     field public static final androidx.car.app.model.Action PAN;
     field public static final int TYPE_APP_ICON = 65538; // 0x10002
     field public static final int TYPE_BACK = 65539; // 0x10003
+    field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public static final int TYPE_COMPOSE_MESSAGE = 65541; // 0x10005
     field public static final int TYPE_CUSTOM = 1; // 0x1
     field public static final int TYPE_PAN = 65540; // 0x10004
   }
diff --git a/car/app/app/src/main/java/androidx/car/app/mediaextensions/MetadataExtras.java b/car/app/app/src/main/java/androidx/car/app/mediaextensions/MetadataExtras.java
index 1a46be5..497ebf4 100644
--- a/car/app/app/src/main/java/androidx/car/app/mediaextensions/MetadataExtras.java
+++ b/car/app/app/src/main/java/androidx/car/app/mediaextensions/MetadataExtras.java
@@ -93,4 +93,66 @@
      */
     public static final String KEY_DESCRIPTION_LINK_MEDIA_ID =
             "androidx.car.app.mediaextensions.KEY_DESCRIPTION_LINK_MEDIA_ID";
+
+    /**
+     * {@link Bundle} key used in the extras of a media item to indicate an immersive audio
+     * experience. Car OEMs should carefully consider which audio effects should be enabled for
+     * such content.
+     *
+     * <p>TYPE: long - to enable, use value
+     * {@link androidx.media.utils.MediaConstants#METADATA_VALUE_ATTRIBUTE_PRESENT}</p>
+     */
+    public static final String KEY_IMMERSIVE_AUDIO =
+            "androidx.car.app.mediaextensions.KEY_IMMERSIVE_AUDIO";
+
+    /**
+     * {@link Bundle} key used in the extras of a media item to indicate a tintable vector drawable
+     * representing its content format. This drawable must be rendered in large views showing
+     * information about the currently playing media item, in an area roughly equivalent to 15
+     * characters of subtitle.
+     *
+     * <p>TYPE: String - a uri pointing to local content (ie not on the web) that can be parsed
+     * into a android.graphics.drawable.Drawable</p>
+     */
+    public static final String KEY_CONTENT_FORMAT_TINTABLE_LARGE_ICON_URI =
+            "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_TINTABLE_LARGE_ICON_URI";
+
+    /**
+     * {@link Bundle} key used in the extras of a media item to indicate a tintable vector drawable
+     * representing its content format. This drawable may be rendered in smaller views showing
+     * information about a media item, in an area roughly equivalent to 2 characters of subtitle.
+     *
+     * <p>TYPE: String - a uri pointing to local content (ie not on the web) that can be parsed
+     * into a android.graphics.drawable.Drawable</p>
+     */
+    public static final String KEY_CONTENT_FORMAT_TINTABLE_SMALL_ICON_URI =
+            "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_TINTABLE_SMALL_ICON_URI";
+
+    /**
+     * Like {@link #KEY_CONTENT_FORMAT_TINTABLE_LARGE_ICON_URI} but indicates a non tintable
+     * vector drawable to use with dark backgrounds.
+     */
+    public static final String KEY_CONTENT_FORMAT_DARK_MODE_LARGE_ICON_URI =
+            "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_DARK_MODE_LARGE_ICON_URI";
+
+    /**
+     * Like {@link #KEY_CONTENT_FORMAT_TINTABLE_LARGE_ICON_URI} but indicates a non tintable
+     * vector drawable to use with light backgrounds.
+     */
+    public static final String KEY_CONTENT_FORMAT_LIGHT_MODE_LARGE_ICON_URI =
+            "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_LIGHT_MODE_LARGE_ICON_URI";
+
+    /**
+     * Like {@link #KEY_CONTENT_FORMAT_TINTABLE_SMALL_ICON_URI} but indicates a non tintable
+     * vector drawable to use with dark backgrounds.
+     */
+    public static final String KEY_CONTENT_FORMAT_DARK_MODE_SMALL_ICON_URI =
+            "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_DARK_MODE_SMALL_ICON_URI";
+
+    /**
+     * Like {@link #KEY_CONTENT_FORMAT_TINTABLE_SMALL_ICON_URI} but indicates a non tintable
+     * vector drawable to use with light backgrounds.
+     */
+    public static final String KEY_CONTENT_FORMAT_LIGHT_MODE_SMALL_ICON_URI =
+            "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_LIGHT_MODE_SMALL_ICON_URI";
 }
diff --git a/car/app/app/src/main/java/androidx/car/app/messaging/model/CarMessage.java b/car/app/app/src/main/java/androidx/car/app/messaging/model/CarMessage.java
index e81089d..303ce3d 100644
--- a/car/app/app/src/main/java/androidx/car/app/messaging/model/CarMessage.java
+++ b/car/app/app/src/main/java/androidx/car/app/messaging/model/CarMessage.java
@@ -18,8 +18,7 @@
 
 import static androidx.car.app.messaging.model.ConversationItem.validateSender;
 
-import static java.util.Objects.requireNonNull;
-
+import android.net.Uri;
 import android.os.Bundle;
 
 import androidx.annotation.NonNull;
@@ -41,8 +40,12 @@
 public class CarMessage {
     @Nullable
     private final Bundle mSender;
-    @NonNull
+    @Nullable
     private final CarText mBody;
+    @Nullable
+    private final String mMultimediaMimeType;
+    @Nullable
+    private final Uri mMultimediaUri;
     private final long mReceivedTimeEpochMillis;
     private final boolean mIsRead;
 
@@ -75,7 +78,9 @@
 
     CarMessage(@NonNull Builder builder) {
         this.mSender = builder.mSender == null ? null : validateSender(builder.mSender).toBundle();
-        this.mBody = requireNonNull(builder.mBody);
+        this.mBody = builder.mBody;
+        this.mMultimediaMimeType = builder.mMultimediaMimeType;
+        this.mMultimediaUri = builder.mMultimediaUri;
         this.mReceivedTimeEpochMillis = builder.mReceivedTimeEpochMillis;
         this.mIsRead = builder.mIsRead;
     }
@@ -83,7 +88,9 @@
     /** Default constructor for serialization. */
     private CarMessage() {
         this.mSender = null;
-        this.mBody = new CarText.Builder("").build();
+        this.mBody = null;
+        this.mMultimediaMimeType = null;
+        this.mMultimediaUri = null;
         this.mReceivedTimeEpochMillis = 0;
         this.mIsRead = false;
     }
@@ -100,12 +107,57 @@
         return mSender == null ? null : Person.fromBundle(mSender);
     }
 
-    /** Returns a {@link CarText} representing the message body */
-    @NonNull
+    /**
+     * Returns a {@link CarText} representing the message body
+     *
+     * <p> Messages must have one or both of the following:
+     * <ul>
+     *     <li> A message body (text)
+     *     <li> A MIME type + URI (image, audio, etc.)
+     * </ul>
+     *
+     * @see #getMultimediaMimeType()
+     * @see #getMultimediaUri()
+     */
+    @Nullable
     public CarText getBody() {
         return mBody;
     }
 
+    /**
+     * Returns a {@link String} representing the MIME type of a multimedia message
+     *
+     * <p> Messages must have one or both of the following:
+     * <ul>
+     *     <li> A message body (text)
+     *     <li> A MIME type + URI (image, audio, etc.)
+     * </ul>
+     *
+     * @see #getBody()
+     * @see #getMultimediaUri()
+     */
+    @Nullable
+    public String getMultimediaMimeType() {
+        return mMultimediaMimeType;
+    }
+
+    /**
+     * Returns a {@link Uri} pointing to the contents of a multimedia message.
+     *
+     * <p> Messages must have one or both of the following:
+     * <ul>
+     *     <li> A message body (text)
+     *     <li> A MIME type + URI (image, audio, etc.)
+     * </ul>
+     *
+     * @see #getBody()
+     * @see #getMultimediaMimeType()
+     */
+    @Nullable
+    public Uri getMultimediaUri() {
+        return mMultimediaUri;
+    }
+
     /** Returns a {@code long} representing the message timestamp (in epoch millis) */
     public long getReceivedTimeEpochMillis() {
         return mReceivedTimeEpochMillis;
@@ -122,6 +174,10 @@
         Person mSender;
         @Nullable
         CarText mBody;
+        @Nullable
+        String mMultimediaMimeType;
+        @Nullable
+        Uri mMultimediaUri;
         long mReceivedTimeEpochMillis;
         boolean mIsRead;
 
@@ -137,12 +193,57 @@
             return this;
         }
 
-        /** Sets a {@link CarText} representing the message body */
-        public @NonNull Builder setBody(@NonNull CarText body) {
+        /**
+         * Sets a {@link CarText} representing the message body
+         *
+         * <p> Messages must have one or both of the following:
+         * <ul>
+         *     <li> A message body (text)
+         *     <li> A MIME type + URI (image, audio, etc.)
+         * </ul>
+         *
+         * @see #setMultimediaMimeType(String)
+         * @see #setMultimediaUri(Uri)
+         */
+        public @NonNull Builder setBody(@Nullable CarText body) {
             mBody = body;
             return this;
         }
 
+        /**
+         * Sets a {@link String} representing the MIME type of a multimedia message
+         *
+         * <p> Messages must have one or both of the following:
+         * <ul>
+         *     <li> A message body (text)
+         *     <li> A MIME type + URI (image, audio, etc.)
+         * </ul>
+         *
+         * @see #setBody(CarText)
+         * @see #setMultimediaUri(Uri)
+         */
+        public @NonNull Builder setMultimediaMimeType(@Nullable String multimediaMimeType) {
+            this.mMultimediaMimeType = multimediaMimeType;
+            return this;
+        }
+
+        /**
+         * Sets a {@link Uri} pointing to the contents of a multimedia message.
+         *
+         * <p> Messages must have one or both of the following:
+         * <ul>
+         *     <li> A message body (text)
+         *     <li> A MIME type + URI (image, audio, etc.)
+         * </ul>
+         *
+         * @see #setBody(CarText)
+         * @see #setMultimediaMimeType(String)
+         */
+        public @NonNull Builder setMultimediaUri(@Nullable Uri multimediaUri) {
+            this.mMultimediaUri = multimediaUri;
+            return this;
+        }
+
         /** Sets a {@code long} representing the message timestamp (in epoch millis) */
         public @NonNull Builder setReceivedTimeEpochMillis(long receivedTimeEpochMillis) {
             mReceivedTimeEpochMillis = receivedTimeEpochMillis;
@@ -157,6 +258,20 @@
 
         /** Returns a new {@link CarMessage} instance defined by this builder */
         public @NonNull CarMessage build() {
+            if (mMultimediaMimeType == null ^ mMultimediaUri == null) {
+                throw new IllegalStateException("Incomplete multimedia data detected in "
+                        + "CarMessage. Please be sure to provide both MIME type and URI for "
+                        + "multimedia messages.");
+            }
+
+            // Conceptually, we're checking that body text and multimedia data (mime type or URI)
+            // are null.
+            // The compiler complains if I check both mime type and URI, due to previous validation.
+            if (mBody == null && mMultimediaMimeType == null) {
+                throw new IllegalStateException("Message must have content. Please provide body "
+                        + "text, multimedia data (URI + MIME type), or both.");
+            }
+
             return new CarMessage(this);
         }
     }
diff --git a/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationItem.java b/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationItem.java
index d83d5b0..1acc0f4 100644
--- a/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationItem.java
+++ b/car/app/app/src/main/java/androidx/car/app/messaging/model/ConversationItem.java
@@ -29,13 +29,16 @@
 import androidx.car.app.annotations.ExperimentalCarApi;
 import androidx.car.app.annotations.KeepFields;
 import androidx.car.app.annotations.RequiresCarApi;
+import androidx.car.app.model.Action;
 import androidx.car.app.model.CarIcon;
 import androidx.car.app.model.CarText;
 import androidx.car.app.model.Item;
+import androidx.car.app.model.constraints.ActionsConstraints;
 import androidx.car.app.utils.CollectionUtils;
 import androidx.core.app.Person;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 
@@ -58,6 +61,8 @@
     private final List<CarMessage> mMessages;
     @NonNull
     private final ConversationCallbackDelegate mConversationCallbackDelegate;
+    @NonNull
+    private final List<Action> mActions;
 
     @Override
     public int hashCode() {
@@ -67,7 +72,8 @@
                 mTitle,
                 mIcon,
                 mIsGroupConversation,
-                mMessages
+                mMessages,
+                mActions
         );
     }
 
@@ -89,6 +95,7 @@
                         .arePersonsEqual(getSelf(), otherConversationItem.getSelf())
                         && mIsGroupConversation == otherConversationItem.mIsGroupConversation
                         && Objects.equals(mMessages, otherConversationItem.mMessages)
+                        && Objects.equals(mActions, otherConversationItem.mActions)
                 ;
     }
 
@@ -101,6 +108,7 @@
         this.mMessages = requireNonNull(CollectionUtils.unmodifiableCopy(builder.mMessages));
         checkState(!mMessages.isEmpty(), "Message list cannot be empty.");
         this.mConversationCallbackDelegate = requireNonNull(builder.mConversationCallbackDelegate);
+        this.mActions = CollectionUtils.unmodifiableCopy(builder.mActions);
     }
 
     /** Default constructor for serialization. */
@@ -123,6 +131,7 @@
                         // Do nothing
                     }
                 });
+        mActions = Collections.emptyList();
     }
 
     /**
@@ -175,6 +184,16 @@
     }
 
     /**
+     * Returns the list of additional actions.
+     *
+     * @see ConversationItem.Builder#addAction(Action)
+     */
+    @NonNull
+    public List<Action> getActions() {
+        return mActions;
+    }
+
+    /**
      * Verifies that a given {@link Person} has the required fields to be a message sender. Returns
      * the input {@link Person} if valid, or throws an exception if invalid.
      *
@@ -202,6 +221,7 @@
         List<CarMessage> mMessages;
         @Nullable
         ConversationCallbackDelegate mConversationCallbackDelegate;
+        final List<Action> mActions;
 
         /**
          * Specifies a unique identifier for the conversation
@@ -278,6 +298,23 @@
             return this;
         }
 
+        /**
+         * Adds an additional action for the conversation.
+         *
+         * @throws NullPointerException     if {@code action} is {@code null}
+         * @throws IllegalArgumentException if {@code action} contains unsupported Action types,
+         *                                  exceeds the maximum number of allowed actions (1) or
+         *                                  does not contain a valid {@link CarIcon}.
+         */
+        @NonNull
+        public Builder addAction(@NonNull Action action) {
+            List<Action> mActionsCopy = new ArrayList<>(mActions);
+            mActionsCopy.add(requireNonNull(action));
+            ActionsConstraints.ACTIONS_CONSTRAINTS_CONVERSATION_ITEM.validateOrThrow(mActionsCopy);
+            mActions.add(action);
+            return this;
+        }
+
         /** Returns a new {@link ConversationItem} instance defined by this builder */
         @NonNull
         public ConversationItem build() {
@@ -286,6 +323,7 @@
 
         /** Returns an empty {@link Builder} instance. */
         public Builder() {
+            mActions = new ArrayList<>();
         }
 
         /** Returns a builder from the given {@link ConversationItem}. */
@@ -297,6 +335,7 @@
             this.mIsGroupConversation = other.isGroupConversation();
             this.mConversationCallbackDelegate = other.getConversationCallbackDelegate();
             this.mMessages = other.getMessages();
+            this.mActions = new ArrayList<>(other.getActions());
         }
     }
 }
diff --git a/car/app/app/src/main/java/androidx/car/app/model/Action.java b/car/app/app/src/main/java/androidx/car/app/model/Action.java
index 38d13b7..7396916 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/Action.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/Action.java
@@ -31,12 +31,14 @@
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.OptIn;
 import androidx.annotation.RestrictTo;
 import androidx.car.app.CarContext;
 import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.ExperimentalCarApi;
+import androidx.car.app.annotations.KeepFields;
 import androidx.car.app.annotations.RequiresCarApi;
 import androidx.car.app.model.constraints.CarIconConstraints;
-import androidx.car.app.annotations.KeepFields;
 import androidx.lifecycle.LifecycleOwner;
 
 import java.lang.annotation.Retention;
@@ -66,8 +68,8 @@
 public final class Action {
     /**
      * The type of action represented by the {@link Action} instance.
-     *
      */
+    @OptIn(markerClass = androidx.car.app.annotations.ExperimentalCarApi.class)
     @RestrictTo(LIBRARY)
     @IntDef(
             value = {
@@ -75,6 +77,7 @@
                     TYPE_APP_ICON,
                     TYPE_BACK,
                     TYPE_PAN,
+                    TYPE_COMPOSE_MESSAGE,
             })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ActionType {
@@ -82,7 +85,6 @@
 
     /**
      * The flag of action represented by the {@link Action} instance.
-     *
      */
     @RestrictTo(LIBRARY)
     @IntDef(
@@ -123,6 +125,13 @@
     public static final int TYPE_PAN = 4 | TYPE_STANDARD;
 
     /**
+     * An action to allow user compose a message.
+     */
+    @ExperimentalCarApi
+    @RequiresCarApi(7)
+    public static final int TYPE_COMPOSE_MESSAGE = 5 | TYPE_STANDARD;
+
+    /**
      * Indicates that this action is the most important one, out of a set of other actions.
      *
      * <p>The action with this flag may be treated differently by the host depending on where they
@@ -160,6 +169,16 @@
     public static final Action APP_ICON = new Action(TYPE_APP_ICON);
 
     /**
+     * A standard action to show the message compose button
+     *
+     * <p>This action is interactive.
+     */
+    @NonNull
+    @ExperimentalCarApi
+    @RequiresCarApi(7)
+    public static final Action COMPOSE_MESSAGE = new Action(TYPE_COMPOSE_MESSAGE);
+
+    /**
      * A standard action to navigate back in the user interface.
      *
      * <p>The default behavior for a back press will call
@@ -275,6 +294,7 @@
     /**
      * Converts the given {@code type} into a string representation.
      */
+    @OptIn(markerClass = androidx.car.app.annotations.ExperimentalCarApi.class)
     @NonNull
     public static String typeToString(@ActionType int type) {
         switch (type) {
@@ -286,6 +306,8 @@
                 return "BACK";
             case TYPE_PAN:
                 return "PAN";
+            case TYPE_COMPOSE_MESSAGE:
+                return "COMPOSE_MESSAGE";
             default:
                 return "<unknown>";
         }
diff --git a/car/app/app/src/main/java/androidx/car/app/model/TabContents.java b/car/app/app/src/main/java/androidx/car/app/model/TabContents.java
index 84c1a68..0a7b5bd 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/TabContents.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/TabContents.java
@@ -28,7 +28,8 @@
 import java.util.Objects;
 
 /**
- * Represents the contents to display for a selected tab in a {@link TabTemplate}.
+ * Represents the contents to display for a selected tab in a {@link TabTemplate}. Only certain
+ * templates may be used as content. See {@link Builder#Builder(Template)} for more details.
  */
 @CarProtocol
 @RequiresCarApi(6)
@@ -112,17 +113,29 @@
          * Creates a {@link TabContents.Builder} instance using the given {@link Template} to
          * display as contents.
          *
-         * <h4>Requirements</h4>
+         * <p>There should be no title, Header {@link Action} or {@link ActionStrip} set on the
+         * template. The host will ignore these.
          *
-         * There should be no title, Header{@link Action} or {@link ActionStrip} set on the
-         * template.
-         * The host will ignore these.
+         * <p>From Car API 6 onward, the following template types are supported as content:
+         * <ul>
+         *     <li>{@code ListTemplate}
+         *     <li>{@code PaneTemplate}
+         *     <li>{@code GridTemplate}
+         *     <li>{@code MessageTemplate}
+         *     <li>{@code SearchTemplate}
+         * </ul>
+         *
+         * <p>From Car API 7 onward, the following templates types are supported as content in
+         * addition to all previously supported template types:
+         * <ul>
+         *     <li>{@code NavigationTemplate}
+         * </ul>
          *
          * @throws NullPointerException     if {@code template} is null
          * @throws IllegalArgumentException if {@code template} does not meet the requirements
          */
         public Builder(@NonNull Template template) {
-            TabContentsConstraints.DEFAULT.validateOrThrow(requireNonNull(template));
+            TabContentsConstraints.API_7.validateOrThrow(requireNonNull(template));
             mTemplate = template;
         }
     }
diff --git a/car/app/app/src/main/java/androidx/car/app/model/TabTemplate.java b/car/app/app/src/main/java/androidx/car/app/model/TabTemplate.java
index fb8974a..ce84c5d 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/TabTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/TabTemplate.java
@@ -239,7 +239,9 @@
         }
 
         /**
-         * Sets the {@link TabContents} to show in the template.
+         * Sets the {@link TabContents} to show in the template. Note that only certain templates
+         * may be used as content. See {@link TabContents.Builder#Builder(Template)} for more
+         * details.
          *
          * @throws NullPointerException if {@code tabContents} is null
          */
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
index 182656b..e61e54b 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/ActionsConstraints.java
@@ -22,6 +22,8 @@
 
 import static java.util.Objects.requireNonNull;
 
+import android.annotation.SuppressLint;
+
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
@@ -143,15 +145,30 @@
                     .build();
 
     /**
+     * Constraints for additional ConversationItem actions. Only allows custom actions.
+     */
+    @NonNull
+    public static final ActionsConstraints ACTIONS_CONSTRAINTS_CONVERSATION_ITEM =
+            new ActionsConstraints.Builder()
+                    .setMaxActions(1)
+                    .setMaxCustomTitles(1)
+                    .addAllowedActionType(Action.TYPE_CUSTOM)
+                    .setRequireActionIcons(true)
+                    .setOnClickListenerAllowed(true)
+                    .build();
+
+    /**
      * Constraints for floating action buttons.
      *
      * <p>Only buttons with icons and background color are allowed.
      */
+    @SuppressLint("UnsafeOptInUsageError")
     @NonNull
     public static final ActionsConstraints ACTIONS_CONSTRAINTS_FAB =
             new ActionsConstraints.Builder()
                     .setMaxActions(1)
                     .addAllowedActionType(Action.TYPE_CUSTOM)
+                    .addAllowedActionType(Action.TYPE_COMPOSE_MESSAGE)
                     .setRequireActionIcons(true)
                     .setRequireActionBackgroundColor(true)
                     .setOnClickListenerAllowed(true)
diff --git a/car/app/app/src/main/java/androidx/car/app/model/constraints/TabContentsConstraints.java b/car/app/app/src/main/java/androidx/car/app/model/constraints/TabContentsConstraints.java
index 79c64d8..25f65df 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/constraints/TabContentsConstraints.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/constraints/TabContentsConstraints.java
@@ -26,6 +26,7 @@
 import androidx.car.app.model.PaneTemplate;
 import androidx.car.app.model.SearchTemplate;
 import androidx.car.app.model.Template;
+import androidx.car.app.navigation.model.NavigationTemplate;
 
 import java.util.Arrays;
 import java.util.HashSet;
@@ -39,7 +40,10 @@
 @RestrictTo(RestrictTo.Scope.LIBRARY)
 public class TabContentsConstraints {
 
-    /** Allow restricted set of templates as contents for a tab */
+    /**
+     * The set of allowed templates as content within a tab template since the introduction of the
+     * tab template (API 6).
+     */
     @NonNull
     public static final TabContentsConstraints DEFAULT =
             new TabContentsConstraints(Arrays.asList(
@@ -49,6 +53,19 @@
                     MessageTemplate.class,
                     SearchTemplate.class
             ));
+
+    /** The set of allowed templates as content within a tab template since API 7. */
+    @NonNull
+    public static final TabContentsConstraints API_7 =
+            new TabContentsConstraints(Arrays.asList(
+                    ListTemplate.class,
+                    PaneTemplate.class,
+                    GridTemplate.class,
+                    MessageTemplate.class,
+                    SearchTemplate.class,
+                    NavigationTemplate.class
+            ));
+
     private HashSet<Class<? extends Template>> mAllowedTemplateTypes;
 
     /**
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/NavigationTemplate.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/NavigationTemplate.java
index 94190e0..5c467cd 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/NavigationTemplate.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/NavigationTemplate.java
@@ -29,13 +29,13 @@
 import androidx.car.app.Screen;
 import androidx.car.app.SurfaceCallback;
 import androidx.car.app.annotations.CarProtocol;
+import androidx.car.app.annotations.KeepFields;
 import androidx.car.app.annotations.RequiresCarApi;
 import androidx.car.app.model.Action;
 import androidx.car.app.model.ActionStrip;
 import androidx.car.app.model.CarColor;
 import androidx.car.app.model.Template;
 import androidx.car.app.model.Toggle;
-import androidx.car.app.annotations.KeepFields;
 
 import java.util.Objects;
 
@@ -306,15 +306,17 @@
         /**
          * Sets the {@link TravelEstimate} to the final destination.
          *
-         * @throws IllegalArgumentException if the {@link TravelEstimate}'s remaining time is
-         *                                  {@link TravelEstimate#REMAINING_TIME_UNKNOWN} or less
-         *                                  than zero
+         * @throws IllegalArgumentException if the {@link TravelEstimate}'s remaining time is less
+         *                                  than zero unless the value provided is
+         *                                  {@link TravelEstimate#REMAINING_TIME_UNKNOWN}
          * @throws NullPointerException     if {@code destinationTravelEstimate} is {@code null}
          */
         @NonNull
         public Builder setDestinationTravelEstimate(
                 @NonNull TravelEstimate destinationTravelEstimate) {
-            if (requireNonNull(destinationTravelEstimate).getRemainingTimeSeconds() < 0) {
+            if (requireNonNull(destinationTravelEstimate).getRemainingTimeSeconds() < 0
+                    && requireNonNull(destinationTravelEstimate).getRemainingTimeSeconds()
+                    != TravelEstimate.REMAINING_TIME_UNKNOWN) {
                 throw new IllegalArgumentException(
                         "The destination travel estimate's remaining time must be greater or "
                                 + "equal to zero");
diff --git a/car/app/app/src/test/java/androidx/car/app/messaging/model/CarMessageTest.java b/car/app/app/src/test/java/androidx/car/app/messaging/model/CarMessageTest.java
index 06caf6b..c574bc6 100644
--- a/car/app/app/src/test/java/androidx/car/app/messaging/model/CarMessageTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/messaging/model/CarMessageTest.java
@@ -39,6 +39,14 @@
         // assert no crash
     }
 
+    /** Ensure the builder does not fail for the minimum set of required fields. */
+    @Test
+    public void build_withRequiredFieldsOnly_multimedia() {
+        TestConversationFactory.createMinimalMultimediaMessage();
+
+        // assert no crash
+    }
+
     /** Ensure the builder does not fail when all fields are assigned. */
     @Test
     public void build_withAllFields() {
@@ -47,18 +55,52 @@
         // assert no crash
     }
 
-    // Ignore nullability, so we can null out a builder field
-    @SuppressWarnings("ConstantConditions")
     @Test
-    public void build_throwsException_ifMessageBodyMissing() {
+    public void build_throwsException_ifMultimediaDataIncomplete() {
         assertThrows(
-                NullPointerException.class,
-                () -> TestConversationFactory.createMinimalMessageBuilder()
-                        .setBody(null)
+                IllegalStateException.class,
+                () -> TestConversationFactory.createMinimalMultimediaMessageBuilder()
+                        .setMultimediaMimeType(null)
+                        .build()
+        );
+
+        assertThrows(
+                IllegalStateException.class,
+                () -> TestConversationFactory.createMinimalMultimediaMessageBuilder()
+                        .setMultimediaUri(null)
+                        .build()
+        );
+
+        // Even if the message has valid body text, incomplete multimedia metadata is still
+        // considered invalid
+        assertThrows(
+                IllegalStateException.class,
+                () -> TestConversationFactory.createFullyPopulatedMessageBuilder()
+                        .setMultimediaMimeType(null)
+                        .build()
+        );
+
+        assertThrows(
+                IllegalStateException.class,
+                () -> TestConversationFactory.createFullyPopulatedMessageBuilder()
+                        .setMultimediaUri(null)
                         .build()
         );
     }
 
+    @Test
+    public void build_throwsException_ifNoContent() {
+        assertThrows(
+                IllegalStateException.class,
+                () -> TestConversationFactory.createFullyPopulatedMessageBuilder()
+                        .setBody(null)
+                        .setMultimediaMimeType(null)
+                        .setMultimediaUri(null)
+                        .build()
+        );
+    }
+
+    @Test
     public void build_throwsException_ifSenderNameMissing() {
         assertThrows(
                 NullPointerException.class,
@@ -70,6 +112,7 @@
         );
     }
 
+    @Test
     public void build_throwsException_ifSenderKeyMissing() {
         assertThrows(
                 NullPointerException.class,
diff --git a/car/app/app/src/test/java/androidx/car/app/messaging/model/ConversationItemTest.java b/car/app/app/src/test/java/androidx/car/app/messaging/model/ConversationItemTest.java
index ef2956c..e6b6e49 100644
--- a/car/app/app/src/test/java/androidx/car/app/messaging/model/ConversationItemTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/messaging/model/ConversationItemTest.java
@@ -21,8 +21,11 @@
 import static org.junit.Assert.assertThrows;
 
 import androidx.annotation.NonNull;
+import androidx.car.app.TestUtils;
+import androidx.car.app.model.Action;
 import androidx.car.app.model.CarIcon;
 import androidx.car.app.model.CarText;
+import androidx.test.core.app.ApplicationProvider;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -191,6 +194,77 @@
         assertEqual(fullyPopulatedItem, modifiedConversationCallback);
     }
 
+    @Test
+    public void addAction() {
+        CarIcon icon = TestUtils.getTestCarIcon(ApplicationProvider.getApplicationContext(),
+                "ic_test_1");
+        Action customAction = new Action.Builder().setIcon(icon).build();
+        ConversationItem item =
+                TestConversationFactory
+                        .createFullyPopulatedConversationItemBuilder()
+                        .addAction(customAction)
+                        .build();
+
+        assertThat(item.getActions()).containsExactly(customAction);
+    }
+
+    @Test
+    public void addAction_appIconInvalid_throws() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> TestConversationFactory
+                        .createFullyPopulatedConversationItemBuilder()
+                        .addAction(Action.APP_ICON)
+                        .build());
+    }
+
+    @Test
+    public void addAction_backInvalid_throws() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> TestConversationFactory
+                        .createFullyPopulatedConversationItemBuilder()
+                        .addAction(Action.BACK)
+                        .build());
+    }
+
+    @Test
+    public void addAction_panInvalid_throws() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> TestConversationFactory
+                        .createFullyPopulatedConversationItemBuilder()
+                        .addAction(Action.PAN)
+                        .build());
+    }
+
+    @Test
+    public void addAction_manyActions_throws() {
+        CarIcon icon = TestUtils.getTestCarIcon(ApplicationProvider.getApplicationContext(),
+                "ic_test_1");
+        Action customAction = new Action.Builder().setIcon(icon).build();
+
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> TestConversationFactory
+                        .createFullyPopulatedConversationItemBuilder()
+                        .addAction(customAction)
+                        .addAction(customAction)
+                        .build());
+    }
+
+    @Test
+    public void addAction_invalidActionNullIcon_throws() {
+        Action customAction = TestUtils.createAction("Title", /* icon= */ null);
+
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> TestConversationFactory
+                        .createFullyPopulatedConversationItemBuilder()
+                        .addAction(customAction)
+                        .build());
+    }
+
     private void assertEqual(ConversationItem item1, ConversationItem item2) {
         assertThat(item1).isEqualTo(item2);
         assertThat(item1.hashCode()).isEqualTo(item2.hashCode());
diff --git a/car/app/app/src/test/java/androidx/car/app/messaging/model/TestConversationFactory.java b/car/app/app/src/test/java/androidx/car/app/messaging/model/TestConversationFactory.java
index bd1274a..72dcbb0 100644
--- a/car/app/app/src/test/java/androidx/car/app/messaging/model/TestConversationFactory.java
+++ b/car/app/app/src/test/java/androidx/car/app/messaging/model/TestConversationFactory.java
@@ -23,6 +23,7 @@
 import androidx.car.app.model.CarIcon;
 import androidx.car.app.model.CarText;
 import androidx.car.app.model.ItemList;
+import androidx.car.app.model.ListTemplate;
 import androidx.core.app.Person;
 import androidx.core.graphics.drawable.IconCompat;
 
@@ -47,6 +48,9 @@
                 }
             };
 
+    public static String MULTIMEDIA_MESSAGE_MIME_TYPE = "coolmimetypes/mycooldataformat";
+    public static Uri MULTIMEDIA_MESSAGE_URI = new Uri.Builder().path("foo/bar/test/uri").build();
+
     // region Person
     /**
      * Creates a {@link Person.Builder} instance for testing
@@ -113,10 +117,32 @@
     /**
      * Creates a {@link CarMessage.Builder} instance for testing
      *
+     * <p>This method fills in the minimum required data to create a valid {@link CarMessage}.
+     */
+    public static CarMessage.Builder createMinimalMultimediaMessageBuilder() {
+        return new CarMessage.Builder()
+                .setMultimediaMimeType(MULTIMEDIA_MESSAGE_MIME_TYPE)
+                .setMultimediaUri(MULTIMEDIA_MESSAGE_URI);
+    }
+
+    /**
+     * Creates a {@link CarMessage} instance for testing
+     *
+     * <p>This method fills in the minimum required data to create a valid {@link CarMessage}.
+     */
+    public static CarMessage createMinimalMultimediaMessage() {
+        return createMinimalMultimediaMessageBuilder().build();
+    }
+
+    /**
+     * Creates a {@link CarMessage.Builder} instance for testing
+     *
      * <p>This method populates every field in  {@link CarMessage.Builder}.
      */
     public static CarMessage.Builder createFullyPopulatedMessageBuilder() {
         return createMinimalMessageBuilder()
+                .setMultimediaMimeType(MULTIMEDIA_MESSAGE_MIME_TYPE)
+                .setMultimediaUri(MULTIMEDIA_MESSAGE_URI)
                 .setSender(createFullyPopulatedPerson())
                 .setRead(true)
                 .setReceivedTimeEpochMillis(12345);
@@ -187,6 +213,12 @@
         return new ItemList.Builder().addItem(createMinimalConversationItem()).build();
     }
 
+    public static ListTemplate createListTemplateWithConversationItem() {
+        return new ListTemplate.Builder()
+            .setSingleList(createItemListWithConversationItem())
+            .build();
+    }
+
     private TestConversationFactory() {
         // Do not instantiate
     }
diff --git a/car/app/app/src/test/java/androidx/car/app/model/ListTemplateTest.java b/car/app/app/src/test/java/androidx/car/app/model/ListTemplateTest.java
index 1777bdf..b0c1369 100644
--- a/car/app/app/src/test/java/androidx/car/app/model/ListTemplateTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/ListTemplateTest.java
@@ -263,6 +263,19 @@
     }
 
     @Test
+    public void createInstance_addComposeAction() {
+        CarIcon icon = TestUtils.getTestCarIcon(ApplicationProvider.getApplicationContext(),
+                "ic_test_1");
+        Action composeAction = Action.COMPOSE_MESSAGE;
+        ListTemplate template =
+                new ListTemplate.Builder()
+                        .setSingleList(getList())
+                        .addAction(composeAction)
+                        .build();
+        assertThat(template.getActions()).containsExactly(composeAction);
+    }
+
+    @Test
     public void createInstance_addAction_appIconInvalid_throws() {
         assertThrows(
                 IllegalArgumentException.class,
diff --git a/car/app/app/src/test/java/androidx/car/app/model/TabContentsTest.java b/car/app/app/src/test/java/androidx/car/app/model/TabContentsTest.java
index bc16980..4b38369 100644
--- a/car/app/app/src/test/java/androidx/car/app/model/TabContentsTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/TabContentsTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertThrows;
 
 import androidx.car.app.navigation.model.MapTemplate;
+import androidx.car.app.navigation.model.NavigationTemplate;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -74,6 +75,17 @@
     }
 
     @Test
+    public void createInstance_navigationTemplate() {
+        NavigationTemplate template =
+                new NavigationTemplate.Builder().setActionStrip(new ActionStrip.Builder().addAction(
+                        new Action.Builder().setTitle("test").build()).build()).build();
+
+        TabContents tabContents = new TabContents.Builder(template).build();
+
+        assertEquals(template, tabContents.getTemplate());
+    }
+
+    @Test
     public void equals() {
         MessageTemplate template = new MessageTemplate.Builder("title")
                 .addAction(
diff --git a/car/app/app/src/test/java/androidx/car/app/navigation/model/NavigationTemplateTest.java b/car/app/app/src/test/java/androidx/car/app/navigation/model/NavigationTemplateTest.java
index 5edd1f2..aec4364 100644
--- a/car/app/app/src/test/java/androidx/car/app/navigation/model/NavigationTemplateTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/navigation/model/NavigationTemplateTest.java
@@ -65,6 +65,40 @@
                 () -> new NavigationTemplate.Builder().setMapActionStrip(mActionStrip));
     }
 
+    @Test
+    public void negativeValueInRemainingTimeSeconds_throws() {
+        assertThrows(IllegalArgumentException.class,
+                () -> createMinimalTemplateWithRemainingSeconds(-100));
+    }
+
+    @Test
+    public void unknownValueInRemainingTimeSeconds_works() {
+        NavigationTemplate navigationTemplate =
+                createMinimalTemplateWithRemainingSeconds(TravelEstimate.REMAINING_TIME_UNKNOWN);
+
+        assertThat(
+                navigationTemplate.getDestinationTravelEstimate().getRemainingTimeSeconds())
+                .isEqualTo(TravelEstimate.REMAINING_TIME_UNKNOWN);
+    }
+
+
+    private NavigationTemplate createMinimalTemplateWithRemainingSeconds(long remainingSeconds) {
+        TravelEstimate travelEstimate =
+                new TravelEstimate.Builder(
+                        Distance.create(/* displayDistance= */ 20, Distance.UNIT_METERS),
+                        createDateTimeWithZone("2020-05-14T19:57:00-07:00", "US/Pacific"))
+                        .setRemainingTimeSeconds(remainingSeconds).build();
+
+        return new NavigationTemplate.Builder()
+                .setNavigationInfo(
+                        new RoutingInfo.Builder().setCurrentStep(mCurrentStep,
+                                mCurrentDistance).build())
+                .setActionStrip(mActionStrip)
+                .setDestinationTravelEstimate(travelEstimate)
+                .build();
+    }
+
+
     /** Tests basic construction of a template with a minimal data. */
     @Test
     public void createMinimalInstance() {
@@ -209,10 +243,9 @@
     @Test
     public void notEquals_panModeListenerChange() {
         NavigationTemplate template = new NavigationTemplate.Builder().setActionStrip(
-                mActionStrip).setPanModeListener((isInPanMode) -> {}).build();
+                mActionStrip).setPanModeListener((isInPanMode) -> { }).build();
 
-        assertThat(template)
-                .isNotEqualTo(
+        assertThat(template).isNotEqualTo(
                         new NavigationTemplate.Builder()
                                 .setActionStrip(mActionStrip)
                                 .build());
diff --git a/collection/collection/src/nativeMain/kotlin/androidx/collection/CollectionPlatformUtils.native.kt b/collection/collection/src/nativeMain/kotlin/androidx/collection/CollectionPlatformUtils.native.kt
index 20777ea..fdacbb6 100644
--- a/collection/collection/src/nativeMain/kotlin/androidx/collection/CollectionPlatformUtils.native.kt
+++ b/collection/collection/src/nativeMain/kotlin/androidx/collection/CollectionPlatformUtils.native.kt
@@ -21,6 +21,6 @@
 internal actual object CollectionPlatformUtils {
 
     internal actual inline fun createIndexOutOfBoundsException(): IndexOutOfBoundsException {
-        return ArrayIndexOutOfBoundsException()
+        return IndexOutOfBoundsException()
     }
 }
diff --git a/collection/collection/src/nativeMain/kotlin/androidx/collection/internal/Lock.native.kt b/collection/collection/src/nativeMain/kotlin/androidx/collection/internal/Lock.native.kt
index 558b8dc..70af07f 100644
--- a/collection/collection/src/nativeMain/kotlin/androidx/collection/internal/Lock.native.kt
+++ b/collection/collection/src/nativeMain/kotlin/androidx/collection/internal/Lock.native.kt
@@ -18,6 +18,7 @@
 
 import kotlin.native.internal.createCleaner
 import kotlinx.cinterop.Arena
+import kotlinx.cinterop.ExperimentalForeignApi
 import kotlinx.cinterop.alloc
 import kotlinx.cinterop.ptr
 import platform.posix.pthread_mutex_destroy
@@ -38,6 +39,7 @@
 internal expect val PTHREAD_MUTEX_RECURSIVE: Int
 
 @Suppress("ACTUAL_WITHOUT_EXPECT") // https://youtrack.jetbrains.com/issue/KT-37316
+@OptIn(ExperimentalForeignApi::class)
 internal actual class Lock actual constructor() {
 
     private val resources = Resources()
diff --git a/compose/animation/animation-core/api/current.txt b/compose/animation/animation-core/api/current.txt
index f6e8e56..d1a61aa 100644
--- a/compose/animation/animation-core/api/current.txt
+++ b/compose/animation/animation-core/api/current.txt
@@ -239,7 +239,7 @@
   public final class DecayAnimationSpecKt {
     method public static float calculateTargetValue(androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float>, float initialValue, float initialVelocity);
     method public static <T, V extends androidx.compose.animation.core.AnimationVector> T calculateTargetValue(androidx.compose.animation.core.DecayAnimationSpec<T>, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T initialValue, T initialVelocity);
-    method public static <T> androidx.compose.animation.core.DecayAnimationSpec<T> exponentialDecay(optional float frictionMultiplier, optional float absVelocityThreshold);
+    method public static <T> androidx.compose.animation.core.DecayAnimationSpec<T> exponentialDecay(optional @FloatRange(from=0.0, fromInclusive=false) float frictionMultiplier, optional @FloatRange(from=0.0, fromInclusive=false) float absVelocityThreshold);
     method public static <T> androidx.compose.animation.core.DecayAnimationSpec<T> generateDecayAnimationSpec(androidx.compose.animation.core.FloatDecayAnimationSpec);
   }
 
@@ -358,7 +358,7 @@
   }
 
   public final class FloatExponentialDecaySpec implements androidx.compose.animation.core.FloatDecayAnimationSpec {
-    ctor public FloatExponentialDecaySpec(optional float frictionMultiplier, optional float absVelocityThreshold);
+    ctor public FloatExponentialDecaySpec(optional @FloatRange(from=0.0, fromInclusive=false) float frictionMultiplier, optional @FloatRange(from=0.0, fromInclusive=false) float absVelocityThreshold);
     method public float getAbsVelocityThreshold();
     method public long getDurationNanos(float initialValue, float initialVelocity);
     method public float getTargetValue(float initialValue, float initialVelocity);
@@ -450,15 +450,15 @@
 
   public static final class KeyframesSpec.KeyframesSpecConfig<T> {
     ctor public KeyframesSpec.KeyframesSpecConfig();
-    method public infix androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T> at(T, int timeStamp);
+    method public infix androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T> at(T, @IntRange(from=0L) int timeStamp);
     method public infix androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T> atFraction(T, float fraction);
-    method public int getDelayMillis();
-    method public int getDurationMillis();
+    method @IntRange(from=0L) public int getDelayMillis();
+    method @IntRange(from=0L) public int getDurationMillis();
     method public void setDelayMillis(int);
     method public void setDurationMillis(int);
     method public infix void with(androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T>, androidx.compose.animation.core.Easing easing);
-    property public final int delayMillis;
-    property public final int durationMillis;
+    property @IntRange(from=0L) public final int delayMillis;
+    property @IntRange(from=0L) public final int durationMillis;
   }
 
   public final class MutableTransitionState<S> {
diff --git a/compose/animation/animation-core/api/restricted_current.txt b/compose/animation/animation-core/api/restricted_current.txt
index 091fb90..23c18bd 100644
--- a/compose/animation/animation-core/api/restricted_current.txt
+++ b/compose/animation/animation-core/api/restricted_current.txt
@@ -239,7 +239,7 @@
   public final class DecayAnimationSpecKt {
     method public static float calculateTargetValue(androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float>, float initialValue, float initialVelocity);
     method public static <T, V extends androidx.compose.animation.core.AnimationVector> T calculateTargetValue(androidx.compose.animation.core.DecayAnimationSpec<T>, androidx.compose.animation.core.TwoWayConverter<T,V> typeConverter, T initialValue, T initialVelocity);
-    method public static <T> androidx.compose.animation.core.DecayAnimationSpec<T> exponentialDecay(optional float frictionMultiplier, optional float absVelocityThreshold);
+    method public static <T> androidx.compose.animation.core.DecayAnimationSpec<T> exponentialDecay(optional @FloatRange(from=0.0, fromInclusive=false) float frictionMultiplier, optional @FloatRange(from=0.0, fromInclusive=false) float absVelocityThreshold);
     method public static <T> androidx.compose.animation.core.DecayAnimationSpec<T> generateDecayAnimationSpec(androidx.compose.animation.core.FloatDecayAnimationSpec);
   }
 
@@ -358,7 +358,7 @@
   }
 
   public final class FloatExponentialDecaySpec implements androidx.compose.animation.core.FloatDecayAnimationSpec {
-    ctor public FloatExponentialDecaySpec(optional float frictionMultiplier, optional float absVelocityThreshold);
+    ctor public FloatExponentialDecaySpec(optional @FloatRange(from=0.0, fromInclusive=false) float frictionMultiplier, optional @FloatRange(from=0.0, fromInclusive=false) float absVelocityThreshold);
     method public float getAbsVelocityThreshold();
     method public long getDurationNanos(float initialValue, float initialVelocity);
     method public float getTargetValue(float initialValue, float initialVelocity);
@@ -450,15 +450,15 @@
 
   public static final class KeyframesSpec.KeyframesSpecConfig<T> {
     ctor public KeyframesSpec.KeyframesSpecConfig();
-    method public infix androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T> at(T, int timeStamp);
+    method public infix androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T> at(T, @IntRange(from=0L) int timeStamp);
     method public infix androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T> atFraction(T, float fraction);
-    method public int getDelayMillis();
-    method public int getDurationMillis();
+    method @IntRange(from=0L) public int getDelayMillis();
+    method @IntRange(from=0L) public int getDurationMillis();
     method public void setDelayMillis(int);
     method public void setDurationMillis(int);
     method public infix void with(androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T>, androidx.compose.animation.core.Easing easing);
-    property public final int delayMillis;
-    property public final int durationMillis;
+    property @IntRange(from=0L) public final int delayMillis;
+    property @IntRange(from=0L) public final int durationMillis;
   }
 
   public final class MutableTransitionState<S> {
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationSpec.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationSpec.kt
index 126a5d1..e280595 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationSpec.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationSpec.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.animation.core
 
+import androidx.annotation.IntRange
 import androidx.compose.animation.core.AnimationConstants.DefaultDurationMillis
 import androidx.compose.animation.core.KeyframesSpec.KeyframesSpecConfig
 import androidx.compose.runtime.Immutable
@@ -421,14 +422,14 @@
          * Duration of the animation in milliseconds. The minimum is `0` and defaults to
          * [DefaultDurationMillis]
          */
-        /*@IntRange(from = 0)*/
+        @get:IntRange(from = 0)
         var durationMillis: Int = DefaultDurationMillis
 
         /**
          * The amount of time that the animation should be delayed. The minimum is `0` and defaults
          * to 0.
          */
-        /*@IntRange(from = 0)*/
+        @get:IntRange(from = 0)
         var delayMillis: Int = 0
 
         internal val keyframes = mutableMapOf<Int, KeyframeEntity<T>>()
@@ -442,7 +443,7 @@
          * @return an [KeyframeEntity] so a custom [Easing] can be added by [with] method.
          */
         // TODO: Need a IntRange equivalent annotation
-        infix fun T.at(/*@IntRange(from = 0)*/ timeStamp: Int): KeyframeEntity<T> {
+        infix fun T.at(@IntRange(from = 0) timeStamp: Int): KeyframeEntity<T> {
             return KeyframeEntity(this).also {
                 keyframes[timeStamp] = it
             }
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/DecayAnimationSpec.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/DecayAnimationSpec.kt
index 2770f02..58a3561 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/DecayAnimationSpec.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/DecayAnimationSpec.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.animation.core
 
+import androidx.annotation.FloatRange
 import androidx.compose.ui.unit.IntOffset
 
 /**
@@ -99,15 +100,15 @@
  * Must be greater than `0`.
  */
 fun <T> exponentialDecay(
-    /*@FloatRange(
+    @FloatRange(
         from = 0.0,
         fromInclusive = false
-    )*/
+    )
     frictionMultiplier: Float = 1f,
-    /*@FloatRange(
+    @FloatRange(
         from = 0.0,
         fromInclusive = false
-    )*/
+    )
     absVelocityThreshold: Float = 0.1f
 ): DecayAnimationSpec<T> =
     FloatExponentialDecaySpec(frictionMultiplier, absVelocityThreshold).generateDecayAnimationSpec()
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatDecayAnimationSpec.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatDecayAnimationSpec.kt
index 98cc051..8790428 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatDecayAnimationSpec.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/FloatDecayAnimationSpec.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.animation.core
 
+import androidx.annotation.FloatRange
 import kotlin.math.abs
 import kotlin.math.exp
 import kotlin.math.ln
@@ -99,15 +100,15 @@
  * rest for the animation to finish.
  */
 class FloatExponentialDecaySpec(
-    /*@FloatRange(
+    @FloatRange(
         from = 0.0,
         fromInclusive = false
-    )*/
+    )
     frictionMultiplier: Float = 1f,
-    /*@FloatRange(
+    @FloatRange(
         from = 0.0,
         fromInclusive = false
-    )*/
+    )
     absVelocityThreshold: Float = 0.1f
 ) : FloatDecayAnimationSpec {
 
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
index 3e9edc1..17cfebe 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/Transition.kt
@@ -18,7 +18,6 @@
 
 package androidx.compose.animation.core
 
-import androidx.compose.animation.core.internal.JvmDefaultWithCompatibility
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
@@ -39,7 +38,6 @@
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
-import kotlin.jvm.JvmName
 import kotlin.math.max
 
 /**
@@ -446,6 +444,10 @@
         }
     }
 
+    override fun toString(): String {
+        return animations.fold("Transition animation values: ") { acc, anim -> "$acc$anim, " }
+    }
+
     private fun onChildAnimationUpdated() {
         updateChildrenNeeded = true
         if (isSeeking) {
@@ -567,6 +569,10 @@
             needsReset = true
         }
 
+        override fun toString(): String {
+            return "current value: $value, target: $targetValue, spec: $animationSpec"
+        }
+
         // This gets called *during* composition
         internal fun updateTargetValue(targetValue: T, animationSpec: FiniteAnimationSpec<T>) {
             if (this.targetValue != targetValue || needsReset) {
diff --git a/compose/animation/animation-graphics/build.gradle b/compose/animation/animation-graphics/build.gradle
index dacdfee..4faa4b5 100644
--- a/compose/animation/animation-graphics/build.gradle
+++ b/compose/animation/animation-graphics/build.gradle
@@ -120,7 +120,6 @@
         if (desktopEnabled) {
             desktopTest {
                 dependsOn(jvmTest)
-                dependsOn(desktopMain)
                 dependencies {
                 }
             }
diff --git a/compose/animation/animation-graphics/src/androidAndroidTest/kotlin/androidx/compose/animation/graphics/res/AnimatedVectorPainterResourcesTest.kt b/compose/animation/animation-graphics/src/androidAndroidTest/kotlin/androidx/compose/animation/graphics/res/AnimatedVectorPainterResourcesTest.kt
new file mode 100644
index 0000000..2f4bbd2
--- /dev/null
+++ b/compose/animation/animation-graphics/src/androidAndroidTest/kotlin/androidx/compose/animation/graphics/res/AnimatedVectorPainterResourcesTest.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.animation.graphics.res
+
+import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi
+import androidx.compose.animation.graphics.test.R
+import androidx.compose.animation.graphics.vector.AnimatedImageVector
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.graphics.vector.VectorConfig
+import androidx.compose.ui.graphics.vector.VectorProperty
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalAnimationGraphicsApi::class)
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class AnimatedVectorPainterResourcesTest {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun vectorConfig() {
+        val isAtEnd = mutableStateOf(false)
+        var checked = false
+        rule.setContent {
+            val avd = AnimatedImageVector.animatedVectorResource(R.drawable.avd_complex)
+            rememberAnimatedVectorPainter(
+                animatedImageVector = avd,
+                atEnd = isAtEnd.value
+            ) { _, map ->
+                if (!checked) {
+                    assertThat(map).containsKey("background")
+                    val config = map["background"] as VectorConfig
+                    val fill = config.getOrDefault(VectorProperty.Fill, null)
+                    assertThat(fill).isNotNull()
+                    val stroke = config.getOrDefault(VectorProperty.Stroke, null)
+                    assertThat(stroke).isNull()
+                    checked = true
+                }
+            }
+        }
+        rule.runOnIdle { isAtEnd.value = true }
+        rule.waitForIdle()
+        assertThat(checked).isTrue()
+    }
+
+    @Test
+    fun targetDuplicated() {
+        val isAtEnd = mutableStateOf(false)
+        var checked = false
+        rule.setContent {
+            val avd = AnimatedImageVector.animatedVectorResource(R.drawable.target_duplicated)
+            rememberAnimatedVectorPainter(
+                animatedImageVector = avd,
+                atEnd = isAtEnd.value
+            ) { _, map ->
+                if (!checked) {
+                    assertThat(map).containsKey("line_01")
+                    val config = map["line_01"] as VectorConfig
+                    val strokeWidth = config.getOrDefault(VectorProperty.StrokeLineWidth, 0f)
+                    assertThat(strokeWidth).isNotEqualTo(0f)
+                    val stroke = config.getOrDefault(VectorProperty.Stroke, null)
+                    assertThat(stroke).isNotNull()
+                    checked = true
+                }
+            }
+        }
+        rule.runOnIdle { isAtEnd.value = true }
+        rule.waitForIdle()
+        assertThat(checked).isTrue()
+    }
+}
diff --git a/compose/animation/animation-graphics/src/androidAndroidTest/res/drawable/target_duplicated.xml b/compose/animation/animation-graphics/src/androidAndroidTest/res/drawable/target_duplicated.xml
new file mode 100644
index 0000000..88254bba
--- /dev/null
+++ b/compose/animation/animation-graphics/src/androidAndroidTest/res/drawable/target_duplicated.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <target android:name="line_01">
+        <aapt:attr name="android:animation">
+            <objectAnimator
+                android:duration="1000"
+                android:propertyName="strokeColor"
+                android:valueFrom="#ff0000"
+                android:valueTo="#0000ff"
+                android:valueType="colorType" />
+        </aapt:attr>
+    </target>
+    <target android:name="line_01">
+        <aapt:attr name="android:animation">
+            <objectAnimator
+                android:duration="1000"
+                android:propertyName="strokeWidth"
+                android:valueFrom="1"
+                android:valueTo="4"
+                android:valueType="floatType" />
+        </aapt:attr>
+    </target>
+    <aapt:attr name="android:drawable">
+        <vector
+            android:width="30dp"
+            android:height="30dp"
+            android:viewportHeight="30"
+            android:viewportWidth="30">
+            <path
+                android:name="line_01"
+                android:pathData=" M0,15 L30,15 "
+                android:strokeAlpha="1"
+                android:strokeColor="#ffffff"
+                android:strokeLineCap="round"
+                android:strokeLineJoin="round"
+                android:strokeWidth="1" />
+        </vector>
+    </aapt:attr>
+</animated-vector>
diff --git a/compose/animation/animation-graphics/src/androidMain/kotlin/androidx/compose/animation/graphics/res/AnimatedVectorPainterResources.android.kt b/compose/animation/animation-graphics/src/androidMain/kotlin/androidx/compose/animation/graphics/res/AnimatedVectorPainterResources.android.kt
index d444652..58544fd 100644
--- a/compose/animation/animation-graphics/src/androidMain/kotlin/androidx/compose/animation/graphics/res/AnimatedVectorPainterResources.android.kt
+++ b/compose/animation/animation-graphics/src/androidMain/kotlin/androidx/compose/animation/graphics/res/AnimatedVectorPainterResources.android.kt
@@ -16,9 +16,11 @@
 
 package androidx.compose.animation.graphics.res
 
+import androidx.annotation.VisibleForTesting
 import androidx.compose.animation.core.updateTransition
 import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi
 import androidx.compose.animation.graphics.vector.AnimatedImageVector
+import androidx.compose.animation.graphics.vector.StateVectorConfig
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.graphics.painter.Painter
 import androidx.compose.ui.graphics.vector.RenderVectorGroup
@@ -48,8 +50,9 @@
 }
 
 @ExperimentalAnimationGraphicsApi
+@VisibleForTesting
 @Composable
-private fun rememberAnimatedVectorPainter(
+internal fun rememberAnimatedVectorPainter(
     animatedImageVector: AnimatedImageVector,
     atEnd: Boolean,
     render: @Composable @VectorComposable (VectorGroup, Map<String, VectorConfig>) -> Unit
@@ -65,14 +68,19 @@
         autoMirror = true
     ) { _, _ ->
         val transition = updateTransition(atEnd, label = animatedImageVector.imageVector.name)
-        render(
-            animatedImageVector.imageVector.root,
-            animatedImageVector.targets.associate { target ->
-                target.name to target.animator.createVectorConfig(
-                    transition,
-                    animatedImageVector.totalDuration
-                )
+        val map = mutableMapOf<String, StateVectorConfig>()
+        for (target in animatedImageVector.targets) {
+            val config = target.animator.createVectorConfig(
+                transition,
+                animatedImageVector.totalDuration
+            )
+            val currentConfig = map[target.name]
+            if (currentConfig != null) {
+                currentConfig.merge(config)
+            } else {
+                map[target.name] = config
             }
-        )
+        }
+        render(animatedImageVector.imageVector.root, map)
     }
 }
diff --git a/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/vector/Animator.kt b/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/vector/Animator.kt
index 483a690..37fa401 100644
--- a/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/vector/Animator.kt
+++ b/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/vector/Animator.kt
@@ -50,7 +50,7 @@
     fun createVectorConfig(
         transition: Transition<Boolean>,
         overallDuration: Int
-    ): VectorConfig {
+    ): StateVectorConfig {
         return remember { StateVectorConfig() }.also { config ->
             Configure(transition, config, overallDuration)
         }
@@ -485,6 +485,25 @@
             is VectorProperty.TrimPathOffset -> trimPathOffsetState?.value ?: defaultValue
         } as T
     }
+
+    fun merge(config: StateVectorConfig) {
+        if (config.rotationState != null) rotationState = config.rotationState
+        if (config.pivotXState != null) pivotXState = config.pivotXState
+        if (config.pivotYState != null) pivotYState = config.pivotYState
+        if (config.scaleXState != null) scaleXState = config.scaleXState
+        if (config.scaleYState != null) scaleYState = config.scaleYState
+        if (config.translateXState != null) translateXState = config.translateXState
+        if (config.translateYState != null) translateYState = config.translateYState
+        if (config.pathDataState != null) pathDataState = config.pathDataState
+        if (config.fillColorState != null) fillColorState = config.fillColorState
+        if (config.strokeColorState != null) strokeColorState = config.strokeColorState
+        if (config.strokeWidthState != null) strokeWidthState = config.strokeWidthState
+        if (config.strokeAlphaState != null) strokeAlphaState = config.strokeAlphaState
+        if (config.fillAlphaState != null) fillAlphaState = config.fillAlphaState
+        if (config.trimPathStartState != null) trimPathStartState = config.trimPathStartState
+        if (config.trimPathEndState != null) trimPathEndState = config.trimPathEndState
+        if (config.trimPathOffsetState != null) trimPathOffsetState = config.trimPathOffsetState
+    }
 }
 
 private fun lerp(start: List<PathNode>, stop: List<PathNode>, fraction: Float): List<PathNode> {
diff --git a/compose/animation/animation/api/current.txt b/compose/animation/animation/api/current.txt
index 540e1d9..f66083e 100644
--- a/compose/animation/animation/api/current.txt
+++ b/compose/animation/animation/api/current.txt
@@ -17,6 +17,7 @@
   }
 
   public sealed interface AnimatedContentTransitionScope<S> extends androidx.compose.animation.core.Transition.Segment<S> {
+    method public default androidx.compose.animation.ExitTransition getHold(androidx.compose.animation.ExitTransition.Companion);
     method public androidx.compose.animation.EnterTransition slideIntoContainer(int towards, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Integer> initialOffset);
     method public androidx.compose.animation.ExitTransition slideOutOfContainer(int towards, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Integer> targetOffset);
     method public infix androidx.compose.animation.ContentTransform using(androidx.compose.animation.ContentTransform, androidx.compose.animation.SizeTransform? sizeTransform);
diff --git a/compose/animation/animation/api/restricted_current.txt b/compose/animation/animation/api/restricted_current.txt
index 540e1d9..f66083e 100644
--- a/compose/animation/animation/api/restricted_current.txt
+++ b/compose/animation/animation/api/restricted_current.txt
@@ -17,6 +17,7 @@
   }
 
   public sealed interface AnimatedContentTransitionScope<S> extends androidx.compose.animation.core.Transition.Segment<S> {
+    method public default androidx.compose.animation.ExitTransition getHold(androidx.compose.animation.ExitTransition.Companion);
     method public androidx.compose.animation.EnterTransition slideIntoContainer(int towards, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Integer> initialOffset);
     method public androidx.compose.animation.ExitTransition slideOutOfContainer(int towards, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Integer> targetOffset);
     method public infix androidx.compose.animation.ContentTransform using(androidx.compose.animation.ContentTransform, androidx.compose.animation.SizeTransform? sizeTransform);
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/gesture/FancyScrollingDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/gesture/FancyScrollingDemo.kt
index 86bc567..78804eb 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/gesture/FancyScrollingDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/gesture/FancyScrollingDemo.kt
@@ -77,9 +77,9 @@
                     val decay = exponentialDecay<Float>()
                     val target = decay.calculateTargetValue(animScroll.value, velocity)
                     // Adjust the target position to center align the item
-                    var rem = target % itemWidth.value
+                    var rem = target % itemWidth.floatValue
                     if (rem < 0) {
-                        rem += itemWidth.value
+                        rem += itemWidth.floatValue
                     }
                     animScroll.animateTo(
                         targetValue = target - rem,
@@ -93,7 +93,7 @@
         Canvas(modifier.fillMaxWidth().height(400.dp)) {
             val width = size.width / 2f
             val scroll = animScroll.value + width / 2
-            itemWidth.value = width
+            itemWidth.floatValue = width
             if (DEBUG) {
                 Log.w(
                     "Anim",
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/gesture/SwipeToDismissDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/gesture/SwipeToDismissDemo.kt
index 2f0db13..5853da2 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/gesture/SwipeToDismissDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/gesture/SwipeToDismissDemo.kt
@@ -89,7 +89,7 @@
     this.pointerInput(Unit) {
         coroutineScope {
             while (true) {
-                height.value = size.height
+                height.intValue = size.height
                 val velocityTracker = VelocityTracker()
                 awaitPointerEventScope {
                     val pointerId = awaitFirstDown().id
@@ -126,7 +126,7 @@
             }
         }
     }.offset { IntOffset(0, animatedOffset.value.roundToInt()) }
-        .graphicsLayer(alpha = calculateAlpha(animatedOffset.value, height.value))
+        .graphicsLayer(alpha = calculateAlpha(animatedOffset.value, height.intValue))
 }
 
 private fun calculateAlpha(offset: Float, size: Int): Float {
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/layoutanimation/NestedMenuDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/layoutanimation/NestedMenuDemo.kt
index eccc068..1c84bef 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/layoutanimation/NestedMenuDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/layoutanimation/NestedMenuDemo.kt
@@ -18,6 +18,7 @@
 
 import androidx.compose.animation.AnimatedContent
 import androidx.compose.animation.AnimatedContentTransitionScope.SlideDirection
+import androidx.compose.animation.ExitTransition
 import androidx.compose.animation.ExperimentalAnimationApi
 import androidx.compose.animation.togetherWith
 import androidx.compose.foundation.background
@@ -74,10 +75,7 @@
                 if (initialState < targetState) {
                     // Going from parent menu to child menu, slide towards left
                     slideIntoContainer(towards = SlideDirection.Left) togetherWith
-                        slideOutOfContainer(
-                            towards = SlideDirection.Left,
-                            targetOffset = { offsetForFullSlide -> offsetForFullSlide / 2 }
-                        )
+                        ExitTransition.Hold
                 } else {
                     // Going from child menu to parent menu, slide towards right
                     slideIntoContainer(
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/layoutanimation/ScreenTransitionDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/layoutanimation/ScreenTransitionDemo.kt
index e798134..21c4e39 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/layoutanimation/ScreenTransitionDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/layoutanimation/ScreenTransitionDemo.kt
@@ -18,6 +18,7 @@
 
 import androidx.compose.animation.AnimatedContent
 import androidx.compose.animation.AnimatedContentTransitionScope.SlideDirection
+import androidx.compose.animation.ExitTransition
 import androidx.compose.animation.core.tween
 import androidx.compose.animation.core.updateTransition
 import androidx.compose.animation.expandHorizontally
@@ -65,7 +66,9 @@
                         else -> TestScreens.Screen1
                     }
                 },
-                modifier = Modifier.align(Alignment.CenterVertically).padding(10.dp)
+                modifier = Modifier
+                    .align(Alignment.CenterVertically)
+                    .padding(10.dp)
             ) {
                 Text("Previous screen")
             }
@@ -77,7 +80,9 @@
                         else -> TestScreens.Screen1
                     }
                 },
-                modifier = Modifier.align(Alignment.CenterVertically).padding(10.dp)
+                modifier = Modifier
+                    .align(Alignment.CenterVertically)
+                    .padding(10.dp)
             ) {
                 Text("Next screen")
             }
@@ -85,25 +90,32 @@
         val transition = updateTransition(targetScreen, "screen transition")
         transition.AnimatedContent(
             transitionSpec = {
-                if (TestScreens.Screen1 isTransitioningTo TestScreens.Screen2 ||
-                    TestScreens.Screen2 isTransitioningTo TestScreens.Screen1
-                ) {
-                    (expandHorizontally(animationSpec = tween(500)) + fadeIn()).togetherWith(
-                        shrinkVertically(animationSpec = tween(500)) +
-                            fadeOut(animationSpec = tween(500))
-                    )
-                } else if (TestScreens.Screen2 isTransitioningTo TestScreens.Screen3) {
-                    slideIntoContainer(towards = SlideDirection.Left) togetherWith
-                        slideOutOfContainer(towards = SlideDirection.Left)
-                } else if (TestScreens.Screen3 isTransitioningTo TestScreens.Screen2) {
-                    slideIntoContainer(towards = SlideDirection.Right) togetherWith
-                        slideOutOfContainer(towards = SlideDirection.Right)
-                } else {
-                    // Material fade through
-                    fadeIn(animationSpec = tween(220, delayMillis = 90)) +
-                        scaleIn(
-                            initialScale = 0.92f, animationSpec = tween(220, delayMillis = 90)
-                        ) togetherWith fadeOut(animationSpec = tween(90))
+                when {
+                    TestScreens.Screen1 isTransitioningTo TestScreens.Screen2 ||
+                        TestScreens.Screen2 isTransitioningTo TestScreens.Screen1 ->
+                        (expandHorizontally(animationSpec = tween(500)) + fadeIn()).togetherWith(
+                            shrinkVertically(animationSpec = tween(500)) +
+                                fadeOut(animationSpec = tween(500))
+                        )
+
+                    TestScreens.Screen2 isTransitioningTo TestScreens.Screen3 ->
+                        slideIntoContainer(towards = SlideDirection.Left) togetherWith
+                            slideOutOfContainer(towards = SlideDirection.Left)
+
+                    TestScreens.Screen3 isTransitioningTo TestScreens.Screen2 ->
+                        slideIntoContainer(towards = SlideDirection.Right) togetherWith
+                            slideOutOfContainer(towards = SlideDirection.Right)
+
+                    TestScreens.Screen3 isTransitioningTo TestScreens.Screen1 ->
+                        slideIntoContainer(towards = SlideDirection.Right) togetherWith
+                            ExitTransition.Hold
+
+                    else ->
+                        // Material fade through
+                        fadeIn(animationSpec = tween(220, delayMillis = 90)) +
+                            scaleIn(
+                                initialScale = 0.92f, animationSpec = tween(220, delayMillis = 90)
+                            ) togetherWith fadeOut(animationSpec = tween(90))
                 }
             }
         ) {
@@ -124,21 +136,30 @@
 
 @Composable
 fun Screen1() {
-    Box(modifier = Modifier.fillMaxSize().padding(30.dp).background(Color(0xffff6f69))) {
+    Box(modifier = Modifier
+        .fillMaxSize()
+        .padding(30.dp)
+        .background(Color(0xffff6f69))) {
         Text("Screen 1", modifier = Modifier.align(Center))
     }
 }
 
 @Composable
 fun Screen2() {
-    Box(modifier = Modifier.fillMaxSize().padding(30.dp).background(Color(0xffffcc5c))) {
+    Box(modifier = Modifier
+        .fillMaxSize()
+        .padding(30.dp)
+        .background(Color(0xffffcc5c))) {
         Text("Screen 2", modifier = Modifier.align(Center))
     }
 }
 
 @Composable
 fun Screen3() {
-    Box(modifier = Modifier.fillMaxSize().padding(30.dp).background(Color(0xff2a9d84))) {
+    Box(modifier = Modifier
+        .fillMaxSize()
+        .padding(30.dp)
+        .background(Color(0xff2a9d84))) {
         Text("Screen 3", modifier = Modifier.align(Center))
     }
 }
diff --git a/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/AnimatedContentSamples.kt b/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/AnimatedContentSamples.kt
index 915c79c..f21c93f 100644
--- a/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/AnimatedContentSamples.kt
+++ b/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/AnimatedContentSamples.kt
@@ -21,6 +21,7 @@
 import androidx.compose.animation.AnimatedContentTransitionScope
 import androidx.compose.animation.AnimatedContentTransitionScope.SlideDirection
 import androidx.compose.animation.ContentTransform
+import androidx.compose.animation.ExitTransition
 import androidx.compose.animation.SizeTransform
 import androidx.compose.animation.core.animateDp
 import androidx.compose.animation.core.keyframes
@@ -283,13 +284,8 @@
         if (initialState < targetState) {
             // Going from parent menu to child menu, slide towards left
             slideIntoContainer(towards = SlideDirection.Left) togetherWith
-                // Slide the parent out by 1/2 the amount required to be completely
-                // out of the bounds. This creates a sense of child menu catching up. Since
-                // the child menu has a higher z-order, it will cover the parent meu as it
-                // comes in.
-                slideOutOfContainer(towards = SlideDirection.Left) { offsetForFullSlide ->
-                    offsetForFullSlide / 2
-                }
+                // Keep exiting content in place while sliding in the incoming content.
+                ExitTransition.Hold
         } else {
             // Going from child menu to parent menu, slide towards right.
             // Slide parent by half amount compared to child menu to create an interesting
diff --git a/compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/AnimatedContentTest.kt b/compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/AnimatedContentTest.kt
index 9ef1861..9540bd4 100644
--- a/compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/AnimatedContentTest.kt
+++ b/compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/AnimatedContentTest.kt
@@ -30,6 +30,7 @@
 import androidx.compose.foundation.layout.requiredSize
 import androidx.compose.foundation.layout.size
 import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -600,6 +601,135 @@
         }
     }
 
+    @OptIn(ExperimentalAnimationApi::class)
+    @Test
+    fun testExitHold() {
+        var target by mutableStateOf(true)
+        var box1Disposed = false
+        var box2EnterFinished = false
+        rule.setContent {
+            AnimatedContent(
+                targetState = target,
+                transitionSpec = {
+                    fadeIn(tween(200)) togetherWith
+                        fadeOut(tween(5)) + ExitTransition.Hold
+                }
+            ) {
+                if (it) {
+                    Box(Modifier.size(200.dp)) {
+                        DisposableEffect(key1 = Unit) {
+                            onDispose {
+                                box1Disposed = true
+                            }
+                        }
+                    }
+                } else {
+                    Box(Modifier.size(200.dp)) {
+                        box2EnterFinished =
+                            transition.targetState == transition.currentState &&
+                                transition.targetState == EnterExitState.Visible
+                    }
+                }
+            }
+        }
+
+        rule.waitForIdle()
+        rule.mainClock.autoAdvance = false
+        rule.runOnIdle {
+            target = !target
+        }
+
+        rule.waitForIdle()
+        repeat(10) {
+            rule.mainClock.advanceTimeByFrame()
+            assertFalse(box1Disposed)
+            assertFalse(box2EnterFinished)
+        }
+
+        repeat(10) {
+            rule.mainClock.advanceTimeByFrame()
+            rule.waitForIdle()
+            assertEquals(box1Disposed, box2EnterFinished)
+        }
+
+        assertTrue(box1Disposed)
+        assertTrue(box2EnterFinished)
+    }
+
+    @OptIn(ExperimentalAnimationApi::class)
+    @Test
+    fun testExitHoldDefersUntilAllFinished() {
+        var target by mutableStateOf(true)
+        var box1Disposed = false
+        var box2EnterFinished = false
+        var transitionFinished = false
+        rule.setContent {
+            val outerTransition = updateTransition(targetState = target)
+            transitionFinished = !outerTransition.targetState && !outerTransition.currentState
+            outerTransition.AnimatedContent(
+                transitionSpec = {
+                    fadeIn(tween(160)) togetherWith
+                        fadeOut(tween(5)) + ExitTransition.Hold using
+                        SizeTransform { _, _ ->
+                            tween(300)
+                        }
+                }
+            ) {
+                if (it) {
+                    Box(Modifier.size(200.dp)) {
+                        DisposableEffect(key1 = Unit) {
+                            onDispose {
+                                box1Disposed = true
+                            }
+                        }
+                    }
+                } else {
+                    Box(Modifier.size(400.dp)) {
+                        box2EnterFinished =
+                            transition.targetState == transition.currentState &&
+                                transition.targetState == EnterExitState.Visible
+                    }
+                }
+            }
+        }
+
+        rule.waitForIdle()
+        rule.mainClock.autoAdvance = false
+        rule.runOnIdle {
+            target = !target
+        }
+
+        rule.waitForIdle()
+        rule.mainClock.advanceTimeByFrame()
+        repeat(10) {
+            rule.mainClock.advanceTimeByFrame()
+            assertFalse(box1Disposed)
+            assertFalse(box2EnterFinished)
+        }
+
+        repeat(3) {
+            rule.mainClock.advanceTimeByFrame()
+            rule.waitForIdle()
+            assertTrue(box2EnterFinished)
+            // Enter finished, but box1 is only disposed when the transition is completely finished,
+            // which includes enter, exit & size change.
+            assertFalse(box1Disposed)
+            assertFalse(transitionFinished)
+        }
+
+        repeat(10) {
+            rule.mainClock.advanceTimeByFrame()
+            rule.waitForIdle()
+            assertTrue(box2EnterFinished)
+            // Enter finished, but box1 is only disposed when the transition is completely finished,
+            // which includes enter, exit & size change.
+            assertEquals(box1Disposed, transitionFinished)
+        }
+
+        assertTrue(box1Disposed)
+        assertTrue(box2EnterFinished)
+    }
+
     @OptIn(InternalAnimationApi::class)
     private val Transition<*>.playTimeMillis get() = (playTimeNanos / 1_000_000L).toInt()
 }
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedContent.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedContent.kt
index ae65174..4449080 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedContent.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedContent.kt
@@ -380,6 +380,21 @@
         ),
         targetOffset: (offsetForFullSlide: Int) -> Int = { it }
     ): ExitTransition
+
+    /**
+     * [ExitTransition.Hold] defers the disposal of the exiting content till both enter and
+     * exit animations have finished. It can be combined with other [ExitTransition]s using
+     * [+][ExitTransition.plus].
+     *
+     * **Important**: [ExitTransition.Hold] works the best when the
+     * [zIndex][ContentTransform.targetContentZIndex] for the incoming and outgoing content are
+     * specified. Otherwise, if the content gets interrupted from entering and switching to exiting
+     * using [ExitTransition.Hold], the holding pattern may render exiting content on top of the
+     * entering content, unless the z-order is specified.
+     *
+     * @sample androidx.compose.animation.samples.SlideIntoContainerSample
+     */
+    val ExitTransition.Companion.Hold: ExitTransition get() = Hold
 }
 
 internal class AnimatedContentTransitionScopeImpl<S> internal constructor(
@@ -754,7 +769,8 @@
                 }
                 // TODO: Will need a custom impl of this to: 1) get the signal for when
                 // the animation is finished, 2) get the target size properly
-                AnimatedVisibility(
+                AnimatedEnterExitImpl(
+                    this,
                     { it == stateForContent },
                     enter = specOnEnter.targetContentEnter,
                     exit = exit,
@@ -765,7 +781,12 @@
                                 placeable.place(0, 0, zIndex = specOnEnter.targetContentZIndex)
                             }
                         }
-                        .then(childData.apply { isTarget = stateForContent == targetState })
+                        .then(childData.apply { isTarget = stateForContent == targetState }),
+                    shouldDisposeBlock = { currentState, targetState ->
+                        currentState == EnterExitState.PostExit &&
+                            targetState == EnterExitState.PostExit &&
+                            !exit.data.hold
+                    }
                 ) {
                     // TODO: Should Transition.AnimatedVisibility have an end listener?
                     DisposableEffect(this) {
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedVisibility.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedVisibility.kt
index 2e6a3b2..7e03d6f 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedVisibility.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedVisibility.kt
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+@file:OptIn(ExperimentalAnimationApi::class)
+
 package androidx.compose.animation
 
 import androidx.compose.animation.EnterExitState.PostExit
@@ -31,10 +33,12 @@
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.RowScope
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.key
 import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.produceState
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.runtime.snapshotFlow
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.composed
@@ -50,7 +54,6 @@
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.util.fastForEach
 import androidx.compose.ui.util.fastMaxBy
-import kotlinx.coroutines.flow.collect
 
 /**
  * [AnimatedVisibility] composable animates the appearance and disappearance of its content, as
@@ -126,7 +129,7 @@
     content: @Composable() AnimatedVisibilityScope.() -> Unit
 ) {
     val transition = updateTransition(visible, label)
-    AnimatedEnterExitImpl(transition, { it }, modifier, enter, exit, content)
+    AnimatedEnterExitImpl(transition, { it }, modifier, enter, exit, content = content)
 }
 
 /**
@@ -201,7 +204,7 @@
     content: @Composable() AnimatedVisibilityScope.() -> Unit
 ) {
     val transition = updateTransition(visible, label)
-    AnimatedEnterExitImpl(transition, { it }, modifier, enter, exit, content)
+    AnimatedEnterExitImpl(transition, { it }, modifier, enter, exit, content = content)
 }
 
 /**
@@ -274,7 +277,7 @@
     content: @Composable AnimatedVisibilityScope.() -> Unit
 ) {
     val transition = updateTransition(visible, label)
-    AnimatedEnterExitImpl(transition, { it }, modifier, enter, exit, content)
+    AnimatedEnterExitImpl(transition, { it }, modifier, enter, exit, content = content)
 }
 
 /**
@@ -380,7 +383,7 @@
     content: @Composable() AnimatedVisibilityScope.() -> Unit
 ) {
     val transition = updateTransition(visibleState, label)
-    AnimatedEnterExitImpl(transition, { it }, modifier, enter, exit, content)
+    AnimatedEnterExitImpl(transition, { it }, modifier, enter, exit, content = content)
 }
 
 /**
@@ -455,7 +458,7 @@
     content: @Composable() AnimatedVisibilityScope.() -> Unit
 ) {
     val transition = updateTransition(visibleState, label)
-    AnimatedEnterExitImpl(transition, { it }, modifier, enter, exit, content)
+    AnimatedEnterExitImpl(transition, { it }, modifier, enter, exit, content = content)
 }
 
 /**
@@ -531,7 +534,7 @@
     content: @Composable() AnimatedVisibilityScope.() -> Unit
 ) {
     val transition = updateTransition(visibleState, label)
-    AnimatedEnterExitImpl(transition, { it }, modifier, enter, exit, content)
+    AnimatedEnterExitImpl(transition, { it }, modifier, enter, exit, content = content)
 }
 
 /**
@@ -604,7 +607,7 @@
     enter: EnterTransition = fadeIn() + expandIn(),
     exit: ExitTransition = shrinkOut() + fadeOut(),
     content: @Composable() AnimatedVisibilityScope.() -> Unit
-) = AnimatedEnterExitImpl(this, visible, modifier, enter, exit, content)
+) = AnimatedEnterExitImpl(this, visible, modifier, enter, exit, content = content)
 
 /**
  * This is the scope for the content of [AnimatedVisibility]. In this scope, direct and
@@ -721,68 +724,62 @@
 @OptIn(
     ExperimentalTransitionApi::class,
     InternalAnimationApi::class,
-    ExperimentalAnimationApi::class
+    ExperimentalAnimationApi::class,
 )
 @Composable
-private fun <T> AnimatedEnterExitImpl(
+internal fun <T> AnimatedEnterExitImpl(
     transition: Transition<T>,
     visible: (T) -> Boolean,
     modifier: Modifier,
     enter: EnterTransition,
     exit: ExitTransition,
+    shouldDisposeBlock: (EnterExitState, EnterExitState) -> Boolean = { current, target ->
+        current == target && target == PostExit
+    },
     content: @Composable() AnimatedVisibilityScope.() -> Unit
 ) {
-    val isAnimationVisible = remember(transition) {
-        mutableStateOf(visible(transition.currentState))
-    }
-
-    if (visible(transition.targetState) || isAnimationVisible.value || transition.isSeeking) {
+    if (visible(transition.targetState) || visible(transition.currentState) ||
+        transition.isSeeking
+    ) {
         val childTransition = transition.createChildTransition(label = "EnterExitTransition") {
             transition.targetEnterExit(visible, it)
         }
 
-        LaunchedEffect(childTransition) {
+        val shouldDisposeBlockUpdated by rememberUpdatedState(shouldDisposeBlock)
+
+        val shouldDisposeAfterExit by produceState(
+            initialValue = shouldDisposeBlock(
+                childTransition.currentState,
+                childTransition.targetState
+            )
+        ) {
             snapshotFlow {
-                childTransition.currentState == EnterExitState.Visible ||
-                    childTransition.targetState == EnterExitState.Visible
+                childTransition.exitFinished
             }.collect {
-                isAnimationVisible.value = it
+                value = if (it) {
+                    shouldDisposeBlockUpdated(
+                        childTransition.currentState,
+                        childTransition.targetState
+                    )
+                } else {
+                    false
+                }
             }
         }
 
-        AnimatedEnterExitImpl(
-            childTransition,
-            modifier,
-            enter = enter,
-            exit = exit,
-            content = content
-        )
+        if (!childTransition.exitFinished || !shouldDisposeAfterExit) {
+            val scope = remember(transition) { AnimatedVisibilityScopeImpl(childTransition) }
+            Layout(
+                content = { scope.content() },
+                modifier = modifier.then(childTransition.createModifier(enter, exit, "Built-in")),
+                measurePolicy = remember { AnimatedEnterExitMeasurePolicy(scope) }
+            )
+        }
     }
 }
 
-@ExperimentalAnimationApi
-@Composable
-private inline fun AnimatedEnterExitImpl(
-    transition: Transition<EnterExitState>,
-    modifier: Modifier,
-    enter: EnterTransition,
-    exit: ExitTransition,
-    content: @Composable AnimatedVisibilityScope.() -> Unit
-) {
-    // TODO: Get some feedback on whether there's a need to observe this state change in user
-    //  code. If there is, this if check will need to be moved to measure stage, along with some
-    //  structural changes.
-    if (transition.currentState == EnterExitState.Visible ||
-        transition.targetState == EnterExitState.Visible
-    ) {
-        val scope = remember(transition) { AnimatedVisibilityScopeImpl(transition) }
-        Layout(
-            content = { scope.content() },
-            modifier = modifier.then(transition.createModifier(enter, exit, "Built-in")),
-            measurePolicy = remember { AnimatedEnterExitMeasurePolicy(scope) }
-        )
-    }
-}
+private val Transition<EnterExitState>.exitFinished
+    get() = currentState == PostExit && targetState == PostExit
 
 @OptIn(ExperimentalAnimationApi::class)
 private class AnimatedEnterExitMeasurePolicy(
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/DefaultDecayAnimationSpec.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/DefaultDecayAnimationSpec.kt
index 2b156e6..6dcda11 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/DefaultDecayAnimationSpec.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/DefaultDecayAnimationSpec.kt
@@ -25,4 +25,5 @@
 @Composable
 @Deprecated("Replace with rememberSplineBasedDecay<Float>")
 expect fun defaultDecayAnimationSpec(): DecayAnimationSpec<Float>
+@Composable
 expect fun <T> rememberSplineBasedDecay(): DecayAnimationSpec<T>
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/EnterExitTransition.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/EnterExitTransition.kt
index b204bed..e9d8ea9 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/EnterExitTransition.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/EnterExitTransition.kt
@@ -197,7 +197,8 @@
                 fade = data.fade ?: exit.data.fade,
                 slide = data.slide ?: exit.data.slide,
                 changeSize = data.changeSize ?: exit.data.changeSize,
-                scale = data.scale ?: exit.data.scale
+                scale = data.scale ?: exit.data.scale,
+                hold = data.hold || exit.data.hold
             )
         )
     }
@@ -207,13 +208,14 @@
     }
 
     override fun toString(): String =
-        if (this == None) {
-            "ExitTransition.None"
-        } else {
-            data.run {
+        when (this) {
+            None -> "ExitTransition.None"
+            Hold -> "ExitTransition.Hold"
+            else -> data.run {
                 "ExitTransition: \n" + "Fade - " + fade?.toString() + ",\nSlide - " +
                     slide?.toString() + ",\nShrink - " + changeSize?.toString() +
-                    ",\nScale - " + scale?.toString()
+                    ",\nScale - " + scale?.toString() +
+                    ",\nHold - " + hold
             }
         }
 
@@ -232,6 +234,14 @@
          * @sample androidx.compose.animation.samples.AVScopeAnimateEnterExit
          */
         val None: ExitTransition = ExitTransitionImpl(TransitionData())
+
+        /**
+         * Keep this type of exit transition internal and only expose it in AnimatedContent, as
+         * holding only makes sense when there's enter and exit at the same time. In other words,
+         * when dealing with one set of content entering OR exiting, such as AnimatedVisibility,
+         * holding would not be meaningful.
+         */
+        internal val Hold: ExitTransition = ExitTransitionImpl(TransitionData(hold = true))
     }
 }
 
@@ -811,7 +821,8 @@
     val fade: Fade? = null,
     val slide: Slide? = null,
     val changeSize: ChangeSize? = null,
-    val scale: Scale? = null
+    val scale: Scale? = null,
+    val hold: Boolean = false
 )
 
 @OptIn(ExperimentalAnimationApi::class, InternalAnimationApi::class)
@@ -823,17 +834,30 @@
     label: String
 ): Modifier {
 
-    var shouldAnimateSlide by remember(this) { mutableStateOf(false) }
-    var shouldAnimateSizeChange by remember(this) { mutableStateOf(false) }
-    // Animate if the enter or exit transition for the type is defined. Once the shouldAnimateFoo
-    // is set, it'll stay true until the transition is complete.  This would ensure the removal of
-    // any of type animation in the enter/exit amid a transition doesn't result in a
-    // jump. Reset shouldAnimateFoo to false when the transition is finished.
-    val isTransitioning = currentState != targetState || isSeeking
-    shouldAnimateSlide = isTransitioning &&
-        (shouldAnimateSlide || enter.data.slide != null || exit.data.slide != null)
-    shouldAnimateSizeChange = isTransitioning &&
-        (shouldAnimateSizeChange || enter.data.changeSize != null || exit.data.changeSize != null)
+    // Active enter & active exit reference the enter and exit transition that is currently being
+    // used. It is important to preserve the active enter/exit that was previously used before
+    // changing target state, such that if the previous enter/exit is interrupted, we still hold
+    // reference to the enter/exit that define those animations and therefore could recover.
+    var activeEnter by remember(this) { mutableStateOf(enter) }
+    var activeExit by remember(this) { mutableStateOf(exit) }
+    if (currentState == targetState && currentState == EnterExitState.Visible) {
+        if (isSeeking) {
+            // When seeking, the timing is different and there's no need to handle interruptions.
+            activeEnter = enter
+            activeExit = exit
+        } else {
+            activeEnter = EnterTransition.None
+            activeExit = ExitTransition.None
+        }
+    } else if (targetState == EnterExitState.Visible) {
+        activeEnter += enter
+    } else {
+        activeExit += exit
+    }
+
+    val shouldAnimateSlide = activeEnter.data.slide != null || activeExit.data.slide != null
+    val shouldAnimateSizeChange =
+        activeEnter.data.changeSize != null || activeExit.data.changeSize != null
 
     val slideAnimation = if (shouldAnimateSlide) {
         createDeferredAnimation(IntOffset.VectorConverter, remember { "$label slide" })
@@ -851,17 +875,17 @@
         )
     } else null
 
-    val disableClip = (enter.data.changeSize?.clip == false ||
-        exit.data.changeSize?.clip == false) || !shouldAnimateSizeChange
+    val disableClip = (activeEnter.data.changeSize?.clip == false ||
+        activeExit.data.changeSize?.clip == false) || !shouldAnimateSizeChange
 
-    val graphicsLayerBlock = createGraphicsLayerBlock(enter, exit, label)
+    val graphicsLayerBlock = createGraphicsLayerBlock(activeEnter, activeExit, label)
 
     return Modifier
         .graphicsLayer(clip = !disableClip)
         .then(
             EnterExitTransitionElement(
                 this, sizeAnimation, offsetAnimation, slideAnimation,
-                enter, exit, graphicsLayerBlock
+                activeEnter, activeExit, graphicsLayerBlock
             )
         )
 }
@@ -873,14 +897,8 @@
     label: String
 ): GraphicsLayerScope.() -> Unit {
 
-    var shouldAnimateAlpha by remember(this) { mutableStateOf(false) }
-    var shouldAnimateScale by remember(this) { mutableStateOf(false) }
-
-    val isTransitioning = currentState != targetState || isSeeking
-    shouldAnimateAlpha = isTransitioning &&
-        (shouldAnimateAlpha || enter.data.fade != null || exit.data.fade != null)
-    shouldAnimateScale = isTransitioning &&
-        (shouldAnimateScale || enter.data.scale != null || exit.data.scale != null)
+    val shouldAnimateAlpha = enter.data.fade != null || exit.data.fade != null
+    val shouldAnimateScale = enter.data.scale != null || exit.data.scale != null
 
     // Fade - it's important to put fade in the end. Otherwise fade will clip slide.
     // We'll animate if at any point during the transition fadeIn/fadeOut becomes non-null. This
diff --git a/compose/compiler/compiler-daemon/build.gradle b/compose/compiler/compiler-daemon/build.gradle
index 33ee2f7..c1dc0cf 100644
--- a/compose/compiler/compiler-daemon/build.gradle
+++ b/compose/compiler/compiler-daemon/build.gradle
@@ -40,6 +40,10 @@
     archiveBaseName = 'kotlin-compiler-daemon'
 }
 
+project.tasks.named("shadowJar").configure { shadowJar ->
+    shadowJar.outputs.doNotCacheIf("Restoring from cache often takes longer", { true })
+}
+
 androidx {
     name = "Compose Compiler Daemon"
     type = LibraryType.COMPILER_DAEMON
diff --git a/compose/compiler/compiler-daemon/src/main/kotlin/androidx/compose/compiler/daemon/Compiler.kt b/compose/compiler/compiler-daemon/src/main/kotlin/androidx/compose/compiler/daemon/Compiler.kt
index be43334..b73e34e 100644
--- a/compose/compiler/compiler-daemon/src/main/kotlin/androidx/compose/compiler/daemon/Compiler.kt
+++ b/compose/compiler/compiler-daemon/src/main/kotlin/androidx/compose/compiler/daemon/Compiler.kt
@@ -32,7 +32,7 @@
 import org.jetbrains.kotlin.incremental.ClasspathSnapshotFiles
 import org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner
 import org.jetbrains.kotlin.incremental.multiproject.EmptyModulesApiHistory
-import org.jetbrains.kotlin.incremental.withIC
+import org.jetbrains.kotlin.incremental.withIncrementalCompilation
 
 internal fun parseArgs(
     args: Array<String>,
@@ -115,7 +115,7 @@
         return try {
             val compilerArgs = parseArgs(args, daemonCompilerSettings.composePluginPath)
             compilerArgs.moduleName = "test"
-            withIC(compilerArgs) {
+            withIncrementalCompilation(compilerArgs) {
                 compiler.compile(
                     compilerArgs.freeArgs.map { File(it) },
                     compilerArgs,
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractCodegenSignatureTest.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractCodegenSignatureTest.kt
index dee3137..de9aca9 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractCodegenSignatureTest.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractCodegenSignatureTest.kt
@@ -60,7 +60,7 @@
         .replace('$', '%') // replace $ to % to make comparing it to kotlin string literals easier
 }
 
-abstract class AbstractCodegenSignatureTest : AbstractCodegenTest(useFir = false) {
+abstract class AbstractCodegenSignatureTest(useFir: Boolean) : AbstractCodegenTest(useFir) {
     private fun OutputFile.printApi(): String {
         return printPublicApi(asText(), relativePath)
     }
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractCodegenTest.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractCodegenTest.kt
index e461caf..82514bd 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractCodegenTest.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractCodegenTest.kt
@@ -90,13 +90,26 @@
     }
 
     protected fun classLoader(
+        platformSources: Map<String, String>,
+        commonSources: Map<String, String>,
+        dumpClasses: Boolean = false
+    ): GeneratedClassLoader {
+        val loader = createClassLoader(
+            platformSources.map { (fileName, source) -> SourceFile(fileName, source) },
+            commonSources.map { (fileName, source) -> SourceFile(fileName, source) }
+        )
+        if (dumpClasses) dumpClasses(loader)
+        return loader
+    }
+
+    protected fun classLoader(
         sources: Map<String, String>,
         additionalPaths: List<File>,
         dumpClasses: Boolean = false
     ): GeneratedClassLoader {
         val loader = createClassLoader(
             sources.map { (fileName, source) -> SourceFile(fileName, source) },
-            additionalPaths
+            additionalPaths = additionalPaths
         )
         if (dumpClasses) dumpClasses(loader)
         return loader
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractCompilerTest.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractCompilerTest.kt
index 9169b38..af87416 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractCompilerTest.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractCompilerTest.kt
@@ -24,13 +24,17 @@
 import com.intellij.openapi.util.io.FileUtil
 import java.io.File
 import java.net.URLClassLoader
-import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
 import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
 import org.jetbrains.kotlin.cli.jvm.config.configureJdkClasspathRoots
 import org.jetbrains.kotlin.codegen.GeneratedClassLoader
-import org.jetbrains.kotlin.config.CommonConfigurationKeys
+import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
+import org.jetbrains.kotlin.compiler.plugin.registerExtensionsForTest
+import org.jetbrains.kotlin.config.ApiVersion
 import org.jetbrains.kotlin.config.CompilerConfiguration
 import org.jetbrains.kotlin.config.JVMConfigurationKeys
+import org.jetbrains.kotlin.config.LanguageVersion
+import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
+import org.jetbrains.kotlin.config.languageVersionSettings
 import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
 import org.junit.After
 import org.junit.BeforeClass
@@ -104,14 +108,20 @@
 
     protected open fun CompilerConfiguration.updateConfiguration() {}
 
+    @OptIn(ExperimentalCompilerApi::class)
     private fun createCompilerFacade(
         additionalPaths: List<File> = listOf(),
         registerExtensions: (Project.(CompilerConfiguration) -> Unit)? = null
     ) = KotlinCompilerFacade.create(
         testRootDisposable,
         updateConfiguration = {
+            val languageVersion =
+                if (useFir) LanguageVersion.KOTLIN_2_0 else LanguageVersion.KOTLIN_1_9
+            languageVersionSettings = LanguageVersionSettingsImpl(
+                languageVersion,
+                ApiVersion.createByLanguageVersion(languageVersion),
+            )
             updateConfiguration()
-            put(CommonConfigurationKeys.USE_FIR, useFir)
             addJvmClasspathRoots(additionalPaths)
             addJvmClasspathRoots(defaultClassPathRoots)
             if (!getBoolean(JVMConfigurationKeys.NO_JDK) &&
@@ -122,16 +132,19 @@
             configureJdkClasspathRoots()
         },
         registerExtensions = registerExtensions ?: { configuration ->
-            ComposeComponentRegistrar.registerCommonExtensions(this)
-            IrGenerationExtension.registerExtension(
-                this,
-                ComposeComponentRegistrar.createComposeIrExtension(configuration)
-            )
+            registerExtensionsForTest(this, configuration) {
+                with(ComposePluginRegistrar()) {
+                    registerExtensions(it)
+                }
+            }
         }
     )
 
-    protected fun analyze(sourceFiles: List<SourceFile>): AnalysisResult =
-        createCompilerFacade().analyze(sourceFiles)
+    protected fun analyze(
+        platformSources: List<SourceFile>,
+        commonSources: List<SourceFile> = listOf()
+    ): AnalysisResult =
+        createCompilerFacade().analyze(platformSources, commonSources)
 
     protected fun compileToIr(
         sourceFiles: List<SourceFile>,
@@ -141,7 +154,8 @@
         createCompilerFacade(additionalPaths, registerExtensions).compileToIr(sourceFiles)
 
     protected fun createClassLoader(
-        sourceFiles: List<SourceFile>,
+        platformSourceFiles: List<SourceFile>,
+        commonSourceFiles: List<SourceFile> = listOf(),
         additionalPaths: List<File> = listOf()
     ): GeneratedClassLoader {
         val classLoader = URLClassLoader(
@@ -151,7 +165,8 @@
             null
         )
         return GeneratedClassLoader(
-            createCompilerFacade(additionalPaths).compile(sourceFiles).factory,
+            createCompilerFacade(additionalPaths)
+                .compile(platformSourceFiles, commonSourceFiles).factory,
             classLoader
         )
     }
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractComposeDiagnosticsTest.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractComposeDiagnosticsTest.kt
index 54c9f7c..84ac5fd 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractComposeDiagnosticsTest.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractComposeDiagnosticsTest.kt
@@ -16,49 +16,70 @@
 
 package androidx.compose.compiler.plugins.kotlin
 
+import androidx.compose.compiler.plugins.kotlin.facade.AnalysisResult
 import androidx.compose.compiler.plugins.kotlin.facade.SourceFile
-import org.jetbrains.kotlin.checkers.DiagnosedRange
 import org.jetbrains.kotlin.checkers.utils.CheckerTestUtil
 import org.jetbrains.kotlin.utils.addToStdlib.flatGroupBy
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertThrows
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
-@RunWith(JUnit4::class)
-abstract class AbstractComposeDiagnosticsTest : AbstractCompilerTest(useFir = false) {
-    protected fun check(expectedText: String, ignoreParseErrors: Boolean = false) {
-        val diagnosedRanges: MutableList<DiagnosedRange> = ArrayList()
-        val clearText = CheckerTestUtil.parseDiagnosedRanges(expectedText, diagnosedRanges)
+abstract class AbstractComposeDiagnosticsTest(useFir: Boolean) : AbstractCompilerTest(useFir) {
+    protected fun check(
+        expectedText: String,
+        commonText: String? = null,
+        ignoreParseErrors: Boolean = false
+    ) {
+        val clearText = CheckerTestUtil.parseDiagnosedRanges(expectedText, mutableListOf())
+        val clearCommonText = commonText?.let {
+            CheckerTestUtil.parseDiagnosedRanges(commonText, mutableListOf())
+        }
 
         val errors = analyze(
-            listOf(SourceFile("test.kt", clearText, ignoreParseErrors))
+            listOf(SourceFile("test.kt", clearText, ignoreParseErrors)),
+            listOfNotNull(clearCommonText?.let { SourceFile("common.kt", it, ignoreParseErrors) }),
         ).diagnostics
 
-        val rangeToDiagnostics = errors.flatGroupBy { it.textRanges }.mapValues { entry ->
-            entry.value.map { it.factoryName }.toSet()
+        checkDiagnostics(expectedText, clearText, errors["test.kt"])
+        if (clearCommonText != null) {
+            checkDiagnostics(commonText, clearCommonText, errors["common.kt"])
         }
-        val startOffsetToGroups = rangeToDiagnostics.entries.groupBy(
-            keySelector = { it.key.startOffset },
-            valueTransform = { it.value }
-        )
-        val endOffsetsToGroups = rangeToDiagnostics.entries.groupBy(
-            keySelector = { it.key.endOffset },
-            valueTransform = { it.value }
-        )
+    }
 
-        val annotatedText = buildString {
-            for ((i, c) in clearText.withIndex()) {
-                endOffsetsToGroups[i]?.let { groups ->
-                    repeat(groups.size) { append("<!>") }
+    private fun checkDiagnostics(
+        expectedText: String,
+        clearText: String,
+        allDiagnostics: List<AnalysisResult.Diagnostic>?
+    ) {
+        val annotatedText = if (allDiagnostics != null) {
+            val rangeToDiagnostics = allDiagnostics
+                .flatGroupBy { it.textRanges }
+                .mapValues { entry ->
+                    entry.value.map { it.factoryName }.toSet()
                 }
-                startOffsetToGroups[i]?.let { groups ->
-                    for (diagnostics in groups) {
-                        append("<!${diagnostics.joinToString(",")}!>")
+            val startOffsetToGroups = rangeToDiagnostics.entries.groupBy(
+                keySelector = { it.key.startOffset },
+                valueTransform = { it.value }
+            )
+            val endOffsetsToGroups = rangeToDiagnostics.entries.groupBy(
+                keySelector = { it.key.endOffset },
+                valueTransform = { it.value }
+            )
+
+            buildString {
+                for ((i, c) in clearText.withIndex()) {
+                    endOffsetsToGroups[i]?.let { groups ->
+                        repeat(groups.size) { append("<!>") }
                     }
+                    startOffsetToGroups[i]?.let { groups ->
+                        for (diagnostics in groups) {
+                            append("<!${diagnostics.joinToString(",")}!>")
+                        }
+                    }
+                    append(c)
                 }
-                append(c)
             }
+        } else {
+            clearText
         }
 
         assertEquals(expectedText, annotatedText)
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractControlFlowTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractControlFlowTransformTests.kt
index ab590e2..f62ba3a 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractControlFlowTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractControlFlowTransformTests.kt
@@ -17,11 +17,10 @@
 package androidx.compose.compiler.plugins.kotlin
 
 import org.intellij.lang.annotations.Language
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
-@RunWith(JUnit4::class)
-abstract class AbstractControlFlowTransformTests : AbstractIrTransformTest(useFir = false) {
+abstract class AbstractControlFlowTransformTests(
+    useFir: Boolean
+) : AbstractIrTransformTest(useFir) {
     protected fun controlFlow(
         @Language("kotlin")
         source: String,
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt
index b8c6809..33f2bb3 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt
@@ -82,7 +82,7 @@
         val actualTransformed = irModule
             .files[0]
             .validate()
-            .dumpSrc()
+            .dumpSrc(useFir)
             .replace('$', '%')
             // replace source keys for start group calls
             .replace(
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractLiveLiteralTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractLiveLiteralTransformTests.kt
index 2bdd293..567728e 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractLiveLiteralTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractLiveLiteralTransformTests.kt
@@ -22,14 +22,16 @@
 import org.intellij.lang.annotations.Language
 import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
 import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
+import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
+import org.jetbrains.kotlin.compiler.plugin.registerExtensionsForTest
 import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
 import org.jetbrains.kotlin.ir.util.DeepCopySymbolRemapper
 import org.junit.Assert.assertEquals
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
-@RunWith(JUnit4::class)
-abstract class AbstractLiveLiteralTransformTests : AbstractIrTransformTest(useFir = false) {
+abstract class AbstractLiveLiteralTransformTests(
+    useFir: Boolean
+) : AbstractIrTransformTest(useFir) {
+    @OptIn(ExperimentalCompilerApi::class)
     private fun computeKeys(files: List<SourceFile>): List<String> {
         var builtKeys = mutableSetOf<String>()
         compileToIr(
@@ -41,29 +43,34 @@
                 val liveLiteralsV2Enabled = configuration.getBoolean(
                     ComposeConfiguration.LIVE_LITERALS_V2_ENABLED_KEY
                 )
-                ComposeComponentRegistrar.registerCommonExtensions(this)
-                IrGenerationExtension.registerExtension(this, object : IrGenerationExtension {
-                    override fun generate(
-                        moduleFragment: IrModuleFragment,
-                        pluginContext: IrPluginContext
-                    ) {
-                        val symbolRemapper = DeepCopySymbolRemapper()
-                        val keyVisitor = DurableKeyVisitor(builtKeys)
-                        val transformer = object : LiveLiteralTransformer(
-                            liveLiteralsEnabled || liveLiteralsV2Enabled,
-                            liveLiteralsV2Enabled,
-                            keyVisitor,
-                            pluginContext,
-                            symbolRemapper,
-                            ModuleMetricsImpl("temp")
-                        ) {
-                            override fun makeKeySet(): MutableSet<String> {
-                                return super.makeKeySet().also { builtKeys = it }
+                registerExtensionsForTest(this, configuration) {
+                    with(ComposePluginRegistrar) { registerCommonExtensions() }
+                    IrGenerationExtension.registerExtension(
+                        this@compileToIr,
+                        object : IrGenerationExtension {
+                            override fun generate(
+                                moduleFragment: IrModuleFragment,
+                                pluginContext: IrPluginContext
+                            ) {
+                                val symbolRemapper = DeepCopySymbolRemapper()
+                                val keyVisitor = DurableKeyVisitor(builtKeys)
+                                val transformer = object : LiveLiteralTransformer(
+                                    liveLiteralsEnabled || liveLiteralsV2Enabled,
+                                    liveLiteralsV2Enabled,
+                                    keyVisitor,
+                                    pluginContext,
+                                    symbolRemapper,
+                                    ModuleMetricsImpl("temp")
+                                ) {
+                                    override fun makeKeySet(): MutableSet<String> {
+                                        return super.makeKeySet().also { builtKeys = it }
+                                    }
+                                }
+                                transformer.lower(moduleFragment)
                             }
                         }
-                        transformer.lower(moduleFragment)
-                    }
-                })
+                    )
+                }
             }
         )
         return builtKeys.toList()
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractMetricsTransformTest.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractMetricsTransformTest.kt
index 44c13ea..9e5d7ff 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractMetricsTransformTest.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractMetricsTransformTest.kt
@@ -19,9 +19,12 @@
 import androidx.compose.compiler.plugins.kotlin.facade.KotlinCompilerFacade
 import androidx.compose.compiler.plugins.kotlin.facade.SourceFile
 import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
+import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
+import org.jetbrains.kotlin.compiler.plugin.registerExtensionsForTest
 import org.junit.Assert.assertEquals
 
 abstract class AbstractMetricsTransformTest(useFir: Boolean) : AbstractIrTransformTest(useFir) {
+    @OptIn(ExperimentalCompilerApi::class)
     private fun verifyMetrics(
         source: String,
         verify: ModuleMetrics.() -> Unit
@@ -31,10 +34,12 @@
         compileToIr(
             files,
             registerExtensions = { configuration ->
-                ComposeComponentRegistrar.registerCommonExtensions(this)
-                val extension = ComposeComponentRegistrar.createComposeIrExtension(configuration)
-                extension.metrics = metrics
-                IrGenerationExtension.registerExtension(this, extension)
+                registerExtensionsForTest(this, configuration) {
+                    with(ComposePluginRegistrar) { registerCommonExtensions() }
+                    val extension = ComposePluginRegistrar.createComposeIrExtension(configuration)
+                    extension.metrics = metrics
+                    IrGenerationExtension.registerExtension(extension)
+                }
             }
         )
         metrics.verify()
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ClassStabilityTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ClassStabilityTransformTests.kt
index a2fb1f9..7183a6a 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ClassStabilityTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ClassStabilityTransformTests.kt
@@ -29,11 +29,8 @@
 import org.jetbrains.kotlin.ir.util.statements
 import org.junit.Assert.assertEquals
 import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
-@RunWith(JUnit4::class)
-class ClassStabilityTransformTests : AbstractIrTransformTest(useFir = false) {
+class ClassStabilityTransformTests(useFir: Boolean) : AbstractIrTransformTest(useFir) {
     @Test
     fun testEmptyClassIsStable() = assertStability(
         "class Foo",
@@ -951,7 +948,7 @@
                   return <this>.p1%delegate.getValue(<this>, ::p1)
                 }
                 set(value) {
-                  return <this>.p1%delegate.setValue(<this>, ::p1, <set-?>)
+                  <this>.p1%delegate.setValue(<this>, ::p1, <set-?>)
                 }
               static val %stable: Int = 0
             }
@@ -962,7 +959,7 @@
                   return <this>.p1%delegate.getValue(<this>, ::p1)
                 }
                 set(value) {
-                  return <this>.p1%delegate.setValue(<this>, ::p1, <set-?>)
+                  <this>.p1%delegate.setValue(<this>, ::p1, <set-?>)
                 }
               static val %stable: Int = 8
             }
@@ -1172,7 +1169,7 @@
                   return <this>.p1%delegate.getValue(<this>, ::p1)
                 }
                 set(value) {
-                  return <this>.p1%delegate.setValue(<this>, ::p1, <set-?>)
+                  <this>.p1%delegate.setValue(<this>, ::p1, <set-?>)
                 }
               static val %stable: Int = 0
             }
@@ -1183,7 +1180,7 @@
                   return <this>.p1%delegate.getValue(<this>, ::p1)
                 }
                 set(value) {
-                  return <this>.p1%delegate.setValue(<this>, ::p1, <set-?>)
+                  <this>.p1%delegate.setValue(<this>, ::p1, <set-?>)
                 }
               static val %stable: Int = UnstableDelegate.%stable
             }
@@ -1305,9 +1302,9 @@
               if (isTraceInProgress()) {
                 traceEventStart(<>, %dirty, -1, <>)
               }
-              val tmp0_iterator = items.iterator()
-              while (tmp0_iterator.hasNext()) {
-                val item = tmp0_iterator.next()
+              val <iterator> = items.iterator()
+              while (<iterator>.hasNext()) {
+                val item = <iterator>.next()
                 itemContent(item, %composer, 0b01110000 and %dirty)
               }
               if (isTraceInProgress()) {
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposeCallLoweringTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposeCallLoweringTests.kt
index c96a15e..a4adf37 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposeCallLoweringTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposeCallLoweringTests.kt
@@ -26,16 +26,22 @@
 import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.robolectric.RobolectricTestRunner
+import org.robolectric.ParameterizedRobolectricTestRunner
 import org.robolectric.annotation.Config
 
-@RunWith(RobolectricTestRunner::class)
+@RunWith(ParameterizedRobolectricTestRunner::class)
 @Config(
     manifest = Config.NONE,
     minSdk = 23,
     maxSdk = 23
 )
-class ComposeCallLoweringTests : AbstractLoweringTests(useFir = false) {
+class ComposeCallLoweringTests(useFir: Boolean) : AbstractLoweringTests(useFir) {
+    companion object {
+        @JvmStatic
+        @ParameterizedRobolectricTestRunner.Parameters(name = "useFir = {0}")
+        fun data() = arrayOf<Any>(false, true)
+    }
+
     @Test
     @Ignore("b/173733968")
     fun testInlineGroups() {
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposerParamSignatureTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposerParamSignatureTests.kt
index bc52879..f476366 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposerParamSignatureTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposerParamSignatureTests.kt
@@ -21,17 +21,23 @@
 import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.robolectric.RobolectricTestRunner
+import org.robolectric.ParameterizedRobolectricTestRunner
 import org.robolectric.annotation.Config
 
 /* ktlint-disable max-line-length */
-@RunWith(RobolectricTestRunner::class)
+@RunWith(ParameterizedRobolectricTestRunner::class)
 @Config(
     manifest = Config.NONE,
     minSdk = 23,
     maxSdk = 23
 )
-class ComposerParamSignatureTests : AbstractCodegenSignatureTest() {
+class ComposerParamSignatureTests(useFir: Boolean) : AbstractCodegenSignatureTest(useFir) {
+    companion object {
+        @JvmStatic
+        @ParameterizedRobolectricTestRunner.Parameters(name = "useFir = {0}")
+        fun data() = arrayOf<Any>(false, true)
+    }
+
     @Test
     fun testParameterlessChildrenLambdasReused() = checkApi(
         """
@@ -99,6 +105,21 @@
     }
 
     @Test
+    fun testComposableLambdaCall() = validateBytecode(
+        """
+            @Composable
+            fun Foo(f: @Composable () -> Unit) {
+              f()
+            }
+        """
+    ) {
+        // Calls to a composable lambda needs to invoke the `Function2.invoke` interface method
+        // taking two objects and *not* directly the `invoke` method that takes a Composer and
+        // an unboxed int.
+        assertTrue(it.contains("INVOKEINTERFACE kotlin/jvm/functions/Function2.invoke (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; (itf)"))
+    }
+
+    @Test
     fun testStrangeReceiverIssue() = codegen(
         """
         import androidx.compose.runtime.ExplicitGroupsComposable
@@ -1450,15 +1471,7 @@
             public final class TestKt {
               public final static Example(LA;)V
               public final static Usage()V
-              final static INNERCLASS TestKt%Usage%1 null null
-            }
-            final class TestKt%Usage%1 implements A {
-              <init>()V
-              public final compute(I)V
-              static <clinit>()V
-              public final static LTestKt%Usage%1; INSTANCE
-              OUTERCLASS TestKt Usage ()V
-              final static INNERCLASS TestKt%Usage%1 null null
+              private final static Usage%lambda%0(I)V
             }
         """
     )
@@ -1504,6 +1517,113 @@
     )
 
     @Test
+    fun testFunInterfacesInComposableCall() = checkApi(
+        """
+            fun interface MeasurePolicy {
+                fun compute(value: Int): Unit
+            }
+
+            @NonRestartableComposable
+            @Composable fun Text() {
+                Layout { value ->
+                    println(value)
+                }
+            }
+
+            @Composable inline fun Layout(policy: MeasurePolicy) {
+                policy.compute(0)
+            }
+        """,
+        """
+            public abstract interface MeasurePolicy {
+              public abstract compute(I)V
+            }
+            public final class TestKt {
+              public final static Text(Landroidx/compose/runtime/Composer;I)V
+              public final static Layout(LMeasurePolicy;Landroidx/compose/runtime/Composer;I)V
+              private final static Text%lambda%0(I)V
+            }
+        """,
+    )
+
+    @Test
+        fun testComposableFunInterfacesInVariance() = checkApi(
+        """
+           import androidx.compose.runtime.*
+
+            fun interface Consumer<T> {
+                @Composable fun consume(t: T)
+            }
+
+            class Repro<T : Any>() {
+                fun test(consumer: Consumer<in T>) {}
+            }
+
+            fun test() {
+                Repro<String>().test { string ->
+                    println(string)
+                }
+            }
+        """,
+        """
+            public abstract interface Consumer {
+              public abstract consume(Ljava/lang/Object;Landroidx/compose/runtime/Composer;I)V
+            }
+            public final class Repro {
+              public <init>()V
+              public final test(LConsumer;)V
+              static <clinit>()V
+              public final static I %stable
+            }
+            public final class TestKt {
+              public final static test()V
+              final static INNERCLASS TestKt%test%1 null null
+            }
+            final class TestKt%test%1 implements Consumer {
+              <init>()V
+              public final consume(Ljava/lang/String;Landroidx/compose/runtime/Composer;I)V
+              public synthetic bridge consume(Ljava/lang/Object;Landroidx/compose/runtime/Composer;I)V
+              static <clinit>()V
+              public final static LTestKt%test%1; INSTANCE
+              OUTERCLASS TestKt test ()V
+              final static INNERCLASS TestKt%test%1 null null
+              final static INNERCLASS TestKt%test%1%consume%1 null null
+            }
+            final class TestKt%test%1%consume%1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function2 {
+              <init>(LTestKt%test%1;Ljava/lang/String;I)V
+              public final invoke(Landroidx/compose/runtime/Composer;I)V
+              public synthetic bridge invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+              final synthetic LTestKt%test%1; %tmp0_rcvr
+              final synthetic Ljava/lang/String; %string
+              final synthetic I %%changed
+              OUTERCLASS TestKt%test%1 consume (Ljava/lang/String;Landroidx/compose/runtime/Composer;I)V
+              final static INNERCLASS TestKt%test%1 null null
+              final static INNERCLASS TestKt%test%1%consume%1 null null
+            }
+        """
+    )
+
+    val hashCodeEqualsAndToString = if (useFir) {
+        """
+              public static equals-impl(ILjava/lang/Object;)Z
+              public equals(Ljava/lang/Object;)Z
+              public static hashCode-impl(I)I
+              public hashCode()I
+              public static toString-impl(I)Ljava/lang/String;
+              public toString()Ljava/lang/String;
+        """
+    } else {
+        """
+              public static toString-impl(I)Ljava/lang/String;
+              public toString()Ljava/lang/String;
+              public static hashCode-impl(I)I
+              public hashCode()I
+              public static equals-impl(ILjava/lang/Object;)Z
+              public equals(Ljava/lang/Object;)Z
+        """
+    }
+
+    @Test
     fun testFunInterfaceWithInlineReturnType() = checkApi(
         """
             inline class Color(val value: Int)
@@ -1517,12 +1637,7 @@
         """
             public final class Color {
               public final getValue()I
-              public static toString-impl(I)Ljava/lang/String;
-              public toString()Ljava/lang/String;
-              public static hashCode-impl(I)I
-              public hashCode()I
-              public static equals-impl(ILjava/lang/Object;)Z
-              public equals(Ljava/lang/Object;)Z
+              $hashCodeEqualsAndToString
               private synthetic <init>(I)V
               public static constructor-impl(I)I
               public final static synthetic box-impl(I)LColor;
@@ -1562,12 +1677,7 @@
         """
             public final class Color {
               public final getValue()I
-              public static toString-impl(I)Ljava/lang/String;
-              public toString()Ljava/lang/String;
-              public static hashCode-impl(I)I
-              public hashCode()I
-              public static equals-impl(ILjava/lang/Object;)Z
-              public equals(Ljava/lang/Object;)Z
+              $hashCodeEqualsAndToString
               private synthetic <init>(I)V
               public static constructor-impl(I)I
               public final static synthetic box-impl(I)LColor;
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposerParamTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposerParamTransformTests.kt
index 54fa8c2..115f7b9 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposerParamTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ComposerParamTransformTests.kt
@@ -24,11 +24,8 @@
 import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
 import org.junit.Assert.assertEquals
 import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
-@RunWith(JUnit4::class)
-class ComposerParamTransformTests : AbstractIrTransformTest(useFir = false) {
+class ComposerParamTransformTests(useFir: Boolean) : AbstractIrTransformTest(useFir) {
     private fun composerParam(
         @Language("kotlin")
         source: String,
@@ -281,6 +278,69 @@
         """
     )
 
+    // Regression test for b/286132194
+    @Test
+    fun testStableVarargParams(): Unit = composerParam(
+        """
+            @Composable
+            fun B(vararg values: Int) {
+                print(values)
+            }
+
+            @NonRestartableComposable
+            @Composable
+            fun Test() {
+                B(0, 1, 2, 3)
+            }
+        """,
+        """
+            @Composable
+            fun B(values: IntArray, %composer: Composer?, %changed: Int) {
+              %composer = %composer.startRestartGroup(<>)
+              sourceInformation(%composer, "C(B):Test.kt#2487m")
+              val %dirty = %changed
+              %composer.startMovableGroup(<>, values.size)
+              val <iterator> = values.iterator()
+              while (<iterator>.hasNext()) {
+                val value = <iterator>.next()
+                %dirty = %dirty or if (%composer.changed(value)) 0b0100 else 0
+              }
+              %composer.endMovableGroup()
+              if (%dirty and 0b1110 === 0) {
+                %dirty = %dirty or 0b0010
+              }
+              if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
+                if (isTraceInProgress()) {
+                  traceEventStart(<>, %changed, -1, <>)
+                }
+                print(values)
+                if (isTraceInProgress()) {
+                  traceEventEnd()
+                }
+              } else {
+                %composer.skipToGroupEnd()
+              }
+              %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                B(*values, %composer, updateChangedFlags(%changed or 0b0001))
+              }
+            }
+            @NonRestartableComposable
+            @Composable
+            fun Test(%composer: Composer?, %changed: Int) {
+              %composer.startReplaceableGroup(<>)
+              sourceInformation(%composer, "C(Test)<B(0,>:Test.kt#2487m")
+              if (isTraceInProgress()) {
+                traceEventStart(<>, %changed, -1, <>)
+              }
+              B(0, 1, 2, 3, %composer, 0)
+              if (isTraceInProgress()) {
+                traceEventEnd()
+              }
+              %composer.endReplaceableGroup()
+            }
+        """
+    )
+
     @Test
     fun testNonComposableCode(): Unit = composerParam(
         """
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ContextReceiversTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ContextReceiversTransformTests.kt
index 52bf805..f1ddf88 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ContextReceiversTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ContextReceiversTransformTests.kt
@@ -69,7 +69,7 @@
         """,
         """
             @Composable
-            fun Test(_context_receiver_0: Foo, %composer: Composer?, %changed: Int) {
+            fun Test(%context_receiver_0: Foo, %composer: Composer?, %changed: Int) {
               %composer = %composer.startRestartGroup(<>)
               sourceInformation(%composer, "C(Test):Test.kt")
               if (%changed and 0b0001 !== 0 || !%composer.skipping) {
@@ -83,7 +83,7 @@
                 %composer.skipToGroupEnd()
               }
               %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
-                Test(_context_receiver_0, %composer, updateChangedFlags(%changed or 0b0001))
+                Test(%context_receiver_0, %composer, updateChangedFlags(%changed or 0b0001))
               }
             }
         """
@@ -107,7 +107,7 @@
         """,
         """
             @Composable
-            fun A(_context_receiver_0: Foo, _context_receiver_1: Bar, %composer: Composer?, %changed: Int) {
+            fun A(%context_receiver_0: Foo, %context_receiver_1: Bar, %composer: Composer?, %changed: Int) {
               %composer = %composer.startRestartGroup(<>)
               sourceInformation(%composer, "C(A):Test.kt")
               if (%changed and 0b0001 !== 0 || !%composer.skipping) {
@@ -121,11 +121,11 @@
                 %composer.skipToGroupEnd()
               }
               %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
-                A(_context_receiver_0, _context_receiver_1, %composer, updateChangedFlags(%changed or 0b0001))
+                A(%context_receiver_0, %context_receiver_1, %composer, updateChangedFlags(%changed or 0b0001))
               }
             }
             @Composable
-            fun B(_context_receiver_0: Foo, _context_receiver_1: Bar, _context_receiver_2: FooBar, %composer: Composer?, %changed: Int) {
+            fun B(%context_receiver_0: Foo, %context_receiver_1: Bar, %context_receiver_2: FooBar, %composer: Composer?, %changed: Int) {
               %composer = %composer.startRestartGroup(<>)
               sourceInformation(%composer, "C(B):Test.kt")
               if (%changed and 0b0001 !== 0 || !%composer.skipping) {
@@ -139,7 +139,7 @@
                 %composer.skipToGroupEnd()
               }
               %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
-                B(_context_receiver_0, _context_receiver_1, _context_receiver_2, %composer, updateChangedFlags(%changed or 0b0001))
+                B(%context_receiver_0, %context_receiver_1, %context_receiver_2, %composer, updateChangedFlags(%changed or 0b0001))
               }
             }
         """
@@ -163,7 +163,7 @@
         """,
         """
             @Composable
-            fun String.A(_context_receiver_0: Foo, _context_receiver_1: Bar, %composer: Composer?, %changed: Int) {
+            fun String.A(%context_receiver_0: Foo, %context_receiver_1: Bar, %composer: Composer?, %changed: Int) {
               %composer = %composer.startRestartGroup(<>)
               sourceInformation(%composer, "C(A):Test.kt")
               if (%changed and 0b0001 !== 0 || !%composer.skipping) {
@@ -177,11 +177,11 @@
                 %composer.skipToGroupEnd()
               }
               %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
-                A(_context_receiver_0, _context_receiver_1, %composer, updateChangedFlags(%changed or 0b0001))
+                A(%context_receiver_0, %context_receiver_1, %composer, updateChangedFlags(%changed or 0b0001))
               }
             }
             @Composable
-            fun String.B(_context_receiver_0: Foo, _context_receiver_1: Bar, _context_receiver_2: FooBar, %composer: Composer?, %changed: Int) {
+            fun String.B(%context_receiver_0: Foo, %context_receiver_1: Bar, %context_receiver_2: FooBar, %composer: Composer?, %changed: Int) {
               %composer = %composer.startRestartGroup(<>)
               sourceInformation(%composer, "C(B):Test.kt")
               if (%changed and 0b0001 !== 0 || !%composer.skipping) {
@@ -195,7 +195,7 @@
                 %composer.skipToGroupEnd()
               }
               %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
-                B(_context_receiver_0, _context_receiver_1, _context_receiver_2, %composer, updateChangedFlags(%changed or 0b0001))
+                B(%context_receiver_0, %context_receiver_1, %context_receiver_2, %composer, updateChangedFlags(%changed or 0b0001))
               }
             }
         """
@@ -223,7 +223,7 @@
         """,
         """
             @Composable
-            fun A(_context_receiver_0: Foo, _context_receiver_1: Bar, a: Int, %composer: Composer?, %changed: Int, %default: Int) {
+            fun A(%context_receiver_0: Foo, %context_receiver_1: Bar, a: Int, %composer: Composer?, %changed: Int, %default: Int) {
               %composer = %composer.startRestartGroup(<>)
               sourceInformation(%composer, "C(A):Test.kt")
               if (%changed and 0b0001 !== 0 || !%composer.skipping) {
@@ -240,11 +240,11 @@
                 %composer.skipToGroupEnd()
               }
               %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
-                A(_context_receiver_0, _context_receiver_1, a, %composer, updateChangedFlags(%changed or 0b0001), %default)
+                A(%context_receiver_0, %context_receiver_1, a, %composer, updateChangedFlags(%changed or 0b0001), %default)
               }
             }
             @Composable
-            fun B(_context_receiver_0: Foo, _context_receiver_1: Bar, _context_receiver_2: FooBar, a: Int, b: String?, c: Int, %composer: Composer?, %changed: Int, %default: Int) {
+            fun B(%context_receiver_0: Foo, %context_receiver_1: Bar, %context_receiver_2: FooBar, a: Int, b: String?, c: Int, %composer: Composer?, %changed: Int, %default: Int) {
               %composer = %composer.startRestartGroup(<>)
               sourceInformation(%composer, "C(B):Test.kt")
               if (%changed and 0b0001 !== 0 || !%composer.skipping) {
@@ -264,11 +264,11 @@
                 %composer.skipToGroupEnd()
               }
               %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
-                B(_context_receiver_0, _context_receiver_1, _context_receiver_2, a, b, c, %composer, updateChangedFlags(%changed or 0b0001), %default)
+                B(%context_receiver_0, %context_receiver_1, %context_receiver_2, a, b, c, %composer, updateChangedFlags(%changed or 0b0001), %default)
               }
             }
             @Composable
-            fun C(_context_receiver_0: Foo, a: Int, bar: Bar?, %composer: Composer?, %changed: Int, %default: Int) {
+            fun C(%context_receiver_0: Foo, a: Int, bar: Bar?, %composer: Composer?, %changed: Int, %default: Int) {
               %composer = %composer.startRestartGroup(<>)
               sourceInformation(%composer, "C(C):Test.kt")
               val %dirty = %changed
@@ -296,7 +296,7 @@
                 %composer.skipToGroupEnd()
               }
               %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
-                C(_context_receiver_0, a, bar, %composer, updateChangedFlags(%changed or 0b0001), %default)
+                C(%context_receiver_0, a, bar, %composer, updateChangedFlags(%changed or 0b0001), %default)
               }
             }
         """
@@ -316,7 +316,7 @@
         """,
         """
         @Composable
-        fun String.B(_context_receiver_0: Foo, _context_receiver_1: Bar, _context_receiver_2: FooBar, a: Int, b: String?, c: Int, %composer: Composer?, %changed: Int, %default: Int) {
+        fun String.B(%context_receiver_0: Foo, %context_receiver_1: Bar, %context_receiver_2: FooBar, a: Int, b: String?, c: Int, %composer: Composer?, %changed: Int, %default: Int) {
           %composer = %composer.startRestartGroup(<>)
           sourceInformation(%composer, "C(B):Test.kt")
           if (%changed and 0b0001 !== 0 || !%composer.skipping) {
@@ -336,7 +336,7 @@
             %composer.skipToGroupEnd()
           }
           %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
-            B(_context_receiver_0, _context_receiver_1, _context_receiver_2, a, b, c, %composer, updateChangedFlags(%changed or 0b0001), %default)
+            B(%context_receiver_0, %context_receiver_1, %context_receiver_2, a, b, c, %composer, updateChangedFlags(%changed or 0b0001), %default)
           }
         }
         """
@@ -516,9 +516,9 @@
         """,
         """
             @Composable
-            fun Test(_context_receiver_0: A, _context_receiver_1: B, _context_receiver_2: C, _context_receiver_3: D, _context_receiver_4: E, _context_receiver_5: F, _context_receiver_6: G, _context_receiver_7: H, _context_receiver_8: I, _context_receiver_9: J, _context_receiver_10: K, _context_receiver_11: L, %composer: Composer?, %changed: Int, %changed1: Int) {
+            fun Test(%context_receiver_0: A, %context_receiver_1: B, %context_receiver_2: C, %context_receiver_3: D, %context_receiver_4: E, %context_receiver_5: F, %context_receiver_6: G, %context_receiver_7: H, %context_receiver_8: I, %context_receiver_9: J, %context_receiver_10: K, %context_receiver_11: L, %composer: Composer?, %changed: Int, %changed1: Int) {
               %composer = %composer.startRestartGroup(<>)
-              sourceInformation(%composer, "C(Test)P(!2,4,5,6,7,8,9,10,11):Test.kt")
+              sourceInformation(%composer, "C(Test):Test.kt")
               if (%changed and 0b0001 !== 0 || %changed1 and 0b0001 !== 0 || !%composer.skipping) {
                 if (isTraceInProgress()) {
                   traceEventStart(<>, %changed, %changed1, <>)
@@ -530,51 +530,53 @@
                 %composer.skipToGroupEnd()
               }
               %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
-                Test(_context_receiver_0, _context_receiver_1, _context_receiver_2, _context_receiver_3, _context_receiver_4, _context_receiver_5, _context_receiver_6, _context_receiver_7, _context_receiver_8, _context_receiver_9, _context_receiver_10, _context_receiver_11, %composer, updateChangedFlags(%changed or 0b0001), updateChangedFlags(%changed1))
+                Test(%context_receiver_0, %context_receiver_1, %context_receiver_2, %context_receiver_3, %context_receiver_4, %context_receiver_5, %context_receiver_6, %context_receiver_7, %context_receiver_8, %context_receiver_9, %context_receiver_10, %context_receiver_11, %composer, updateChangedFlags(%changed or 0b0001), updateChangedFlags(%changed1))
               }
             }
         """
     )
 
     @Test
-    fun testContextReceiverAndComposableLambdaParam(): Unit = contextReceivers(
-        """
-            class Foo { }
-        """,
-        """
-            context(Foo)
-            @Composable
-            fun Test(a: String, b: @Composable (String) -> Unit) {
-                b("yay")
-            }
-        """,
-        """
-            @Composable
-            @ComposableInferredTarget(scheme = "[0[0]]")
-            fun Test(_context_receiver_0: Foo, a: String, b: Function3<String, Composer, Int, Unit>, %composer: Composer?, %changed: Int) {
-              %composer = %composer.startRestartGroup(<>)
-              sourceInformation(%composer, "C(Test)<b("yay...>:Test.kt")
-              val %dirty = %changed
-              if (%changed and 0b001110000000 === 0) {
-                %dirty = %dirty or if (%composer.changedInstance(b)) 0b000100000000 else 0b10000000
-              }
-              if (%dirty and 0b001010000001 !== 0b10000000 || !%composer.skipping) {
-                if (isTraceInProgress()) {
-                  traceEventStart(<>, %dirty, -1, <>)
+    fun testContextReceiverAndComposableLambdaParam() {
+        contextReceivers(
+            """
+                class Foo { }
+            """,
+            """
+                context(Foo)
+                @Composable
+                fun Test(a: String, b: @Composable (String) -> Unit) {
+                    b("yay")
                 }
-                b("yay", %composer, 0b0110 or 0b01110000 and %dirty shr 0b0011)
-                if (isTraceInProgress()) {
-                  traceEventEnd()
+            """,
+            """
+                @Composable
+                @ComposableInferredTarget(scheme = "[0[0]]")
+                fun Test(%context_receiver_0: Foo, a: String, b: Function3<String, Composer, Int, Unit>, %composer: Composer?, %changed: Int) {
+                  %composer = %composer.startRestartGroup(<>)
+                  sourceInformation(%composer, "C(Test)<b("yay...>:Test.kt")
+                  val %dirty = %changed
+                  if (%changed and 0b001110000000 === 0) {
+                    %dirty = %dirty or if (%composer.changedInstance(b)) 0b000100000000 else 0b10000000
+                  }
+                  if (%dirty and 0b001010000001 !== 0b10000000 || !%composer.skipping) {
+                    if (isTraceInProgress()) {
+                      traceEventStart(<>, %dirty, -1, <>)
+                    }
+                    b("yay", %composer, 0b0110 or 0b01110000 and %dirty shr 0b0011)
+                    if (isTraceInProgress()) {
+                      traceEventEnd()
+                    }
+                  } else {
+                    %composer.skipToGroupEnd()
+                  }
+                  %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                    Test(%context_receiver_0, a, b, %composer, updateChangedFlags(%changed or 0b0001))
+                  }
                 }
-              } else {
-                %composer.skipToGroupEnd()
-              }
-              %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
-                Test(_context_receiver_0, a, b, %composer, updateChangedFlags(%changed or 0b0001))
-              }
-            }
-        """
-    )
+            """
+        )
+    }
 
     @Test
     fun testContextReceiverAndDefaultParamsUsage(): Unit = contextReceivers(
@@ -629,14 +631,14 @@
               }
             }
             @Composable
-            fun Test(_context_receiver_0: Foo, a: String?, b: Int, %composer: Composer?, %changed: Int, %default: Int) {
+            fun Test(%context_receiver_0: Foo, a: String?, b: Int, %composer: Composer?, %changed: Int, %default: Int) {
               %composer = %composer.startRestartGroup(<>)
               sourceInformation(%composer, "C(Test):Test.kt")
               val %dirty = %changed
               if (%default and 0b0001 !== 0) {
                 %dirty = %dirty or 0b0110
               } else if (%changed and 0b1110 === 0) {
-                %dirty = %dirty or if (%composer.changed(_context_receiver_0)) 0b0100 else 0b0010
+                %dirty = %dirty or if (%composer.changed(%context_receiver_0)) 0b0100 else 0b0010
               }
               if (%default and 0b0010 !== 0) {
                 %dirty = %dirty or 0b00110000
@@ -659,7 +661,7 @@
                   traceEventStart(<>, %changed, -1, <>)
                 }
                 val combineParams = a + b
-                if (_context_receiver_0.someString == combineParams) {
+                if (%context_receiver_0.someString == combineParams) {
                   println("Same same")
                 }
                 if (isTraceInProgress()) {
@@ -669,7 +671,7 @@
                 %composer.skipToGroupEnd()
               }
               %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
-                Test(_context_receiver_0, a, b, %composer, updateChangedFlags(%changed or 0b0001), %default)
+                Test(%context_receiver_0, a, b, %composer, updateChangedFlags(%changed or 0b0001), %default)
               }
             }
         """
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTests.kt
index 4f1aa0b..d5dca9d 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTests.kt
@@ -19,7 +19,7 @@
 import org.intellij.lang.annotations.Language
 import org.junit.Test
 
-class ControlFlowTransformTests : AbstractControlFlowTransformTests() {
+class ControlFlowTransformTests(useFir: Boolean) : AbstractControlFlowTransformTests(useFir) {
     @Test
     fun testIfNonComposable(): Unit = controlFlow(
         """
@@ -1032,16 +1032,25 @@
               }
               if (condition) {
                 val tmp0_return = false
+                if (isTraceInProgress()) {
+                  traceEventEnd()
+                }
                 sourceInformationMarkerEnd(%composer)
                 return tmp0_return
               }
               with(obj) {
                 if (condition) {
                   val tmp0_return = false
+                  if (isTraceInProgress()) {
+                    traceEventEnd()
+                  }
                   sourceInformationMarkerEnd(%composer)
                   return tmp0_return
                 }
                 val tmp1_return = %composer.inserting
+                if (isTraceInProgress()) {
+                  traceEventEnd()
+                }
                 sourceInformationMarkerEnd(%composer)
                 return tmp1_return
               }
@@ -1827,13 +1836,13 @@
               if (isTraceInProgress()) {
                 traceEventStart(<>, %changed, -1, <>)
               }
-              val y = val tmp0_elvis_lhs = x
+              val y = val <elvis> = x
               val tmp0_group = when {
-                tmp0_elvis_lhs == null -> {
+                <elvis> == null -> {
                   R(%composer, 0)
                 }
                 else -> {
-                  tmp0_elvis_lhs
+                  <elvis>
                 }
               }
               tmp0_group
@@ -1867,9 +1876,9 @@
               if (isTraceInProgress()) {
                 traceEventStart(<>, %changed, -1, <>)
               }
-              val tmp0_iterator = items.iterator()
-              while (tmp0_iterator.hasNext()) {
-                val i = tmp0_iterator.next()
+              val <iterator> = items.iterator()
+              while (<iterator>.hasNext()) {
+                val i = <iterator>.next()
                 P(i, %composer, 0)
               }
               if (isTraceInProgress()) {
@@ -1904,9 +1913,9 @@
               }
               %composer.startReplaceableGroup(<>)
               sourceInformation(%composer, "*<P(i)>")
-              val tmp0_iterator = items.iterator()
-              while (tmp0_iterator.hasNext()) {
-                val i = tmp0_iterator.next()
+              val <iterator> = items.iterator()
+              while (<iterator>.hasNext()) {
+                val i = <iterator>.next()
                 P(i, %composer, 0)
               }
               %composer.endReplaceableGroup()
@@ -1940,9 +1949,9 @@
               if (isTraceInProgress()) {
                 traceEventStart(<>, %changed, -1, <>)
               }
-              val tmp0_iterator = L(%composer, 0).iterator()
-              while (tmp0_iterator.hasNext()) {
-                val i = tmp0_iterator.next()
+              val <iterator> = L(%composer, 0).iterator()
+              while (<iterator>.hasNext()) {
+                val i = <iterator>.next()
                 print(i)
               }
               if (isTraceInProgress()) {
@@ -2952,14 +2961,14 @@
               a@while (a.hasNext()) {
                 val x = a.next()
                 if (x == 0) {
-                  break
+                  break${if (useFir) "@a" else ""}
                 }
                 %composer.startReplaceableGroup(<>)
                 sourceInformation(%composer, "*<A()>")
                 b@while (b.hasNext()) {
                   val y = b.next()
                   if (y == 0) {
-                    break
+                    break${if (useFir) "@b" else ""}
                   }
                   if (y == x) {
                     %composer.endReplaceableGroup()
@@ -5311,7 +5320,7 @@
                   sourceInformation(%composer, "C<B(a)>,<B(a)>:Test.kt")
                   if (%changed and 0b1011 !== 0b0010 || !%composer.skipping) {
                     if (isTraceInProgress()) {
-                      traceEventStart(<>, %changed, -1, "SomeClass.onCreate.<anonymous> (Test.kt:6)")
+                      traceEventStart(<>, %changed, -1, "SomeClass.onCreate.<anonymous> (Test.kt:7)")
                     }
                     B(a, %composer, 0)
                     B(a, %composer, 0)
@@ -5332,7 +5341,7 @@
                 sourceInformation(%composer, "C<B(a)>,<B(a)>:Test.kt")
                 if (%changed and 0b1011 !== 0b0010 || !%composer.skipping) {
                   if (isTraceInProgress()) {
-                    traceEventStart(<>, %changed, -1, "Test.<anonymous> (Test.kt:15)")
+                    traceEventStart(<>, %changed, -1, "Test.<anonymous> (Test.kt:16)")
                   }
                   B(a, %composer, 0)
                   B(a, %composer, 0)
@@ -5399,7 +5408,7 @@
                 sourceInformation(%composer, "C<IW>:Test.kt")
                 if (%changed and 0b1011 !== 0b0010 || !%composer.skipping) {
                   if (isTraceInProgress()) {
-                    traceEventStart(<>, %changed, -1, "ComposableSingletons%TestKt.lambda-1.<anonymous> (Test.kt:5)")
+                    traceEventStart(<>, %changed, -1, "ComposableSingletons%TestKt.lambda-1.<anonymous> (Test.kt:6)")
                   }
                   IW({ %composer: Composer?, %changed: Int ->
                     sourceInformationMarkerStart(%composer, <>, "C<T(2)>,<T(4)>:Test.kt")
@@ -5661,6 +5670,9 @@
               }
               if (a < 100) {
                 val tmp1_return = 0
+                if (isTraceInProgress()) {
+                  traceEventEnd()
+                }
                 sourceInformationMarkerEnd(%composer)
                 return tmp1_return
               }
@@ -5788,9 +5800,9 @@
                 val a = remember({
                   A()
                 }, %composer, 0)
-                val tmp0_iterator = start until end.iterator()
-                while (tmp0_iterator.hasNext()) {
-                  val i = tmp0_iterator.next()
+                val <iterator> = start until end.iterator()
+                while (<iterator>.hasNext()) {
+                  val i = <iterator>.next()
                   val b = a.get(bKey, %composer, 0b00110110)
                   %composer.startReplaceableGroup(<>)
                   sourceInformation(%composer, "<get(cK...>")
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTestsNoSource.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTestsNoSource.kt
index 7a04e15..4fb99f6 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTestsNoSource.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/ControlFlowTransformTestsNoSource.kt
@@ -19,7 +19,9 @@
 import org.jetbrains.kotlin.config.CompilerConfiguration
 import org.junit.Test
 
-class ControlFlowTransformTestsNoSource : AbstractControlFlowTransformTests() {
+class ControlFlowTransformTestsNoSource(
+    useFir: Boolean
+) : AbstractControlFlowTransformTests(useFir) {
     override fun CompilerConfiguration.updateConfiguration() {
         put(ComposeConfiguration.SOURCE_INFORMATION_ENABLED_KEY, false)
     }
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/DefaultParamTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/DefaultParamTransformTests.kt
index de1087bf..29dcd11 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/DefaultParamTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/DefaultParamTransformTests.kt
@@ -18,11 +18,8 @@
 
 import org.intellij.lang.annotations.Language
 import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
-@RunWith(JUnit4::class)
-class DefaultParamTransformTests : AbstractIrTransformTest(useFir = false) {
+class DefaultParamTransformTests(useFir: Boolean) : AbstractIrTransformTest(useFir) {
     private fun defaultParams(
         @Language("kotlin")
         unchecked: String,
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/DurableFunctionKeyCodegenTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/DurableFunctionKeyCodegenTests.kt
index 1ee3448..728e2ac 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/DurableFunctionKeyCodegenTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/DurableFunctionKeyCodegenTests.kt
@@ -20,17 +20,23 @@
 import org.junit.Assert.assertEquals
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.robolectric.RobolectricTestRunner
+import org.robolectric.ParameterizedRobolectricTestRunner
 import org.robolectric.annotation.Config
 
 /* ktlint-disable max-line-length */
-@RunWith(RobolectricTestRunner::class)
+@RunWith(ParameterizedRobolectricTestRunner::class)
 @Config(
     manifest = Config.NONE,
     minSdk = 23,
     maxSdk = 23
 )
-class DurableFunctionKeyCodegenTests : AbstractCodegenSignatureTest() {
+class DurableFunctionKeyCodegenTests(useFir: Boolean) : AbstractCodegenSignatureTest(useFir) {
+    companion object {
+        @JvmStatic
+        @ParameterizedRobolectricTestRunner.Parameters(name = "useFir = {0}")
+        fun data() = arrayOf<Any>(false, true)
+    }
+
     override fun CompilerConfiguration.updateConfiguration() {
         put(ComposeConfiguration.GENERATE_FUNCTION_KEY_META_CLASSES_KEY, true)
     }
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FcsTypeResolutionTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FcsTypeResolutionTests.kt
index af7a6e3..e813b45 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FcsTypeResolutionTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FcsTypeResolutionTests.kt
@@ -16,9 +16,10 @@
 
 package androidx.compose.compiler.plugins.kotlin
 
+import org.junit.Assume.assumeFalse
 import org.junit.Test
 
-class FcsTypeResolutionTests : AbstractComposeDiagnosticsTest() {
+class FcsTypeResolutionTests(useFir: Boolean) : AbstractComposeDiagnosticsTest(useFir) {
     @Test
     fun testImplicitlyPassedReceiverScope1() = check(
         """
@@ -38,19 +39,21 @@
 
             @Composable
             fun Int.Foo(content: @Composable Int.(foo: String) -> Unit) {
-                content<!NO_VALUE_FOR_PARAMETER!>()<!>
+                ${if (useFir) "<!NO_VALUE_FOR_PARAMETER!>" else ""}content${if (!useFir) "<!NO_VALUE_FOR_PARAMETER!>" else ""}()<!>
             }
 
             @Composable
             fun Bar(content: @Composable Int.() -> Unit) {
-                content<!NO_VALUE_FOR_PARAMETER!>()<!>
+                ${if (useFir) "<!NO_VALUE_FOR_PARAMETER!>" else ""}content${if (!useFir) "<!NO_VALUE_FOR_PARAMETER!>" else ""}()<!>
             }
         """
     )
 
     @Test
-    fun testSmartCastsAndPunning() = check(
-        """
+    fun testSmartCastsAndPunning() {
+        val typeMismatch = if (useFir) "ARGUMENT_TYPE_MISMATCH" else "TYPE_MISMATCH"
+        check(
+            """
             import androidx.compose.runtime.*
 
             @Composable
@@ -58,14 +61,15 @@
 
             @Composable
             fun test(bar: String?) {
-                Foo(<!TYPE_MISMATCH!>bar<!>)
+                Foo(<!$typeMismatch!>bar<!>)
                 if (bar != null) {
                     Foo(bar)
                     Foo(bar=bar)
                 }
             }
         """
-    )
+        )
+    }
 
     @Test
     fun testExtensionInvoke() = check(
@@ -195,25 +199,29 @@
     )
 
     @Test
-    fun testMissingAttributes() = check(
-        """
-            import androidx.compose.runtime.*
+    fun testMissingAttributes() {
+        // Fails on K2 because of KT-57471. We cannot have named composable lambda arguments
+        // until the upstream bug is fixed.
+        assumeFalse(useFir)
+        check(
+            """
+                import androidx.compose.runtime.*
 
-            data class Foo(val value: Int)
+                data class Foo(val value: Int)
 
-            @Composable fun A(x: Foo) { println(x) }
+                @Composable fun A(x: Foo) { println(x) }
 
-            // NOTE: It's important that the diagnostic be only over the call target, and not the
-            // entire element so that a single error doesn't end up making a huge part of an 
-            // otherwise correct file "red".
-            @Composable fun Test(F: @Composable (x: Foo) -> Unit) {
-                // NOTE: constructor attributes and fn params get a "missing parameter" diagnostic
-                A<!NO_VALUE_FOR_PARAMETER!>()<!>
+                // NOTE: It's important that the diagnostic be only over the call target, and not the
+                // entire element so that a single error doesn't end up making a huge part of an
+                // otherwise correct file "red".
+                @Composable fun Test(F: @Composable (x: Foo) -> Unit) {
+                    // NOTE: constructor attributes and fn params get a "missing parameter" diagnostic
+                    A<!NO_VALUE_FOR_PARAMETER!>()<!>
 
-                // local
-                F<!NO_VALUE_FOR_PARAMETER!>()<!>
+                    // local
+                    F<!NO_VALUE_FOR_PARAMETER!>()<!>
 
-                val x = Foo(123)
+                    val x = Foo(123)
 
                 A(x)
                 F(x)
@@ -221,8 +229,9 @@
                 F(<!NAMED_ARGUMENTS_NOT_ALLOWED!>x<!>=x)
             }
 
-        """.trimIndent()
-    )
+            """.trimIndent()
+        )
+    }
 
     @Test
     fun testDuplicateAttributes() = check(
@@ -271,80 +280,91 @@
 
             @Composable fun Test() {
                 <!CREATING_AN_INSTANCE_OF_ABSTRACT_CLASS!>Foo()<!>
-                <!CREATING_AN_INSTANCE_OF_ABSTRACT_CLASS!>Bar<!NO_VALUE_FOR_PARAMETER!>()<!><!>
+                ${if (!useFir) {
+                    "<!CREATING_AN_INSTANCE_OF_ABSTRACT_CLASS!>Bar" +
+                        "<!NO_VALUE_FOR_PARAMETER!>()<!><!>"
+                } else {
+                    "<!CREATING_AN_INSTANCE_OF_ABSTRACT_CLASS,NO_VALUE_FOR_PARAMETER!>Bar()<!>"
+                }}
             }
 
         """.trimIndent()
     )
 
     @Test
-    fun testGenerics() = check(
-        """
-            import androidx.compose.runtime.*
+    fun testGenerics() {
+        val typeMismatch = if (useFir) "ARGUMENT_TYPE_MISMATCH" else "TYPE_MISMATCH"
+        check(
+            """
+                import androidx.compose.runtime.*
 
-            class A { fun a() {} }
-            class B { fun b() {} }
+                class A { fun a() {} }
+                class B { fun b() {} }
 
-            @Composable fun <T> Bar(x: Int, value: T, f: (T) -> Unit) { println(value); println(f); println(x) }
+                @Composable fun <T> Bar(x: Int, value: T, f: (T) -> Unit) { println(value); println(f); println(x) }
 
-            @Composable fun Test() {
+                @Composable fun Test() {
 
-                val fa: (A) -> Unit = { it.a() }
-                val fb: (B) -> Unit = { it.b() }
+                    val fa: (A) -> Unit = { it.a() }
+                    val fb: (B) -> Unit = { it.b() }
 
-                Bar(x=1, value=A(), f={ it.a() })
-                Bar(x=1, value=B(), f={ it.b() })
-                Bar(x=1, value=A(), f=fa)
-                Bar(x=1, value=B(), f=fb)
-                Bar(x=1, value=B(), f={ it.<!UNRESOLVED_REFERENCE!>a<!>() })
-                Bar(x=1, value=A(), f={ it.<!UNRESOLVED_REFERENCE!>b<!>() })
-                Bar(
-                  x=1, 
-                  value=A(), 
-                  f=<!TYPE_MISMATCH!>fb<!>
-                )
-                Bar(
-                  x=1,
-                  value=B(), 
-                  f=<!TYPE_MISMATCH!>fa<!>
-                )
-            }
+                    Bar(x=1, value=A(), f={ it.a() })
+                    Bar(x=1, value=B(), f={ it.b() })
+                    Bar(x=1, value=A(), f=fa)
+                    Bar(x=1, value=B(), f=fb)
+                    Bar(x=1, value=B(), f={ it.<!UNRESOLVED_REFERENCE!>a<!>() })
+                    Bar(x=1, value=A(), f={ it.<!UNRESOLVED_REFERENCE!>b<!>() })
+                    Bar(
+                      x=1,
+                      value=A(),
+                      f=<!$typeMismatch!>fb<!>
+                    )
+                    Bar(
+                      x=1,
+                      value=B(),
+                      f=<!$typeMismatch!>fa<!>
+                    )
+                }
 
-        """.trimIndent()
-    )
+            """.trimIndent()
+        )
+    }
 
     @Test
-    fun testUnresolvedAttributeValueResolvedTarget() = check(
-        """
-            import androidx.compose.runtime.*
+    fun testUnresolvedAttributeValueResolvedTarget() {
+        val typeMismatch = if (useFir) "ARGUMENT_TYPE_MISMATCH" else "TYPE_MISMATCH"
+        check(
+            """
+                import androidx.compose.runtime.*
 
-            @Composable fun Fam(bar: Int, x: Int) {
-                print(bar)
-                print(x)
-            }
+                @Composable fun Fam(bar: Int, x: Int) {
+                    print(bar)
+                    print(x)
+                }
 
-            @Composable fun Test() {
-                Fam(
-                  bar=<!UNRESOLVED_REFERENCE!>undefined<!>,
-                  x=1
-                )
-                Fam(
-                  bar=1,
-                  x=<!UNRESOLVED_REFERENCE!>undefined<!>
-                )
-                Fam(
-                  <!UNRESOLVED_REFERENCE!>bar<!>,
-                  <!UNRESOLVED_REFERENCE!>x<!>
-                )
+                @Composable fun Test() {
+                    Fam(
+                      bar=<!UNRESOLVED_REFERENCE!>undefined<!>,
+                      x=1
+                    )
+                    Fam(
+                      bar=1,
+                      x=<!UNRESOLVED_REFERENCE!>undefined<!>
+                    )
+                    Fam(
+                      <!UNRESOLVED_REFERENCE!>bar<!>,
+                      <!UNRESOLVED_REFERENCE!>x<!>
+                    )
 
-                Fam(
-                  bar=<!TYPE_MISMATCH!>""<!>,
-                  x=<!TYPE_MISMATCH!>""<!>
-                )
-            }
+                    Fam(
+                      bar=<!$typeMismatch!>""<!>,
+                      x=<!$typeMismatch!>""<!>
+                    )
+                }
 
-        """.trimIndent()
-    )
+            """.trimIndent()
+        )
+    }
 
     // TODO(lmr): this triggers an exception!
     @Test
@@ -371,39 +391,47 @@
     )
 
     @Test
-    fun testMismatchedAttributes() = check(
-        """
-            import androidx.compose.runtime.*
+    fun testMismatchedAttributes() {
+        val typeMismatch = if (useFir) "ARGUMENT_TYPE_MISMATCH" else "TYPE_MISMATCH"
+        val constantTypeMismatch = if (useFir) {
+            "ARGUMENT_TYPE_MISMATCH"
+        } else {
+            "CONSTANT_EXPECTED_TYPE_MISMATCH"
+        }
+        check(
+            """
+                import androidx.compose.runtime.*
 
-            open class A {}
-            class B : A() {}
+                open class A {}
+                class B : A() {}
 
-            @Composable fun Foo(x: A = A(), y: A = B(), z: B = B()) {
-                print(x)
-                print(y)
-                print(z)
-            }
+                @Composable fun Foo(x: A = A(), y: A = B(), z: B = B()) {
+                    print(x)
+                    print(y)
+                    print(z)
+                }
 
-            @Composable fun Test() {
-                Foo(
-                    x=A(),
-                    y=A(),
-                    z=<!TYPE_MISMATCH!>A()<!>
-                )
-                Foo(
-                    x=B(),
-                    y=B(),
-                    z=B()
-                )
-                Foo(
-                    x=<!CONSTANT_EXPECTED_TYPE_MISMATCH!>1<!>,
-                    y=<!CONSTANT_EXPECTED_TYPE_MISMATCH!>1<!>,
-                    z=<!CONSTANT_EXPECTED_TYPE_MISMATCH!>1<!>
-                )
-            }
+                @Composable fun Test() {
+                    Foo(
+                        x=A(),
+                        y=A(),
+                        z=<!$typeMismatch!>A()<!>
+                    )
+                    Foo(
+                        x=B(),
+                        y=B(),
+                        z=B()
+                    )
+                    Foo(
+                        x=<!$constantTypeMismatch!>1<!>,
+                        y=<!$constantTypeMismatch!>1<!>,
+                        z=<!$constantTypeMismatch!>1<!>
+                    )
+                }
 
-        """.trimIndent()
-    )
+            """.trimIndent()
+        )
+    }
 
     @Test
     fun testErrorAttributeValue() = check(
@@ -423,106 +451,148 @@
     )
 
     @Test
-    fun testUnresolvedQualifiedTag() = check(
-        """
-            import androidx.compose.runtime.*
+    fun testUnresolvedQualifiedTag() {
+        val functionExpected = if (useFir) {
+            "FUNCTION_EXPECTED"
+        } else {
+            "UNRESOLVED_REFERENCE_WRONG_RECEIVER"
+        }
+        check(
+            """
+                import androidx.compose.runtime.*
 
-            object MyNamespace {
-                @Composable fun Bar(content: @Composable () -> Unit = {}) { 
-                    content() 
+                object MyNamespace {
+                    @Composable fun Bar(content: @Composable () -> Unit = {}) {
+                        content()
+                    }
+
+                    var Baz = @Composable { }
+
+                    var someString = ""
+                    class NonComponent {}
                 }
 
-                var Baz = @Composable { }
-
-                var someString = ""
-                class NonComponent {}
-            }
-
-            class Boo {
-                @Composable fun Wat() { }
-            }
-
-            @Composable fun Test() {
-
-                MyNamespace.Bar()
-                MyNamespace.Baz()
-                MyNamespace.<!UNRESOLVED_REFERENCE!>Qoo<!>()
-                MyNamespace.<!FUNCTION_EXPECTED!>someString<!>()
-                MyNamespace.NonComponent()
-                MyNamespace.Bar {}
-                MyNamespace.Baz <!TOO_MANY_ARGUMENTS!>{}<!>
-
-                val obj = Boo()
-                Boo.<!UNRESOLVED_REFERENCE!>Wat<!>()
-                obj.Wat()
-
-                MyNamespace.<!UNRESOLVED_REFERENCE!>Bam<!>()
-                <!UNRESOLVED_REFERENCE!>SomethingThatDoesntExist<!>.Foo()
-
-                obj.Wat <!TOO_MANY_ARGUMENTS!>{
-                }<!>
-
-                MyNamespace.<!UNRESOLVED_REFERENCE!>Qoo<!> {
+                class Boo {
+                    @Composable fun Wat() { }
                 }
 
-                MyNamespace.<!UNRESOLVED_REFERENCE_WRONG_RECEIVER!>someString<!> {
+                @Composable fun Test() {
+
+                    MyNamespace.Bar()
+                    MyNamespace.Baz()
+                    MyNamespace.<!UNRESOLVED_REFERENCE!>Qoo<!>()
+                    MyNamespace.<!FUNCTION_EXPECTED!>someString<!>()
+                    MyNamespace.NonComponent()
+                    MyNamespace.Bar {}
+                    MyNamespace.Baz <!TOO_MANY_ARGUMENTS!>{}<!>
+
+                    val obj = Boo()
+                    Boo.<!UNRESOLVED_REFERENCE!>Wat<!>()
+                    obj.Wat()
+
+                    MyNamespace.<!UNRESOLVED_REFERENCE!>Bam<!>()
+                    <!UNRESOLVED_REFERENCE!>SomethingThatDoesntExist<!>.Foo()
+
+                    obj.Wat <!TOO_MANY_ARGUMENTS!>{
+                    }<!>
+
+                    MyNamespace.<!UNRESOLVED_REFERENCE!>Qoo<!> {
+                    }
+
+                    MyNamespace.<!$functionExpected!>someString<!> {
+                    }
+
+                    <!UNRESOLVED_REFERENCE!>SomethingThatDoesntExist<!>.Foo {
+                    }
+
+                    MyNamespace.NonComponent <!TOO_MANY_ARGUMENTS!>{}<!>
+
+                    MyNamespace.<!UNRESOLVED_REFERENCE!>Bam<!> {}
+
                 }
 
-                <!UNRESOLVED_REFERENCE!>SomethingThatDoesntExist<!>.Foo {
-                }
-
-                MyNamespace.NonComponent <!TOO_MANY_ARGUMENTS!>{}<!>
-
-                MyNamespace.<!UNRESOLVED_REFERENCE!>Bam<!> {}
-
-            }
-
-        """.trimIndent()
-    )
+            """.trimIndent()
+        )
+    }
 
     // TODO(lmr): overloads creates resolution exception
     @Test
-    fun testChildren() = check(
-        """
-            import androidx.compose.runtime.*
-            import android.widget.Button
-            import android.widget.LinearLayout
+    fun testChildren() {
+        val declarations = """
+                import androidx.compose.runtime.*
+                import android.widget.Button
+                import android.widget.LinearLayout
 
-            @Composable fun ChildrenRequired2(content: @Composable () -> Unit) { content() }
+                @Composable fun ChildrenRequired2(content: @Composable () -> Unit) { content() }
 
-            @Composable fun ChildrenOptional3(content: @Composable () -> Unit = {}){ content() }
+                @Composable fun ChildrenOptional3(content: @Composable () -> Unit = {}){ content() }
 
-            @Composable fun NoChildren2() {}
+                @Composable fun NoChildren2() {}
 
-            @Composable 
-            fun MultiChildren(c: @Composable (x: Int) -> Unit = {}) { c(1) }
+                @Composable
+                fun MultiChildren(c: @Composable (x: Int) -> Unit = {}) { c(1) }
 
-            @Composable 
-            fun MultiChildren(c: @Composable (x: Int, y: Int) -> Unit = { x, y ->println(x + y) }) { c(1,1) }
-
-            @Composable fun Test() {
-                ChildrenRequired2 {}
-                ChildrenRequired2<!NO_VALUE_FOR_PARAMETER!>()<!>
-
-                ChildrenOptional3 {}
-                ChildrenOptional3()
-
-                NoChildren2 <!TOO_MANY_ARGUMENTS!>{}<!>
-                NoChildren2()
-
-                <!OVERLOAD_RESOLUTION_AMBIGUITY!>MultiChildren<!> {}
-                MultiChildren { x ->
-                    println(x)
-                }
-                MultiChildren { x, y ->
-                    println(x + y)
-                }
-                <!NONE_APPLICABLE!>MultiChildren<!> { <!CANNOT_INFER_PARAMETER_TYPE!>x<!>,
-                <!CANNOT_INFER_PARAMETER_TYPE!>y<!>, <!CANNOT_INFER_PARAMETER_TYPE!>z<!> ->
-                    println(x + y + z)
-                }
-            }
-
+                @Composable
+                fun MultiChildren(c: @Composable (x: Int, y: Int) -> Unit = { x, y ->println(x + y) }) { c(1,1) }
         """.trimIndent()
-    )
+        if (!useFir) {
+            check(
+                """
+                $declarations
+
+                @Composable fun Test() {
+                    ChildrenRequired2 {}
+                    ChildrenRequired2<!NO_VALUE_FOR_PARAMETER!>()<!>
+
+                    ChildrenOptional3 {}
+                    ChildrenOptional3()
+
+                    NoChildren2 <!TOO_MANY_ARGUMENTS!>{}<!>
+                    NoChildren2()
+
+                    <!OVERLOAD_RESOLUTION_AMBIGUITY!>MultiChildren<!> {}
+                    MultiChildren { x ->
+                        println(x)
+                    }
+                    MultiChildren { x, y ->
+                        println(x + y)
+                    }
+                    <!NONE_APPLICABLE!>MultiChildren<!> { <!CANNOT_INFER_PARAMETER_TYPE!>x<!>,
+                    <!CANNOT_INFER_PARAMETER_TYPE!>y<!>, <!CANNOT_INFER_PARAMETER_TYPE!>z<!> ->
+                        println(x + y + z)
+                    }
+                }
+            """.trimIndent())
+        } else {
+            check(
+                """
+                $declarations
+
+                @Composable fun Test() {
+                    ChildrenRequired2 {}
+                    <!NO_VALUE_FOR_PARAMETER!>ChildrenRequired2()<!>
+
+                    ChildrenOptional3 {}
+                    ChildrenOptional3()
+
+                    NoChildren2 <!TOO_MANY_ARGUMENTS!>{}<!>
+                    NoChildren2()
+
+                    // This call is not ambiguous in K2. The call can only match the single
+                    // argument lambda - with an implicit `it`. The two argument version would
+                    // have required explicit lambda parameters.
+                    MultiChildren {}
+                    MultiChildren { x ->
+                        println(x)
+                    }
+                    MultiChildren { x, y ->
+                        println(x + y)
+                    }
+                    <!NONE_APPLICABLE!>MultiChildren<!> { x, y, z ->
+                        <!OVERLOAD_RESOLUTION_AMBIGUITY!>println<!>(x <!OVERLOAD_RESOLUTION_AMBIGUITY!>+<!> y <!OVERLOAD_RESOLUTION_AMBIGUITY!>+<!> z)
+                    }
+                }
+            """.trimIndent())
+        }
+    }
 }
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt
index 125adde..f100c5d 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt
@@ -19,11 +19,10 @@
 import org.intellij.lang.annotations.Language
 import org.jetbrains.kotlin.config.CompilerConfiguration
 import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
-@RunWith(JUnit4::class)
-abstract class FunctionBodySkippingTransformTestsBase : AbstractIrTransformTest(useFir = false) {
+abstract class FunctionBodySkippingTransformTestsBase(
+    useFir: Boolean
+) : AbstractIrTransformTest(useFir) {
     protected fun comparisonPropagation(
         @Language("kotlin")
         unchecked: String,
@@ -50,7 +49,9 @@
     )
 }
 
-class FunctionBodySkippingTransformTests : FunctionBodySkippingTransformTestsBase() {
+class FunctionBodySkippingTransformTests(
+    useFir: Boolean
+) : FunctionBodySkippingTransformTestsBase(useFir) {
     @Test
     fun testIfInLambda(): Unit = comparisonPropagation(
         """
@@ -839,11 +840,11 @@
                 traceEventStart(<>, %changed, -1, <>)
               }
               Call(%composer, 0)
-              val tmp0_iterator = 0 .. 1.iterator()
-              while (tmp0_iterator.hasNext()) {
+              val <iterator> = 0 .. 1.iterator()
+              while (<iterator>.hasNext()) {
                 %composer.startReplaceableGroup(<>)
                 sourceInformation(%composer, "<Call()>,<Call()>")
-                val index = tmp0_iterator.next()
+                val index = <iterator>.next()
                 Call(%composer, 0)
                 if (condition()) {
                   %composer.endReplaceableGroup()
@@ -1175,9 +1176,9 @@
               sourceInformation(%composer, "C(B):Test.kt")
               val %dirty = %changed
               %composer.startMovableGroup(<>, values.size)
-              val tmp0_iterator = values.iterator()
-              while (tmp0_iterator.hasNext()) {
-                val value = tmp0_iterator.next()
+              val <iterator> = values.iterator()
+              while (<iterator>.hasNext()) {
+                val value = <iterator>.next()
                 %dirty = %dirty or if (%composer.changed(value)) 0b0100 else 0
               }
               %composer.endMovableGroup()
@@ -1221,9 +1222,9 @@
               sourceInformation(%composer, "C(B):Test.kt")
               val %dirty = %changed
               %composer.startMovableGroup(<>, values.size)
-              val tmp0_iterator = values.iterator()
-              while (tmp0_iterator.hasNext()) {
-                val value = tmp0_iterator.next()
+              val <iterator> = values.iterator()
+              while (<iterator>.hasNext()) {
+                val value = <iterator>.next()
                 %dirty = %dirty or if (%composer.changed(value)) 0b0100 else 0
               }
               %composer.endMovableGroup()
@@ -1481,7 +1482,7 @@
                   traceEventStart(<>, %dirty, -1, <>)
                 }
                 used(y)
-                Wrap(10, composableLambda(%composer, <>, true) { it: Int, %composer: Composer?, %changed: Int ->
+                Wrap(10, composableLambda(%composer, <>, true) { it: ${if (useFir) "@[ParameterName(name = 'x')] " else ""}Int, %composer: Composer?, %changed: Int ->
                   sourceInformation(%composer, "C<A(x)>:Test.kt")
                   val %dirty = %changed
                   if (%changed and 0b1110 === 0) {
@@ -2033,9 +2034,9 @@
                 C(stableTopLevelProp, %composer, 0b0110)
                 C(Companion, %composer, 0b0110)
                 C(Foo.Bar, %composer, 0b0110)
-                C(constInt, %composer, 0b0110)
+                C(${if (!useFir) "constInt" else "123"}, %composer, 0b0110)
                 C(123, %composer, 0b0110)
-                C(123 + 345, %composer, 0b0110)
+                C(${if (!useFir) "123 + 345" else "468"}, %composer, 0b0110)
                 C(x, %composer, 0b0110)
                 C(x * 123, %composer, 0b0110)
                 if (isTraceInProgress()) {
@@ -2694,13 +2695,13 @@
                 sourceInformation(%composer, "C:Test.kt")
                 val %dirty = %changed
                 if (%changed and 0b1110 === 0) {
-                  %dirty = %dirty or if (%composer.changed(%this%null)) 0b0100 else 0b0010
+                  %dirty = %dirty or if (%composer.changed(<this>)) 0b0100 else 0b0010
                 }
                 if (%dirty and 0b01011011 !== 0b00010010 || !%composer.skipping) {
                   if (isTraceInProgress()) {
                     traceEventStart(<>, %changed, -1, <>)
                   }
-                  used(%this%null.x)
+                  used(${if (useFir) "x" else "<this>.x"})
                   if (isTraceInProgress()) {
                     traceEventEnd()
                   }
@@ -2726,13 +2727,13 @@
                 sourceInformation(%composer, "C:Test.kt")
                 val %dirty = %changed
                 if (%changed and 0b1110 === 0) {
-                  %dirty = %dirty or if (%composer.changed(%this%null)) 0b0100 else 0b0010
+                  %dirty = %dirty or if (%composer.changed(<this>)) 0b0100 else 0b0010
                 }
                 if (%dirty and 0b01011011 !== 0b00010010 || !%composer.skipping) {
                   if (isTraceInProgress()) {
                     traceEventStart(<>, %changed, -1, <>)
                   }
-                  used(%this%null.x)
+                  used(${if (useFir) "x" else "<this>.x"})
                   if (isTraceInProgress()) {
                     traceEventEnd()
                   }
@@ -3793,7 +3794,7 @@
                 sourceInformation(%composer, "C:Test.kt")
                 val %dirty = %changed
                 if (%changed and 0b1110 === 0) {
-                  %dirty = %dirty or if (%composer.changed(%this%null)) 0b0100 else 0b0010
+                  %dirty = %dirty or if (%composer.changed(<this>)) 0b0100 else 0b0010
                 }
                 if (%changed and 0b01110000 === 0) {
                   %dirty = %dirty or if (%composer.changed(it)) 0b00100000 else 0b00010000
@@ -3802,7 +3803,7 @@
                   if (isTraceInProgress()) {
                     traceEventStart(<>, %changed, -1, <>)
                   }
-                  used(%this%null)
+                  used(<this>)
                   used(it)
                   if (isTraceInProgress()) {
                     traceEventEnd()
@@ -3839,9 +3840,9 @@
                 %dirty = %dirty or if (%composer.changed(state)) 0b0100 else 0b0010
               }
               %composer.startMovableGroup(<>, values.size)
-              val tmp0_iterator = values.iterator()
-              while (tmp0_iterator.hasNext()) {
-                val value = tmp0_iterator.next()
+              val <iterator> = values.iterator()
+              while (<iterator>.hasNext()) {
+                val value = <iterator>.next()
                 %dirty = %dirty or if (%composer.changed(value)) 0b00100000 else 0
               }
               %composer.endMovableGroup()
@@ -3908,7 +3909,7 @@
                 if (isTraceInProgress()) {
                   traceEventStart(<>, %changed, -1, <>)
                 }
-                Bug(listOf(1, 2, 3), { it: Int, %composer: Composer?, %changed: Int ->
+                Bug(listOf(1, 2, 3), { it: ${if (useFir) "@[ParameterName(name = 'item')] " else ""}Int, %composer: Composer?, %changed: Int ->
                   sourceInformationMarkerStart(%composer, <>, "C<Text(i...>:Test.kt")
                   Text(it.toString(), %composer, 0)
                   sourceInformationMarkerEnd(%composer)
@@ -3928,9 +3929,9 @@
             fun <T> Bug(items: List<T>, content: Function3<@[ParameterName(name = 'item')] T, Composer, Int, Unit>, %composer: Composer?, %changed: Int) {
               %composer.startReplaceableGroup(<>)
               sourceInformation(%composer, "CC(Bug)P(1)*<conten...>:Test.kt")
-              val tmp0_iterator = items.iterator()
-              while (tmp0_iterator.hasNext()) {
-                val item = tmp0_iterator.next()
+              val <iterator> = items.iterator()
+              while (<iterator>.hasNext()) {
+                val item = <iterator>.next()
                 content(item, %composer, 0b01110000 and %changed)
               }
               %composer.endReplaceableGroup()
@@ -3945,7 +3946,9 @@
     )
 }
 
-class FunctionBodySkippingTransformTestsNoSource : FunctionBodySkippingTransformTestsBase() {
+class FunctionBodySkippingTransformTestsNoSource(
+    useFir: Boolean
+) : FunctionBodySkippingTransformTestsBase(useFir) {
     override fun CompilerConfiguration.updateConfiguration() {
         put(ComposeConfiguration.SOURCE_INFORMATION_ENABLED_KEY, false)
     }
@@ -4034,7 +4037,7 @@
                 if (isTraceInProgress()) {
                   traceEventStart(<>, %changed, -1, <>)
                 }
-                Bug(listOf(1, 2, 3), { it: Int, %composer: Composer?, %changed: Int ->
+                Bug(listOf(1, 2, 3), { it: ${if (useFir) "@[ParameterName(name = 'item')] " else ""}Int, %composer: Composer?, %changed: Int ->
                   Text(it.toString(), %composer, 0)
                 }, %composer, 0b0110)
                 if (isTraceInProgress()) {
@@ -4051,9 +4054,9 @@
             @ComposableInferredTarget(scheme = "[0[0]]")
             private fun <T> Bug(items: List<T>, content: Function3<@[ParameterName(name = 'item')] T, Composer, Int, Unit>, %composer: Composer?, %changed: Int) {
               %composer.startReplaceableGroup(<>)
-              val tmp0_iterator = items.iterator()
-              while (tmp0_iterator.hasNext()) {
-                val item = tmp0_iterator.next()
+              val <iterator> = items.iterator()
+              while (<iterator>.hasNext()) {
+                val item = <iterator>.next()
                 content(item, %composer, 0b01110000 and %changed)
               }
               %composer.endReplaceableGroup()
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionalInterfaceExtensionReceiverTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionalInterfaceExtensionReceiverTransformTests.kt
deleted file mode 100644
index fff78f4..0000000
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionalInterfaceExtensionReceiverTransformTests.kt
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.compiler.plugins.kotlin
-
-import org.junit.Test
-
-class FunctionalInterfaceExtensionReceiverTransformTests : AbstractControlFlowTransformTests() {
-    @Test
-    fun testFunctionalInterfaceWithExtensionReceiverTransformation() {
-        verifyComposeIrTransform(
-            source = """
-                import androidx.compose.runtime.*
-                fun interface TestContent {
-                    @Composable
-                    fun String.Content()
-                }
-                @Composable
-                fun Test(content: TestContent) {
-                    with(content) {
-                        "".Content()
-                    }
-                }
-
-                @Composable
-                fun CallTest() {
-                    Test { this.length }
-                }
-            """.trimIndent(),
-            expectedTransformed = """
-            interface TestContent {
-              @Composable
-              abstract fun String.Content(%composer: Composer?, %changed: Int)
-            }
-            @Composable
-            @ComposableInferredTarget(scheme = "[0[0]]")
-            fun Test(content: TestContent, %composer: Composer?, %changed: Int) {
-              %composer = %composer.startRestartGroup(<>)
-              sourceInformation(%composer, "C(Test)*<Conten...>:Test.kt")
-              val %dirty = %changed
-              if (%changed and 0b1110 === 0) {
-                %dirty = %dirty or if (%composer.changed(content)) 0b0100 else 0b0010
-              }
-              if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
-                if (isTraceInProgress()) {
-                  traceEventStart(<>, %changed, -1, <>)
-                }
-                with(content) {
-                  %this%with.Content(%composer, 0b0110)
-                }
-                if (isTraceInProgress()) {
-                  traceEventEnd()
-                }
-              } else {
-                %composer.skipToGroupEnd()
-              }
-              %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
-                Test(content, %composer, updateChangedFlags(%changed or 0b0001))
-              }
-            }
-            @Composable
-            fun CallTest(%composer: Composer?, %changed: Int) {
-              %composer = %composer.startRestartGroup(<>)
-              sourceInformation(%composer, "C(CallTest)<Test>:Test.kt")
-              if (%changed !== 0 || !%composer.skipping) {
-                if (isTraceInProgress()) {
-                  traceEventStart(<>, %changed, -1, <>)
-                }
-                Test(class <no name provided> : TestContent {
-                  @Composable
-                  override fun Content(%this%Test: String, %composer: Composer?, %changed: Int) {
-                    %composer = %composer.startRestartGroup(<>)
-                    sourceInformation(%composer, "C(Content):Test.kt")
-                    val %dirty = %changed
-                    if (%changed and 0b1110 === 0) {
-                      %dirty = %dirty or if (%composer.changed(%this%Test)) 0b0100 else 0b0010
-                    }
-                    if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
-                      if (isTraceInProgress()) {
-                        traceEventStart(<>, %changed, -1, <>)
-                      }
-                      %this%Test.length
-                      if (isTraceInProgress()) {
-                        traceEventEnd()
-                      }
-                    } else {
-                      %composer.skipToGroupEnd()
-                    }
-                    val tmp0_rcvr = <this>
-                    %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
-                      tmp0_rcvr.Content(%this%Test, %composer, updateChangedFlags(%changed or 0b0001))
-                    }
-                  }
-                }
-                <no name provided>(), %composer, 0)
-                if (isTraceInProgress()) {
-                  traceEventEnd()
-                }
-              } else {
-                %composer.skipToGroupEnd()
-              }
-              %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
-                CallTest(%composer, updateChangedFlags(%changed or 0b0001))
-              }
-            }
-            """.trimIndent()
-        )
-    }
-}
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionalInterfaceTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionalInterfaceTransformTests.kt
new file mode 100644
index 0000000..606cc70
--- /dev/null
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionalInterfaceTransformTests.kt
@@ -0,0 +1,390 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.compiler.plugins.kotlin
+
+import org.junit.Test
+
+class FunctionalInterfaceTransformTests(
+    useFir: Boolean
+) : AbstractControlFlowTransformTests(useFir) {
+    @Test
+    fun testFunctionalInterfaceWithExtensionReceiverTransformation() {
+        verifyComposeIrTransform(
+            source = """
+                import androidx.compose.runtime.*
+                fun interface TestContent {
+                    @Composable
+                    fun String.Content()
+                }
+                @Composable
+                fun Test(content: TestContent) {
+                    with(content) {
+                        "".Content()
+                    }
+                }
+
+                @Composable
+                fun CallTest() {
+                    Test { this.length }
+                }
+            """.trimIndent(),
+            expectedTransformed = """
+            interface TestContent {
+              @Composable
+              abstract fun String.Content(%composer: Composer?, %changed: Int)
+            }
+            @Composable
+            @ComposableInferredTarget(scheme = "[0[0]]")
+            fun Test(content: TestContent, %composer: Composer?, %changed: Int) {
+              %composer = %composer.startRestartGroup(<>)
+              sourceInformation(%composer, "C(Test)*<Conten...>:Test.kt")
+              val %dirty = %changed
+              if (%changed and 0b1110 === 0) {
+                %dirty = %dirty or if (%composer.changed(content)) 0b0100 else 0b0010
+              }
+              if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
+                if (isTraceInProgress()) {
+                  traceEventStart(<>, %changed, -1, <>)
+                }
+                with(content) {
+                  %this%with.Content(%composer, 0b0110)
+                }
+                if (isTraceInProgress()) {
+                  traceEventEnd()
+                }
+              } else {
+                %composer.skipToGroupEnd()
+              }
+              %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                Test(content, %composer, updateChangedFlags(%changed or 0b0001))
+              }
+            }
+            @Composable
+            fun CallTest(%composer: Composer?, %changed: Int) {
+              %composer = %composer.startRestartGroup(<>)
+              sourceInformation(%composer, "C(CallTest)<Test>:Test.kt")
+              if (%changed !== 0 || !%composer.skipping) {
+                if (isTraceInProgress()) {
+                  traceEventStart(<>, %changed, -1, <>)
+                }
+                Test(class <no name provided> : TestContent {
+                  @Composable
+                  override fun Content(%this%Test: String, %composer: Composer?, %changed: Int) {
+                    %composer = %composer.startRestartGroup(<>)
+                    sourceInformation(%composer, "C(Content):Test.kt")
+                    val %dirty = %changed
+                    if (%changed and 0b1110 === 0) {
+                      %dirty = %dirty or if (%composer.changed(%this%Test)) 0b0100 else 0b0010
+                    }
+                    if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
+                      if (isTraceInProgress()) {
+                        traceEventStart(<>, %changed, -1, <>)
+                      }
+                      %this%Test.length
+                      if (isTraceInProgress()) {
+                        traceEventEnd()
+                      }
+                    } else {
+                      %composer.skipToGroupEnd()
+                    }
+                    val tmp0_rcvr = <this>
+                    %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                      tmp0_rcvr.Content(%this%Test, %composer, updateChangedFlags(%changed or 0b0001))
+                    }
+                  }
+                }
+                <no name provided>(), %composer, 0)
+                if (isTraceInProgress()) {
+                  traceEventEnd()
+                }
+              } else {
+                %composer.skipToGroupEnd()
+              }
+              %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                CallTest(%composer, updateChangedFlags(%changed or 0b0001))
+              }
+            }
+            """.trimIndent()
+        )
+    }
+
+    @Test
+    fun testFunInterfaces() = verifyComposeIrTransform(
+        """
+            import androidx.compose.runtime.*
+
+            fun interface A {
+                fun compute(value: Int): Unit
+            }
+
+            @Composable
+            fun Example(a: A) {
+                Example { it -> a.compute(it) }
+            }
+        """,
+        """
+            interface A {
+              abstract fun compute(value: Int)
+            }
+            @Composable
+            fun Example(a: A, %composer: Composer?, %changed: Int) {
+              %composer = %composer.startRestartGroup(<>)
+              sourceInformation(%composer, "C(Example)<Exampl...>:Test.kt")
+              val %dirty = %changed
+              if (%changed and 0b1110 === 0) {
+                %dirty = %dirty or if (%composer.changed(a)) 0b0100 else 0b0010
+              }
+              if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
+                if (isTraceInProgress()) {
+                  traceEventStart(<>, %changed, -1, <>)
+                }
+                Example(A { it: Int ->
+                  a.compute(it)
+                }, %composer, 0)
+                if (isTraceInProgress()) {
+                  traceEventEnd()
+                }
+              } else {
+                %composer.skipToGroupEnd()
+              }
+              %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                Example(a, %composer, updateChangedFlags(%changed or 0b0001))
+              }
+            }
+        """
+    )
+
+    @Test
+    fun testComposableFunInterfaces() = verifyComposeIrTransform(
+        """
+            import androidx.compose.runtime.*
+
+            fun interface A {
+                @Composable fun compute(value: Int): Unit
+            }
+            fun Example(a: A) {
+                Example { it -> a.compute(it) }
+            }
+        """,
+        """
+            interface A {
+              @Composable
+              abstract fun compute(value: Int, %composer: Composer?, %changed: Int)
+            }
+            fun Example(a: A) {
+              Example(class <no name provided> : A {
+                @Composable
+                override fun compute(it: Int, %composer: Composer?, %changed: Int) {
+                  %composer = %composer.startRestartGroup(<>)
+                  sourceInformation(%composer, "C(compute)<comput...>:Test.kt")
+                  val %dirty = %changed
+                  if (%changed and 0b1110 === 0) {
+                    %dirty = %dirty or if (%composer.changed(it)) 0b0100 else 0b0010
+                  }
+                  if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
+                    if (isTraceInProgress()) {
+                      traceEventStart(<>, %dirty, -1, <>)
+                    }
+                    a.compute(it, %composer, 0b1110 and %dirty)
+                    if (isTraceInProgress()) {
+                      traceEventEnd()
+                    }
+                  } else {
+                    %composer.skipToGroupEnd()
+                  }
+                  val tmp0_rcvr = <this>
+                  %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                    tmp0_rcvr.compute(it, %composer, updateChangedFlags(%changed or 0b0001))
+                  }
+                }
+              }
+              <no name provided>())
+            }
+        """
+    )
+
+    @Test
+    fun testComposableFunInterfacesInVariance() = verifyComposeIrTransform(
+        """
+            import androidx.compose.runtime.*
+
+            fun interface Consumer<T> {
+                @Composable fun consume(t: T)
+            }
+
+            class Repro<T : Any> {
+                fun test(consumer: Consumer<in T>) {}
+            }
+
+            fun test() {
+                Repro<String>().test { string ->
+                    println(string)
+                }
+            }
+        """,
+        """
+            interface Consumer<T>  {
+              @Composable
+              abstract fun consume(t: T, %composer: Composer?, %changed: Int)
+            }
+            @StabilityInferred(parameters = 0)
+            class Repro<T: Any>  {
+              fun test(consumer: Consumer<in T>) { }
+              static val %stable: Int = 0
+            }
+            fun test() {
+              Repro().test(class <no name provided> : Consumer<Any?> {
+                @Composable
+                override fun consume(string: String, %composer: Composer?, %changed: Int) {
+                  %composer = %composer.startRestartGroup(<>)
+                  sourceInformation(%composer, "C(consume):Test.kt")
+                  val %dirty = %changed
+                  if (%changed and 0b1110 === 0) {
+                    %dirty = %dirty or if (%composer.changed(string)) 0b0100 else 0b0010
+                  }
+                  if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
+                    if (isTraceInProgress()) {
+                      traceEventStart(<>, %changed, -1, <>)
+                    }
+                    println(string)
+                    if (isTraceInProgress()) {
+                      traceEventEnd()
+                    }
+                  } else {
+                    %composer.skipToGroupEnd()
+                  }
+                  val tmp0_rcvr = <this>
+                  %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                    tmp0_rcvr.consume(string, %composer, updateChangedFlags(%changed or 0b0001))
+                  }
+                }
+              }
+              <no name provided>())
+            }
+        """
+    )
+
+    @Test
+    fun testCaptureStableFunInterface() = verifyComposeIrTransform(
+        """
+            import androidx.compose.runtime.*
+
+            fun interface Consumer {
+                fun consume(t: Int)
+            }
+
+            @Composable fun Test(int: Int) {
+                Example {
+                    println(int)
+                }
+            }
+
+            @Composable inline fun Example(consumer: Consumer) {
+            }
+        """,
+        """
+            interface Consumer {
+              abstract fun consume(t: Int)
+            }
+            @Composable
+            fun Test(int: Int, %composer: Composer?, %changed: Int) {
+              %composer = %composer.startRestartGroup(<>)
+              sourceInformation(%composer, "C(Test)<{>,<Exampl...>:Test.kt")
+              val %dirty = %changed
+              if (%changed and 0b1110 === 0) {
+                %dirty = %dirty or if (%composer.changed(int)) 0b0100 else 0b0010
+              }
+              if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
+                if (isTraceInProgress()) {
+                  traceEventStart(<>, %dirty, -1, <>)
+                }
+                Example(remember(int, {
+                  Consumer { it: Int ->
+                    println(int)
+                  }
+                }, %composer, 0b1110 and %dirty), %composer, 0)
+                if (isTraceInProgress()) {
+                  traceEventEnd()
+                }
+              } else {
+                %composer.skipToGroupEnd()
+              }
+              %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                Test(int, %composer, updateChangedFlags(%changed or 0b0001))
+              }
+            }
+            @Composable
+            fun Example(consumer: Consumer, %composer: Composer?, %changed: Int) {
+              %composer.startReplaceableGroup(<>)
+              sourceInformation(%composer, "CC(Example):Test.kt")
+              %composer.endReplaceableGroup()
+            }
+        """
+    )
+
+    @Test
+    fun testNoCaptureFunInterface() = verifyComposeIrTransform(
+        """
+            import androidx.compose.runtime.*
+
+            fun interface Consumer {
+                fun consume(t: Int)
+            }
+
+            @Composable fun Test(int: Int) {
+                Example {
+                    println(it)
+                }
+            }
+
+            @Composable inline fun Example(consumer: Consumer) {
+            }
+        """,
+        """
+            interface Consumer {
+              abstract fun consume(t: Int)
+            }
+            @Composable
+            fun Test(int: Int, %composer: Composer?, %changed: Int) {
+              %composer = %composer.startRestartGroup(<>)
+              sourceInformation(%composer, "C(Test)<Exampl...>:Test.kt")
+              if (%changed and 0b0001 !== 0 || !%composer.skipping) {
+                if (isTraceInProgress()) {
+                  traceEventStart(<>, %changed, -1, <>)
+                }
+                Example(Consumer { it: Int ->
+                  println(it)
+                }, %composer, 0b0110)
+                if (isTraceInProgress()) {
+                  traceEventEnd()
+                }
+              } else {
+                %composer.skipToGroupEnd()
+              }
+              %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                Test(int, %composer, updateChangedFlags(%changed or 0b0001))
+              }
+            }
+            @Composable
+            fun Example(consumer: Consumer, %composer: Composer?, %changed: Int) {
+              %composer.startReplaceableGroup(<>)
+              sourceInformation(%composer, "CC(Example):Test.kt")
+              %composer.endReplaceableGroup()
+            }
+        """
+    )
+}
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/KtxCrossModuleTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/KtxCrossModuleTests.kt
index 52576e3..29a2855 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/KtxCrossModuleTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/KtxCrossModuleTests.kt
@@ -28,16 +28,22 @@
 import org.junit.Test
 import org.junit.rules.TemporaryFolder
 import org.junit.runner.RunWith
-import org.robolectric.RobolectricTestRunner
+import org.robolectric.ParameterizedRobolectricTestRunner
 import org.robolectric.annotation.Config
 
-@RunWith(RobolectricTestRunner::class)
+@RunWith(ParameterizedRobolectricTestRunner::class)
 @Config(
     manifest = Config.NONE,
     minSdk = 23,
     maxSdk = 23
 )
-class KtxCrossModuleTests : AbstractCodegenTest(useFir = false) {
+class KtxCrossModuleTests(useFir: Boolean) : AbstractCodegenTest(useFir) {
+    companion object {
+        @JvmStatic
+        @ParameterizedRobolectricTestRunner.Parameters(name = "useFir = {0}")
+        fun data() = arrayOf<Any>(false, true)
+    }
+
     @Test
     fun testInlineFunctionDefaultArgument() {
         compile(
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/KtxTransformationTest.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/KtxTransformationTest.kt
index b17d8dd..a1cfb0c 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/KtxTransformationTest.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/KtxTransformationTest.kt
@@ -16,12 +16,10 @@
 
 package androidx.compose.compiler.plugins.kotlin
 
+import org.junit.Assume.assumeFalse
 import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
-@RunWith(JUnit4::class)
-class KtxTransformationTest : AbstractCodegenTest(useFir = false) {
+class KtxTransformationTest(useFir: Boolean) : AbstractCodegenTest(useFir) {
 //    b/179279455
 //    @Test
 //    fun testObserveLowering() {
@@ -540,6 +538,9 @@
 
     @Test
     fun testLambdaWithArgs() {
+        // FIR does not support named lambda arguments
+        // We will deprecate this in Compose, see b/281677454
+        assumeFalse(useFir)
         testCompile(
             """
         import androidx.compose.runtime.*
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationRegressionTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationRegressionTests.kt
index 16d0c91..bd58f17 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationRegressionTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationRegressionTests.kt
@@ -17,8 +17,6 @@
 package androidx.compose.compiler.plugins.kotlin
 
 import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 /**
  * This test merely ensures that code gen changes are evaluated against potentially
@@ -28,8 +26,7 @@
  * The Android Studio debugger searches for `ComposableSingletons` classes by name.
  * Any changes to the naming scheme have to be reflected in the Android Studio code.
  */
-@RunWith(JUnit4::class)
-class LambdaMemoizationRegressionTests : AbstractIrTransformTest(useFir = false) {
+class LambdaMemoizationRegressionTests(useFir: Boolean) : AbstractIrTransformTest(useFir) {
     @Test
     fun testNestedComposableSingletonsClass() = verifyComposeIrTransform(
         """
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTests.kt
index 5e0d632..c895f16 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTests.kt
@@ -22,17 +22,23 @@
 import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.robolectric.RobolectricTestRunner
+import org.robolectric.ParameterizedRobolectricTestRunner
 import org.robolectric.Shadows.shadowOf
 import org.robolectric.annotation.Config
 
-@RunWith(RobolectricTestRunner::class)
+@RunWith(ParameterizedRobolectricTestRunner::class)
 @Config(
     manifest = Config.NONE,
     minSdk = 23,
     maxSdk = 23
 )
-class LambdaMemoizationTests : AbstractLoweringTests(useFir = false) {
+class LambdaMemoizationTests(useFir: Boolean) : AbstractLoweringTests(useFir) {
+    companion object {
+        @JvmStatic
+        @ParameterizedRobolectricTestRunner.Parameters(name = "useFir = {0}")
+        fun data() = arrayOf<Any>(false, true)
+    }
+
     @Test
     @Ignore("b/179279455")
     fun nonCapturingEventLambda() = skipping(
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt
index 0eb6a7c..12c05f9 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt
@@ -17,11 +17,8 @@
 package androidx.compose.compiler.plugins.kotlin
 
 import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
-@RunWith(JUnit4::class)
-class LambdaMemoizationTransformTests : AbstractIrTransformTest(useFir = false) {
+class LambdaMemoizationTransformTests(useFir: Boolean) : AbstractIrTransformTest(useFir) {
     @Test
     fun testCapturedThisFromFieldInitializer() = verifyComposeIrTransform(
         """
@@ -320,7 +317,7 @@
                   }
                   %composer.endReplaceableGroup()
                 }
-                AnimatedContent(1.0f, composableLambda(%composer, <>, false) { it: Float, %composer: Composer?, %changed: Int ->
+                AnimatedContent(1.0f, composableLambda(%composer, <>, false) { it: ${if (useFir) "@[ParameterName(name = 'targetState')] " else ""}Float, %composer: Composer?, %changed: Int ->
                   sourceInformation(%composer, "C<Foo()>:Test.kt")
                   if (isTraceInProgress()) {
                     traceEventStart(<>, %changed, -1, <>)
@@ -1123,49 +1120,52 @@
     )
 
     @Test
-    fun testComposableCaptureInDelegates() = verifyComposeIrTransform(
-        """
-            import androidx.compose.runtime.*
+    fun testComposableCaptureInDelegates() {
+        val delegateImplementation = """
+                  val content: Function2<Composer, Int, Unit>
+                    get() {
+                      return <this>.%%delegate_0.content
+                    }"""
+        verifyComposeIrTransform(
+            """
+                import androidx.compose.runtime.*
 
-            class Test(val value: Int) : Delegate by Impl({
-                value
-            })
-        """,
-        """
-            @StabilityInferred(parameters = 0)
-            class Test(val value: Int) : Delegate {
-              private val %%delegate_0: Impl = Impl(composableLambdaInstance(<>, true) { %composer: Composer?, %changed: Int ->
-                sourceInformation(%composer, "C:Test.kt")
-                if (%changed and 0b1011 !== 0b0010 || !%composer.skipping) {
-                  if (isTraceInProgress()) {
-                    traceEventStart(<>, %changed, -1, <>)
+                class Test(val value: Int) : Delegate by Impl({
+                    value
+                })
+            """,
+            """
+                @StabilityInferred(parameters = 0)
+                class Test(val value: Int) : Delegate {${if (useFir) delegateImplementation else ""}
+                  private val %%delegate_0: Impl = Impl(composableLambdaInstance(<>, true) { %composer: Composer?, %changed: Int ->
+                    sourceInformation(%composer, "C:Test.kt")
+                    if (%changed and 0b1011 !== 0b0010 || !%composer.skipping) {
+                      if (isTraceInProgress()) {
+                        traceEventStart(<>, %changed, -1, <>)
+                      }
+                      value
+                      if (isTraceInProgress()) {
+                        traceEventEnd()
+                      }
+                    } else {
+                      %composer.skipToGroupEnd()
+                    }
                   }
-                  value
-                  if (isTraceInProgress()) {
-                    traceEventEnd()
-                  }
-                } else {
-                  %composer.skipToGroupEnd()
+                  )${if (!useFir) delegateImplementation else ""}
+                  static val %stable: Int = 0
                 }
-              }
-              )
-              val content: Function2<Composer, Int, Unit>
-                get() {
-                  return <this>.%%delegate_0.content
+            """,
+            """
+                import androidx.compose.runtime.Composable
+
+                interface Delegate {
+                    val content: @Composable () -> Unit
                 }
-              static val %stable: Int = 0
-            }
-        """,
-        """
-            import androidx.compose.runtime.Composable
 
-            interface Delegate {
-                val content: @Composable () -> Unit
-            }
-
-            class Impl(override val content: @Composable () -> Unit) : Delegate
-        """
-    )
+                class Impl(override val content: @Composable () -> Unit) : Delegate
+            """
+        )
+    }
 
     @Test // Regression validating b/246399235
     fun testB246399235() {
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LiveLiteralCodegenTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LiveLiteralCodegenTests.kt
index 6c68452..2438b0b 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LiveLiteralCodegenTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LiveLiteralCodegenTests.kt
@@ -24,16 +24,22 @@
 import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.robolectric.RobolectricTestRunner
+import org.robolectric.ParameterizedRobolectricTestRunner
 import org.robolectric.annotation.Config
 
-@RunWith(RobolectricTestRunner::class)
+@RunWith(ParameterizedRobolectricTestRunner::class)
 @Config(
     manifest = Config.NONE,
     minSdk = 23,
     maxSdk = 23
 )
-class LiveLiteralCodegenTests : AbstractLoweringTests(useFir = false) {
+class LiveLiteralCodegenTests(useFir: Boolean) : AbstractLoweringTests(useFir) {
+    companion object {
+        @JvmStatic
+        @ParameterizedRobolectricTestRunner.Parameters(name = "useFir = {0}")
+        fun data() = arrayOf<Any>(false, true)
+    }
+
     override fun CompilerConfiguration.updateConfiguration() {
         put(ComposeConfiguration.LIVE_LITERALS_ENABLED_KEY, true)
     }
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LiveLiteralTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LiveLiteralTransformTests.kt
index a95dc1b..e729daf 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LiveLiteralTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LiveLiteralTransformTests.kt
@@ -17,9 +17,11 @@
 package androidx.compose.compiler.plugins.kotlin
 
 import org.jetbrains.kotlin.config.CompilerConfiguration
+import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
 import org.junit.Test
 
-class LiveLiteralTransformTests : AbstractLiveLiteralTransformTests() {
+class LiveLiteralTransformTests(useFir: Boolean) : AbstractLiveLiteralTransformTests(useFir) {
     override fun CompilerConfiguration.updateConfiguration() {
         put(ComposeConfiguration.LIVE_LITERALS_ENABLED_KEY, true)
     }
@@ -48,16 +50,24 @@
     }
 
     @Test
-    fun testDispatchReceiver() = assertKeys(
-        "Int%%this%call-toString%arg-0%call-print%fun-Test",
-        "Int%arg-0%call-print-1%fun-Test"
-    ) {
-        """
+    fun testDispatchReceiver() {
+        // K2 constant folds the toString call.
+        val printOneToStringKey = if (useFir) {
+            "String%arg-0%call-print%fun-Test"
+        } else {
+            "Int%%this%call-toString%arg-0%call-print%fun-Test"
+        }
+        assertKeys(
+            printOneToStringKey,
+            "Int%arg-0%call-print-1%fun-Test"
+        ) {
+            """
         fun Test() {
             print(1.toString())
             print(1)
         }
         """
+        }
     }
 
     @Test
@@ -405,175 +415,260 @@
     )
 
     @Test
-    fun testBasicTransform(): Unit = assertTransform(
-        """
-        """,
-        """
-            fun A() {
-              print(1)
-              print("Hello World")
-              if (true) {
-                print(3 + 4)
-              }
-              if (true) {
-                print(1.0f)
-              }
-              print(3)
-            }
-        """,
-        """
-            fun A() {
-              print(LiveLiterals%TestKt.Int%arg-0%call-print%fun-A())
-              print(LiveLiterals%TestKt.String%arg-0%call-print-1%fun-A())
-              if (LiveLiterals%TestKt.Boolean%cond%if%fun-A()) {
-                print(LiveLiterals%TestKt.Int%%this%call-plus%arg-0%call-print%branch%if%fun-A() + LiveLiterals%TestKt.Int%arg-0%call-plus%arg-0%call-print%branch%if%fun-A())
-              }
-              if (LiveLiterals%TestKt.Boolean%cond%if-1%fun-A()) {
-                print(LiveLiterals%TestKt.Float%arg-0%call-print%branch%if-1%fun-A())
-              }
-              print(LiveLiterals%TestKt.Int%arg-0%call-print-2%fun-A())
-            }
-            @LiveLiteralFileInfo(file = "/Test.kt")
-            internal object LiveLiterals%TestKt {
-              val Int%arg-0%call-print%fun-A: Int = 1
-              var State%Int%arg-0%call-print%fun-A: State<Int>?
-              @LiveLiteralInfo(key = "Int%arg-0%call-print%fun-A", offset = 62)
-              fun Int%arg-0%call-print%fun-A(): Int {
-                if (!isLiveLiteralsEnabled) {
-                  return Int%arg-0%call-print%fun-A
+    fun testBasicTransform() {
+        // String constant start offsets are off by one in K2.
+        // TODO: Inline the non-K2 offset once fixed.
+        val stringConstantOffset = if (useFir) 85 else 86
+        assertTransform(
+            """
+            """,
+            """
+                fun A() {
+                  print(1)
+                  print("Hello World")
+                  if (true) {
+                    print(4)
+                  }
+                  if (true) {
+                    print(1.0f)
+                  }
+                  print(3)
                 }
-                val tmp0 = State%Int%arg-0%call-print%fun-A
-                return if (tmp0 == null) {
-                  val tmp1 = liveLiteral("Int%arg-0%call-print%fun-A", Int%arg-0%call-print%fun-A)
-                  State%Int%arg-0%call-print%fun-A = tmp1
-                  tmp1
-                } else {
-                  tmp0
+            """,
+            """
+                fun A() {
+                  print(LiveLiterals%TestKt.Int%arg-0%call-print%fun-A())
+                  print(LiveLiterals%TestKt.String%arg-0%call-print-1%fun-A())
+                  if (LiveLiterals%TestKt.Boolean%cond%if%fun-A()) {
+                    print(LiveLiterals%TestKt.Int%arg-0%call-print%branch%if%fun-A())
+                  }
+                  if (LiveLiterals%TestKt.Boolean%cond%if-1%fun-A()) {
+                    print(LiveLiterals%TestKt.Float%arg-0%call-print%branch%if-1%fun-A())
+                  }
+                  print(LiveLiterals%TestKt.Int%arg-0%call-print-2%fun-A())
                 }
-                .value
-              }
-              val String%arg-0%call-print-1%fun-A: String = "Hello World"
-              var State%String%arg-0%call-print-1%fun-A: State<String>?
-              @LiveLiteralInfo(key = "String%arg-0%call-print-1%fun-A", offset = 74)
-              fun String%arg-0%call-print-1%fun-A(): String {
-                if (!isLiveLiteralsEnabled) {
-                  return String%arg-0%call-print-1%fun-A
+                @LiveLiteralFileInfo(file = "/Test.kt")
+                internal object LiveLiterals%TestKt {
+                  val Int%arg-0%call-print%fun-A: Int = 1
+                  var State%Int%arg-0%call-print%fun-A: State<Int>?
+                  @LiveLiteralInfo(key = "Int%arg-0%call-print%fun-A", offset = 70)
+                  fun Int%arg-0%call-print%fun-A(): Int {
+                    if (!isLiveLiteralsEnabled) {
+                      return Int%arg-0%call-print%fun-A
+                    }
+                    val tmp0 = State%Int%arg-0%call-print%fun-A
+                    return if (tmp0 == null) {
+                      val tmp1 = liveLiteral("Int%arg-0%call-print%fun-A", Int%arg-0%call-print%fun-A)
+                      State%Int%arg-0%call-print%fun-A = tmp1
+                      tmp1
+                    } else {
+                      tmp0
+                    }
+                    .value
+                  }
+                  val String%arg-0%call-print-1%fun-A: String = "Hello World"
+                  var State%String%arg-0%call-print-1%fun-A: State<String>?
+                  @LiveLiteralInfo(key = "String%arg-0%call-print-1%fun-A", offset = $stringConstantOffset)
+                  fun String%arg-0%call-print-1%fun-A(): String {
+                    if (!isLiveLiteralsEnabled) {
+                      return String%arg-0%call-print-1%fun-A
+                    }
+                    val tmp0 = State%String%arg-0%call-print-1%fun-A
+                    return if (tmp0 == null) {
+                      val tmp1 = liveLiteral("String%arg-0%call-print-1%fun-A", String%arg-0%call-print-1%fun-A)
+                      State%String%arg-0%call-print-1%fun-A = tmp1
+                      tmp1
+                    } else {
+                      tmp0
+                    }
+                    .value
+                  }
+                  val Boolean%cond%if%fun-A: Boolean = true
+                  var State%Boolean%cond%if%fun-A: State<Boolean>?
+                  @LiveLiteralInfo(key = "Boolean%cond%if%fun-A", offset = 110)
+                  fun Boolean%cond%if%fun-A(): Boolean {
+                    if (!isLiveLiteralsEnabled) {
+                      return Boolean%cond%if%fun-A
+                    }
+                    val tmp0 = State%Boolean%cond%if%fun-A
+                    return if (tmp0 == null) {
+                      val tmp1 = liveLiteral("Boolean%cond%if%fun-A", Boolean%cond%if%fun-A)
+                      State%Boolean%cond%if%fun-A = tmp1
+                      tmp1
+                    } else {
+                      tmp0
+                    }
+                    .value
+                  }
+                  val Int%arg-0%call-print%branch%if%fun-A: Int = 4
+                  var State%Int%arg-0%call-print%branch%if%fun-A: State<Int>?
+                  @LiveLiteralInfo(key = "Int%arg-0%call-print%branch%if%fun-A", offset = 132)
+                  fun Int%arg-0%call-print%branch%if%fun-A(): Int {
+                    if (!isLiveLiteralsEnabled) {
+                      return Int%arg-0%call-print%branch%if%fun-A
+                    }
+                    val tmp0 = State%Int%arg-0%call-print%branch%if%fun-A
+                    return if (tmp0 == null) {
+                      val tmp1 = liveLiteral("Int%arg-0%call-print%branch%if%fun-A", Int%arg-0%call-print%branch%if%fun-A)
+                      State%Int%arg-0%call-print%branch%if%fun-A = tmp1
+                      tmp1
+                    } else {
+                      tmp0
+                    }
+                    .value
+                  }
+                  val Boolean%cond%if-1%fun-A: Boolean = true
+                  var State%Boolean%cond%if-1%fun-A: State<Boolean>?
+                  @LiveLiteralInfo(key = "Boolean%cond%if-1%fun-A", offset = 153)
+                  fun Boolean%cond%if-1%fun-A(): Boolean {
+                    if (!isLiveLiteralsEnabled) {
+                      return Boolean%cond%if-1%fun-A
+                    }
+                    val tmp0 = State%Boolean%cond%if-1%fun-A
+                    return if (tmp0 == null) {
+                      val tmp1 = liveLiteral("Boolean%cond%if-1%fun-A", Boolean%cond%if-1%fun-A)
+                      State%Boolean%cond%if-1%fun-A = tmp1
+                      tmp1
+                    } else {
+                      tmp0
+                    }
+                    .value
+                  }
+                  val Float%arg-0%call-print%branch%if-1%fun-A: Float = 1.0f
+                  var State%Float%arg-0%call-print%branch%if-1%fun-A: State<Float>?
+                  @LiveLiteralInfo(key = "Float%arg-0%call-print%branch%if-1%fun-A", offset = 175)
+                  fun Float%arg-0%call-print%branch%if-1%fun-A(): Float {
+                    if (!isLiveLiteralsEnabled) {
+                      return Float%arg-0%call-print%branch%if-1%fun-A
+                    }
+                    val tmp0 = State%Float%arg-0%call-print%branch%if-1%fun-A
+                    return if (tmp0 == null) {
+                      val tmp1 = liveLiteral("Float%arg-0%call-print%branch%if-1%fun-A", Float%arg-0%call-print%branch%if-1%fun-A)
+                      State%Float%arg-0%call-print%branch%if-1%fun-A = tmp1
+                      tmp1
+                    } else {
+                      tmp0
+                    }
+                    .value
+                  }
+                  val Int%arg-0%call-print-2%fun-A: Int = 3
+                  var State%Int%arg-0%call-print-2%fun-A: State<Int>?
+                  @LiveLiteralInfo(key = "Int%arg-0%call-print-2%fun-A", offset = 201)
+                  fun Int%arg-0%call-print-2%fun-A(): Int {
+                    if (!isLiveLiteralsEnabled) {
+                      return Int%arg-0%call-print-2%fun-A
+                    }
+                    val tmp0 = State%Int%arg-0%call-print-2%fun-A
+                    return if (tmp0 == null) {
+                      val tmp1 = liveLiteral("Int%arg-0%call-print-2%fun-A", Int%arg-0%call-print-2%fun-A)
+                      State%Int%arg-0%call-print-2%fun-A = tmp1
+                      tmp1
+                    } else {
+                      tmp0
+                    }
+                    .value
+                  }
                 }
-                val tmp0 = State%String%arg-0%call-print-1%fun-A
-                return if (tmp0 == null) {
-                  val tmp1 = liveLiteral("String%arg-0%call-print-1%fun-A", String%arg-0%call-print-1%fun-A)
-                  State%String%arg-0%call-print-1%fun-A = tmp1
-                  tmp1
-                } else {
-                  tmp0
+            """
+        )
+    }
+
+    @Test
+    fun testBasicTransformConstantFoldingK1() {
+        // K1 does not constant fold.
+        assumeFalse(useFir)
+        assertTransform(
+            """
+            """,
+            """
+                fun A() {
+                    print(3 + 4)
                 }
-                .value
-              }
-              val Boolean%cond%if%fun-A: Boolean = true
-              var State%Boolean%cond%if%fun-A: State<Boolean>?
-              @LiveLiteralInfo(key = "Boolean%cond%if%fun-A", offset = 94)
-              fun Boolean%cond%if%fun-A(): Boolean {
-                if (!isLiveLiteralsEnabled) {
-                  return Boolean%cond%if%fun-A
+            """,
+            """
+                fun A() {
+                  print(LiveLiterals%TestKt.Int%%this%call-plus%arg-0%call-print%fun-A() + LiveLiterals%TestKt.Int%arg-0%call-plus%arg-0%call-print%fun-A())
                 }
-                val tmp0 = State%Boolean%cond%if%fun-A
-                return if (tmp0 == null) {
-                  val tmp1 = liveLiteral("Boolean%cond%if%fun-A", Boolean%cond%if%fun-A)
-                  State%Boolean%cond%if%fun-A = tmp1
-                  tmp1
-                } else {
-                  tmp0
+                @LiveLiteralFileInfo(file = "/Test.kt")
+                internal object LiveLiterals%TestKt {
+                  val Int%%this%call-plus%arg-0%call-print%fun-A: Int = 3
+                  var State%Int%%this%call-plus%arg-0%call-print%fun-A: State<Int>?
+                  @LiveLiteralInfo(key = "Int%%this%call-plus%arg-0%call-print%fun-A", offset = 72)
+                  fun Int%%this%call-plus%arg-0%call-print%fun-A(): Int {
+                    if (!isLiveLiteralsEnabled) {
+                      return Int%%this%call-plus%arg-0%call-print%fun-A
+                    }
+                    val tmp0 = State%Int%%this%call-plus%arg-0%call-print%fun-A
+                    return if (tmp0 == null) {
+                      val tmp1 = liveLiteral("Int%%this%call-plus%arg-0%call-print%fun-A", Int%%this%call-plus%arg-0%call-print%fun-A)
+                      State%Int%%this%call-plus%arg-0%call-print%fun-A = tmp1
+                      tmp1
+                    } else {
+                      tmp0
+                    }
+                    .value
+                  }
+                  val Int%arg-0%call-plus%arg-0%call-print%fun-A: Int = 4
+                  var State%Int%arg-0%call-plus%arg-0%call-print%fun-A: State<Int>?
+                  @LiveLiteralInfo(key = "Int%arg-0%call-plus%arg-0%call-print%fun-A", offset = 76)
+                  fun Int%arg-0%call-plus%arg-0%call-print%fun-A(): Int {
+                    if (!isLiveLiteralsEnabled) {
+                      return Int%arg-0%call-plus%arg-0%call-print%fun-A
+                    }
+                    val tmp0 = State%Int%arg-0%call-plus%arg-0%call-print%fun-A
+                    return if (tmp0 == null) {
+                      val tmp1 = liveLiteral("Int%arg-0%call-plus%arg-0%call-print%fun-A", Int%arg-0%call-plus%arg-0%call-print%fun-A)
+                      State%Int%arg-0%call-plus%arg-0%call-print%fun-A = tmp1
+                      tmp1
+                    } else {
+                      tmp0
+                    }
+                    .value
+                  }
                 }
-                .value
-              }
-              val Int%%this%call-plus%arg-0%call-print%branch%if%fun-A: Int = 3
-              var State%Int%%this%call-plus%arg-0%call-print%branch%if%fun-A: State<Int>?
-              @LiveLiteralInfo(key = "Int%%this%call-plus%arg-0%call-print%branch%if%fun-A", offset = 112)
-              fun Int%%this%call-plus%arg-0%call-print%branch%if%fun-A(): Int {
-                if (!isLiveLiteralsEnabled) {
-                  return Int%%this%call-plus%arg-0%call-print%branch%if%fun-A
+            """
+        )
+    }
+
+    @Test
+    fun testBasicTransformConstantFoldingK2() {
+        // K2 constant folds.
+        assumeTrue(useFir)
+        assertTransform(
+            """
+            """,
+            """
+                fun A() {
+                    print(3 + 4)
                 }
-                val tmp0 = State%Int%%this%call-plus%arg-0%call-print%branch%if%fun-A
-                return if (tmp0 == null) {
-                  val tmp1 = liveLiteral("Int%%this%call-plus%arg-0%call-print%branch%if%fun-A", Int%%this%call-plus%arg-0%call-print%branch%if%fun-A)
-                  State%Int%%this%call-plus%arg-0%call-print%branch%if%fun-A = tmp1
-                  tmp1
-                } else {
-                  tmp0
+            """,
+            """
+                fun A() {
+                  print(LiveLiterals%TestKt.Int%arg-0%call-print%fun-A())
                 }
-                .value
-              }
-              val Int%arg-0%call-plus%arg-0%call-print%branch%if%fun-A: Int = 4
-              var State%Int%arg-0%call-plus%arg-0%call-print%branch%if%fun-A: State<Int>?
-              @LiveLiteralInfo(key = "Int%arg-0%call-plus%arg-0%call-print%branch%if%fun-A", offset = 116)
-              fun Int%arg-0%call-plus%arg-0%call-print%branch%if%fun-A(): Int {
-                if (!isLiveLiteralsEnabled) {
-                  return Int%arg-0%call-plus%arg-0%call-print%branch%if%fun-A
+                @LiveLiteralFileInfo(file = "/Test.kt")
+                internal object LiveLiterals%TestKt {
+                  val Int%arg-0%call-print%fun-A: Int = 7
+                  var State%Int%arg-0%call-print%fun-A: State<Int>?
+                  @LiveLiteralInfo(key = "Int%arg-0%call-print%fun-A", offset = 74)
+                  fun Int%arg-0%call-print%fun-A(): Int {
+                    if (!isLiveLiteralsEnabled) {
+                      return Int%arg-0%call-print%fun-A
+                    }
+                    val tmp0 = State%Int%arg-0%call-print%fun-A
+                    return if (tmp0 == null) {
+                      val tmp1 = liveLiteral("Int%arg-0%call-print%fun-A", Int%arg-0%call-print%fun-A)
+                      State%Int%arg-0%call-print%fun-A = tmp1
+                      tmp1
+                    } else {
+                      tmp0
+                    }
+                    .value
+                  }
                 }
-                val tmp0 = State%Int%arg-0%call-plus%arg-0%call-print%branch%if%fun-A
-                return if (tmp0 == null) {
-                  val tmp1 = liveLiteral("Int%arg-0%call-plus%arg-0%call-print%branch%if%fun-A", Int%arg-0%call-plus%arg-0%call-print%branch%if%fun-A)
-                  State%Int%arg-0%call-plus%arg-0%call-print%branch%if%fun-A = tmp1
-                  tmp1
-                } else {
-                  tmp0
-                }
-                .value
-              }
-              val Boolean%cond%if-1%fun-A: Boolean = true
-              var State%Boolean%cond%if-1%fun-A: State<Boolean>?
-              @LiveLiteralInfo(key = "Boolean%cond%if-1%fun-A", offset = 129)
-              fun Boolean%cond%if-1%fun-A(): Boolean {
-                if (!isLiveLiteralsEnabled) {
-                  return Boolean%cond%if-1%fun-A
-                }
-                val tmp0 = State%Boolean%cond%if-1%fun-A
-                return if (tmp0 == null) {
-                  val tmp1 = liveLiteral("Boolean%cond%if-1%fun-A", Boolean%cond%if-1%fun-A)
-                  State%Boolean%cond%if-1%fun-A = tmp1
-                  tmp1
-                } else {
-                  tmp0
-                }
-                .value
-              }
-              val Float%arg-0%call-print%branch%if-1%fun-A: Float = 1.0f
-              var State%Float%arg-0%call-print%branch%if-1%fun-A: State<Float>?
-              @LiveLiteralInfo(key = "Float%arg-0%call-print%branch%if-1%fun-A", offset = 147)
-              fun Float%arg-0%call-print%branch%if-1%fun-A(): Float {
-                if (!isLiveLiteralsEnabled) {
-                  return Float%arg-0%call-print%branch%if-1%fun-A
-                }
-                val tmp0 = State%Float%arg-0%call-print%branch%if-1%fun-A
-                return if (tmp0 == null) {
-                  val tmp1 = liveLiteral("Float%arg-0%call-print%branch%if-1%fun-A", Float%arg-0%call-print%branch%if-1%fun-A)
-                  State%Float%arg-0%call-print%branch%if-1%fun-A = tmp1
-                  tmp1
-                } else {
-                  tmp0
-                }
-                .value
-              }
-              val Int%arg-0%call-print-2%fun-A: Int = 3
-              var State%Int%arg-0%call-print-2%fun-A: State<Int>?
-              @LiveLiteralInfo(key = "Int%arg-0%call-print-2%fun-A", offset = 165)
-              fun Int%arg-0%call-print-2%fun-A(): Int {
-                if (!isLiveLiteralsEnabled) {
-                  return Int%arg-0%call-print-2%fun-A
-                }
-                val tmp0 = State%Int%arg-0%call-print-2%fun-A
-                return if (tmp0 == null) {
-                  val tmp1 = liveLiteral("Int%arg-0%call-print-2%fun-A", Int%arg-0%call-print-2%fun-A)
-                  State%Int%arg-0%call-print-2%fun-A = tmp1
-                  tmp1
-                } else {
-                  tmp0
-                }
-                .value
-              }
-            }
-        """
-    )
+            """
+        )
+    }
 
     @Test
     fun testComposeIrSkippingWithDefaultsRelease() = verifyComposeIrTransform(
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LiveLiteralV2TransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LiveLiteralV2TransformTests.kt
index 0b42755..d124150 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LiveLiteralV2TransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LiveLiteralV2TransformTests.kt
@@ -17,9 +17,11 @@
 package androidx.compose.compiler.plugins.kotlin
 
 import org.jetbrains.kotlin.config.CompilerConfiguration
+import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
 import org.junit.Test
 
-class LiveLiteralV2TransformTests : AbstractLiveLiteralTransformTests() {
+class LiveLiteralV2TransformTests(useFir: Boolean) : AbstractLiveLiteralTransformTests(useFir) {
     override fun CompilerConfiguration.updateConfiguration() {
         put(ComposeConfiguration.LIVE_LITERALS_V2_ENABLED_KEY, true)
     }
@@ -48,16 +50,24 @@
     }
 
     @Test
-    fun testDispatchReceiver() = assertKeys(
-        "Int%%this%call-toString%arg-0%call-print%fun-Test",
-        "Int%arg-0%call-print-1%fun-Test"
-    ) {
-        """
+    fun testDispatchReceiver() {
+        // K2 constant folds `1.toString`.
+        val printOneToStringKey = if (useFir) {
+            "String%arg-0%call-print%fun-Test"
+        } else {
+            "Int%%this%call-toString%arg-0%call-print%fun-Test"
+        }
+        assertKeys(
+            printOneToStringKey,
+            "Int%arg-0%call-print-1%fun-Test"
+        ) {
+            """
         fun Test() {
             print(1.toString())
             print(1)
         }
-        """
+            """
+        }
     }
 
     @Test
@@ -406,176 +416,263 @@
     )
 
     @Test
-    fun testBasicTransform(): Unit = assertTransform(
-        """
-        """,
-        """
-            fun A() {
-              print(1)
-              print("Hello World")
-              if (true) {
-                print(3 + 4)
-              }
-              if (true) {
-                print(1.0f)
-              }
-              print(3)
-            }
-        """,
-        """
-            fun A() {
-              print(LiveLiterals%TestKt.Int%arg-0%call-print%fun-A())
-              print(LiveLiterals%TestKt.String%arg-0%call-print-1%fun-A())
-              if (LiveLiterals%TestKt.Boolean%cond%if%fun-A()) {
-                print(LiveLiterals%TestKt.Int%%this%call-plus%arg-0%call-print%branch%if%fun-A() + LiveLiterals%TestKt.Int%arg-0%call-plus%arg-0%call-print%branch%if%fun-A())
-              }
-              if (LiveLiterals%TestKt.Boolean%cond%if-1%fun-A()) {
-                print(LiveLiterals%TestKt.Float%arg-0%call-print%branch%if-1%fun-A())
-              }
-              print(LiveLiterals%TestKt.Int%arg-0%call-print-2%fun-A())
-            }
-            @LiveLiteralFileInfo(file = "/Test.kt")
-            internal object LiveLiterals%TestKt {
-              val enabled: Boolean = false
-              val Int%arg-0%call-print%fun-A: Int = 1
-              var State%Int%arg-0%call-print%fun-A: State<Int>?
-              @LiveLiteralInfo(key = "Int%arg-0%call-print%fun-A", offset = 62)
-              fun Int%arg-0%call-print%fun-A(): Int {
-                if (!enabled) {
-                  return Int%arg-0%call-print%fun-A
+    fun testBasicTransform() {
+        // String constant start offsets are off by one in K2.
+        // TODO: Inline the non-K2 offset once fixed.
+        val stringConstantOffset = if (useFir) 85 else 86
+        assertTransform(
+            """
+            """,
+            """
+                fun A() {
+                  print(1)
+                  print("Hello World")
+                  if (true) {
+                    print(7)
+                  }
+                  if (true) {
+                    print(1.0f)
+                  }
+                  print(3)
                 }
-                val tmp0 = State%Int%arg-0%call-print%fun-A
-                return if (tmp0 == null) {
-                  val tmp1 = liveLiteral("Int%arg-0%call-print%fun-A", Int%arg-0%call-print%fun-A)
-                  State%Int%arg-0%call-print%fun-A = tmp1
-                  tmp1
-                } else {
-                  tmp0
+            """,
+            """
+                fun A() {
+                  print(LiveLiterals%TestKt.Int%arg-0%call-print%fun-A())
+                  print(LiveLiterals%TestKt.String%arg-0%call-print-1%fun-A())
+                  if (LiveLiterals%TestKt.Boolean%cond%if%fun-A()) {
+                    print(LiveLiterals%TestKt.Int%arg-0%call-print%branch%if%fun-A())
+                  }
+                  if (LiveLiterals%TestKt.Boolean%cond%if-1%fun-A()) {
+                    print(LiveLiterals%TestKt.Float%arg-0%call-print%branch%if-1%fun-A())
+                  }
+                  print(LiveLiterals%TestKt.Int%arg-0%call-print-2%fun-A())
                 }
-                .value
-              }
-              val String%arg-0%call-print-1%fun-A: String = "Hello World"
-              var State%String%arg-0%call-print-1%fun-A: State<String>?
-              @LiveLiteralInfo(key = "String%arg-0%call-print-1%fun-A", offset = 74)
-              fun String%arg-0%call-print-1%fun-A(): String {
-                if (!enabled) {
-                  return String%arg-0%call-print-1%fun-A
+                @LiveLiteralFileInfo(file = "/Test.kt")
+                internal object LiveLiterals%TestKt {
+                  val enabled: Boolean = false
+                  val Int%arg-0%call-print%fun-A: Int = 1
+                  var State%Int%arg-0%call-print%fun-A: State<Int>?
+                  @LiveLiteralInfo(key = "Int%arg-0%call-print%fun-A", offset = 70)
+                  fun Int%arg-0%call-print%fun-A(): Int {
+                    if (!enabled) {
+                      return Int%arg-0%call-print%fun-A
+                    }
+                    val tmp0 = State%Int%arg-0%call-print%fun-A
+                    return if (tmp0 == null) {
+                      val tmp1 = liveLiteral("Int%arg-0%call-print%fun-A", Int%arg-0%call-print%fun-A)
+                      State%Int%arg-0%call-print%fun-A = tmp1
+                      tmp1
+                    } else {
+                      tmp0
+                    }
+                    .value
+                  }
+                  val String%arg-0%call-print-1%fun-A: String = "Hello World"
+                  var State%String%arg-0%call-print-1%fun-A: State<String>?
+                  @LiveLiteralInfo(key = "String%arg-0%call-print-1%fun-A", offset = $stringConstantOffset)
+                  fun String%arg-0%call-print-1%fun-A(): String {
+                    if (!enabled) {
+                      return String%arg-0%call-print-1%fun-A
+                    }
+                    val tmp0 = State%String%arg-0%call-print-1%fun-A
+                    return if (tmp0 == null) {
+                      val tmp1 = liveLiteral("String%arg-0%call-print-1%fun-A", String%arg-0%call-print-1%fun-A)
+                      State%String%arg-0%call-print-1%fun-A = tmp1
+                      tmp1
+                    } else {
+                      tmp0
+                    }
+                    .value
+                  }
+                  val Boolean%cond%if%fun-A: Boolean = true
+                  var State%Boolean%cond%if%fun-A: State<Boolean>?
+                  @LiveLiteralInfo(key = "Boolean%cond%if%fun-A", offset = 110)
+                  fun Boolean%cond%if%fun-A(): Boolean {
+                    if (!enabled) {
+                      return Boolean%cond%if%fun-A
+                    }
+                    val tmp0 = State%Boolean%cond%if%fun-A
+                    return if (tmp0 == null) {
+                      val tmp1 = liveLiteral("Boolean%cond%if%fun-A", Boolean%cond%if%fun-A)
+                      State%Boolean%cond%if%fun-A = tmp1
+                      tmp1
+                    } else {
+                      tmp0
+                    }
+                    .value
+                  }
+                  val Int%arg-0%call-print%branch%if%fun-A: Int = 7
+                  var State%Int%arg-0%call-print%branch%if%fun-A: State<Int>?
+                  @LiveLiteralInfo(key = "Int%arg-0%call-print%branch%if%fun-A", offset = 132)
+                  fun Int%arg-0%call-print%branch%if%fun-A(): Int {
+                    if (!enabled) {
+                      return Int%arg-0%call-print%branch%if%fun-A
+                    }
+                    val tmp0 = State%Int%arg-0%call-print%branch%if%fun-A
+                    return if (tmp0 == null) {
+                      val tmp1 = liveLiteral("Int%arg-0%call-print%branch%if%fun-A", Int%arg-0%call-print%branch%if%fun-A)
+                      State%Int%arg-0%call-print%branch%if%fun-A = tmp1
+                      tmp1
+                    } else {
+                      tmp0
+                    }
+                    .value
+                  }
+                  val Boolean%cond%if-1%fun-A: Boolean = true
+                  var State%Boolean%cond%if-1%fun-A: State<Boolean>?
+                  @LiveLiteralInfo(key = "Boolean%cond%if-1%fun-A", offset = 153)
+                  fun Boolean%cond%if-1%fun-A(): Boolean {
+                    if (!enabled) {
+                      return Boolean%cond%if-1%fun-A
+                    }
+                    val tmp0 = State%Boolean%cond%if-1%fun-A
+                    return if (tmp0 == null) {
+                      val tmp1 = liveLiteral("Boolean%cond%if-1%fun-A", Boolean%cond%if-1%fun-A)
+                      State%Boolean%cond%if-1%fun-A = tmp1
+                      tmp1
+                    } else {
+                      tmp0
+                    }
+                    .value
+                  }
+                  val Float%arg-0%call-print%branch%if-1%fun-A: Float = 1.0f
+                  var State%Float%arg-0%call-print%branch%if-1%fun-A: State<Float>?
+                  @LiveLiteralInfo(key = "Float%arg-0%call-print%branch%if-1%fun-A", offset = 175)
+                  fun Float%arg-0%call-print%branch%if-1%fun-A(): Float {
+                    if (!enabled) {
+                      return Float%arg-0%call-print%branch%if-1%fun-A
+                    }
+                    val tmp0 = State%Float%arg-0%call-print%branch%if-1%fun-A
+                    return if (tmp0 == null) {
+                      val tmp1 = liveLiteral("Float%arg-0%call-print%branch%if-1%fun-A", Float%arg-0%call-print%branch%if-1%fun-A)
+                      State%Float%arg-0%call-print%branch%if-1%fun-A = tmp1
+                      tmp1
+                    } else {
+                      tmp0
+                    }
+                    .value
+                  }
+                  val Int%arg-0%call-print-2%fun-A: Int = 3
+                  var State%Int%arg-0%call-print-2%fun-A: State<Int>?
+                  @LiveLiteralInfo(key = "Int%arg-0%call-print-2%fun-A", offset = 201)
+                  fun Int%arg-0%call-print-2%fun-A(): Int {
+                    if (!enabled) {
+                      return Int%arg-0%call-print-2%fun-A
+                    }
+                    val tmp0 = State%Int%arg-0%call-print-2%fun-A
+                    return if (tmp0 == null) {
+                      val tmp1 = liveLiteral("Int%arg-0%call-print-2%fun-A", Int%arg-0%call-print-2%fun-A)
+                      State%Int%arg-0%call-print-2%fun-A = tmp1
+                      tmp1
+                    } else {
+                      tmp0
+                    }
+                    .value
+                  }
                 }
-                val tmp0 = State%String%arg-0%call-print-1%fun-A
-                return if (tmp0 == null) {
-                  val tmp1 = liveLiteral("String%arg-0%call-print-1%fun-A", String%arg-0%call-print-1%fun-A)
-                  State%String%arg-0%call-print-1%fun-A = tmp1
-                  tmp1
-                } else {
-                  tmp0
+            """
+        )
+    }
+
+    @Test
+    fun testBasicTransformConstantFoldingK1() {
+        // K1 does not constant fold.
+        assumeFalse(useFir)
+        assertTransform(
+            """
+            """,
+            """
+                fun A() {
+                    print(3 + 4)
                 }
-                .value
-              }
-              val Boolean%cond%if%fun-A: Boolean = true
-              var State%Boolean%cond%if%fun-A: State<Boolean>?
-              @LiveLiteralInfo(key = "Boolean%cond%if%fun-A", offset = 94)
-              fun Boolean%cond%if%fun-A(): Boolean {
-                if (!enabled) {
-                  return Boolean%cond%if%fun-A
+            """,
+            """
+                fun A() {
+                  print(LiveLiterals%TestKt.Int%%this%call-plus%arg-0%call-print%fun-A() + LiveLiterals%TestKt.Int%arg-0%call-plus%arg-0%call-print%fun-A())
                 }
-                val tmp0 = State%Boolean%cond%if%fun-A
-                return if (tmp0 == null) {
-                  val tmp1 = liveLiteral("Boolean%cond%if%fun-A", Boolean%cond%if%fun-A)
-                  State%Boolean%cond%if%fun-A = tmp1
-                  tmp1
-                } else {
-                  tmp0
+                @LiveLiteralFileInfo(file = "/Test.kt")
+                internal object LiveLiterals%TestKt {
+                  val enabled: Boolean = false
+                  val Int%%this%call-plus%arg-0%call-print%fun-A: Int = 3
+                  var State%Int%%this%call-plus%arg-0%call-print%fun-A: State<Int>?
+                  @LiveLiteralInfo(key = "Int%%this%call-plus%arg-0%call-print%fun-A", offset = 72)
+                  fun Int%%this%call-plus%arg-0%call-print%fun-A(): Int {
+                    if (!enabled) {
+                      return Int%%this%call-plus%arg-0%call-print%fun-A
+                    }
+                    val tmp0 = State%Int%%this%call-plus%arg-0%call-print%fun-A
+                    return if (tmp0 == null) {
+                      val tmp1 = liveLiteral("Int%%this%call-plus%arg-0%call-print%fun-A", Int%%this%call-plus%arg-0%call-print%fun-A)
+                      State%Int%%this%call-plus%arg-0%call-print%fun-A = tmp1
+                      tmp1
+                    } else {
+                      tmp0
+                    }
+                    .value
+                  }
+                  val Int%arg-0%call-plus%arg-0%call-print%fun-A: Int = 4
+                  var State%Int%arg-0%call-plus%arg-0%call-print%fun-A: State<Int>?
+                  @LiveLiteralInfo(key = "Int%arg-0%call-plus%arg-0%call-print%fun-A", offset = 76)
+                  fun Int%arg-0%call-plus%arg-0%call-print%fun-A(): Int {
+                    if (!enabled) {
+                      return Int%arg-0%call-plus%arg-0%call-print%fun-A
+                    }
+                    val tmp0 = State%Int%arg-0%call-plus%arg-0%call-print%fun-A
+                    return if (tmp0 == null) {
+                      val tmp1 = liveLiteral("Int%arg-0%call-plus%arg-0%call-print%fun-A", Int%arg-0%call-plus%arg-0%call-print%fun-A)
+                      State%Int%arg-0%call-plus%arg-0%call-print%fun-A = tmp1
+                      tmp1
+                    } else {
+                      tmp0
+                    }
+                    .value
+                  }
                 }
-                .value
-              }
-              val Int%%this%call-plus%arg-0%call-print%branch%if%fun-A: Int = 3
-              var State%Int%%this%call-plus%arg-0%call-print%branch%if%fun-A: State<Int>?
-              @LiveLiteralInfo(key = "Int%%this%call-plus%arg-0%call-print%branch%if%fun-A", offset = 112)
-              fun Int%%this%call-plus%arg-0%call-print%branch%if%fun-A(): Int {
-                if (!enabled) {
-                  return Int%%this%call-plus%arg-0%call-print%branch%if%fun-A
+            """
+        )
+    }
+
+    @Test
+    fun testBasicTransformConstantFoldingK2() {
+        // K2 constant folds.
+        assumeTrue(useFir)
+        assertTransform(
+            """
+            """,
+            """
+                fun A() {
+                    print(3 + 4)
                 }
-                val tmp0 = State%Int%%this%call-plus%arg-0%call-print%branch%if%fun-A
-                return if (tmp0 == null) {
-                  val tmp1 = liveLiteral("Int%%this%call-plus%arg-0%call-print%branch%if%fun-A", Int%%this%call-plus%arg-0%call-print%branch%if%fun-A)
-                  State%Int%%this%call-plus%arg-0%call-print%branch%if%fun-A = tmp1
-                  tmp1
-                } else {
-                  tmp0
+            """,
+            """
+                fun A() {
+                  print(LiveLiterals%TestKt.Int%arg-0%call-print%fun-A())
                 }
-                .value
-              }
-              val Int%arg-0%call-plus%arg-0%call-print%branch%if%fun-A: Int = 4
-              var State%Int%arg-0%call-plus%arg-0%call-print%branch%if%fun-A: State<Int>?
-              @LiveLiteralInfo(key = "Int%arg-0%call-plus%arg-0%call-print%branch%if%fun-A", offset = 116)
-              fun Int%arg-0%call-plus%arg-0%call-print%branch%if%fun-A(): Int {
-                if (!enabled) {
-                  return Int%arg-0%call-plus%arg-0%call-print%branch%if%fun-A
+                @LiveLiteralFileInfo(file = "/Test.kt")
+                internal object LiveLiterals%TestKt {
+                  val enabled: Boolean = false
+                  val Int%arg-0%call-print%fun-A: Int = 7
+                  var State%Int%arg-0%call-print%fun-A: State<Int>?
+                  @LiveLiteralInfo(key = "Int%arg-0%call-print%fun-A", offset = 74)
+                  fun Int%arg-0%call-print%fun-A(): Int {
+                    if (!enabled) {
+                      return Int%arg-0%call-print%fun-A
+                    }
+                    val tmp0 = State%Int%arg-0%call-print%fun-A
+                    return if (tmp0 == null) {
+                      val tmp1 = liveLiteral("Int%arg-0%call-print%fun-A", Int%arg-0%call-print%fun-A)
+                      State%Int%arg-0%call-print%fun-A = tmp1
+                      tmp1
+                    } else {
+                      tmp0
+                    }
+                    .value
+                  }
                 }
-                val tmp0 = State%Int%arg-0%call-plus%arg-0%call-print%branch%if%fun-A
-                return if (tmp0 == null) {
-                  val tmp1 = liveLiteral("Int%arg-0%call-plus%arg-0%call-print%branch%if%fun-A", Int%arg-0%call-plus%arg-0%call-print%branch%if%fun-A)
-                  State%Int%arg-0%call-plus%arg-0%call-print%branch%if%fun-A = tmp1
-                  tmp1
-                } else {
-                  tmp0
-                }
-                .value
-              }
-              val Boolean%cond%if-1%fun-A: Boolean = true
-              var State%Boolean%cond%if-1%fun-A: State<Boolean>?
-              @LiveLiteralInfo(key = "Boolean%cond%if-1%fun-A", offset = 129)
-              fun Boolean%cond%if-1%fun-A(): Boolean {
-                if (!enabled) {
-                  return Boolean%cond%if-1%fun-A
-                }
-                val tmp0 = State%Boolean%cond%if-1%fun-A
-                return if (tmp0 == null) {
-                  val tmp1 = liveLiteral("Boolean%cond%if-1%fun-A", Boolean%cond%if-1%fun-A)
-                  State%Boolean%cond%if-1%fun-A = tmp1
-                  tmp1
-                } else {
-                  tmp0
-                }
-                .value
-              }
-              val Float%arg-0%call-print%branch%if-1%fun-A: Float = 1.0f
-              var State%Float%arg-0%call-print%branch%if-1%fun-A: State<Float>?
-              @LiveLiteralInfo(key = "Float%arg-0%call-print%branch%if-1%fun-A", offset = 147)
-              fun Float%arg-0%call-print%branch%if-1%fun-A(): Float {
-                if (!enabled) {
-                  return Float%arg-0%call-print%branch%if-1%fun-A
-                }
-                val tmp0 = State%Float%arg-0%call-print%branch%if-1%fun-A
-                return if (tmp0 == null) {
-                  val tmp1 = liveLiteral("Float%arg-0%call-print%branch%if-1%fun-A", Float%arg-0%call-print%branch%if-1%fun-A)
-                  State%Float%arg-0%call-print%branch%if-1%fun-A = tmp1
-                  tmp1
-                } else {
-                  tmp0
-                }
-                .value
-              }
-              val Int%arg-0%call-print-2%fun-A: Int = 3
-              var State%Int%arg-0%call-print-2%fun-A: State<Int>?
-              @LiveLiteralInfo(key = "Int%arg-0%call-print-2%fun-A", offset = 165)
-              fun Int%arg-0%call-print-2%fun-A(): Int {
-                if (!enabled) {
-                  return Int%arg-0%call-print-2%fun-A
-                }
-                val tmp0 = State%Int%arg-0%call-print-2%fun-A
-                return if (tmp0 == null) {
-                  val tmp1 = liveLiteral("Int%arg-0%call-print-2%fun-A", Int%arg-0%call-print-2%fun-A)
-                  State%Int%arg-0%call-print-2%fun-A = tmp1
-                  tmp1
-                } else {
-                  tmp0
-                }
-                .value
-              }
-            }
-        """
-    )
+            """
+        )
+    }
 
     @Test
     fun testComposeIrSkippingWithDefaultsRelease() = verifyComposeIrTransform(
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/RememberIntrinsicTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/RememberIntrinsicTransformTests.kt
index 13f7adc..790002d 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/RememberIntrinsicTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/RememberIntrinsicTransformTests.kt
@@ -19,11 +19,8 @@
 import org.intellij.lang.annotations.Language
 import org.jetbrains.kotlin.config.CompilerConfiguration
 import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
-@RunWith(JUnit4::class)
-class RememberIntrinsicTransformTests : AbstractIrTransformTest(useFir = false) {
+class RememberIntrinsicTransformTests(useFir: Boolean) : AbstractIrTransformTest(useFir) {
     override fun CompilerConfiguration.updateConfiguration() {
         put(ComposeConfiguration.SOURCE_INFORMATION_ENABLED_KEY, true)
         put(ComposeConfiguration.INTRINSIC_REMEMBER_OPTIMIZATION_ENABLED_KEY, true)
@@ -717,9 +714,9 @@
               if (isTraceInProgress()) {
                 traceEventStart(<>, %changed, -1, <>)
               }
-              val tmp0_iterator = items.iterator()
-              while (tmp0_iterator.hasNext()) {
-                val item = tmp0_iterator.next()
+              val <iterator> = items.iterator()
+              while (<iterator>.hasNext()) {
+                val item = <iterator>.next()
                 val foo = remember({
                   Foo()
                 }, %composer, 0)
@@ -761,9 +758,9 @@
               if (isTraceInProgress()) {
                 traceEventStart(<>, %changed, -1, <>)
               }
-              val tmp0_iterator = items.iterator()
-              while (tmp0_iterator.hasNext()) {
-                val item = tmp0_iterator.next()
+              val <iterator> = items.iterator()
+              while (<iterator>.hasNext()) {
+                val item = <iterator>.next()
                 val foo = remember({
                   Foo()
                 }, %composer, 0)
@@ -1724,9 +1721,9 @@
               sourceInformation(%composer, "C(Test)<rememb...>,<Text("...>:Test.kt")
               val %dirty = %changed
               %composer.startMovableGroup(<>, strings.size)
-              val tmp0_iterator = strings.iterator()
-              while (tmp0_iterator.hasNext()) {
-                val value = tmp0_iterator.next()
+              val <iterator> = strings.iterator()
+              while (<iterator>.hasNext()) {
+                val value = <iterator>.next()
                 %dirty = %dirty or if (%composer.changed(value)) 0b0100 else 0
               }
               %composer.endMovableGroup()
@@ -1779,16 +1776,16 @@
         expectedTransformed = """
             val content: Function3<@[ParameterName(name = 'a')] SomeUnstableClass, Composer, Int, Unit> = ComposableSingletons%TestKt.lambda-1
             internal object ComposableSingletons%TestKt {
-              val lambda-1: Function3<SomeUnstableClass, Composer, Int, Unit> = composableLambdaInstance(<>, false) { it: SomeUnstableClass, %composer: Composer?, %changed: Int ->
+              val lambda-1: Function3<${if (useFir) "@[ParameterName(name = 'a')] " else ""}SomeUnstableClass, Composer, Int, Unit> = composableLambdaInstance(<>, false) { it: ${if (useFir) "@[ParameterName(name = 'a')] " else ""}SomeUnstableClass, %composer: Composer?, %changed: Int ->
                 sourceInformation(%composer, "C<rememb...>:Test.kt")
                 if (isTraceInProgress()) {
                   traceEventStart(<>, %changed, -1, <>)
                 }
                 %composer.startReplaceableGroup(<>)
                 sourceInformation(%composer, "*<rememb...>")
-                val tmp0_iterator = 0 until count.iterator()
-                while (tmp0_iterator.hasNext()) {
-                  val index = tmp0_iterator.next()
+                val <iterator> = 0 until count.iterator()
+                while (<iterator>.hasNext()) {
+                  val index = <iterator>.next()
                   val i = remember({
                     index
                   }, %composer, 0)
@@ -1826,14 +1823,14 @@
         expectedTransformed = """
             val content: Function3<@[ParameterName(name = 'a')] SomeUnstableClass, Composer, Int, Unit> = ComposableSingletons%TestKt.lambda-1
             internal object ComposableSingletons%TestKt {
-              val lambda-1: Function3<SomeUnstableClass, Composer, Int, Unit> = composableLambdaInstance(<>, false) { it: SomeUnstableClass, %composer: Composer?, %changed: Int ->
+              val lambda-1: Function3<${if (useFir) "@[ParameterName(name = 'a')] " else ""}SomeUnstableClass, Composer, Int, Unit> = composableLambdaInstance(<>, false) { it: ${if (useFir) "@[ParameterName(name = 'a')] " else ""}SomeUnstableClass, %composer: Composer?, %changed: Int ->
                 sourceInformation(%composer, "C*<rememb...>:Test.kt")
                 if (isTraceInProgress()) {
                   traceEventStart(<>, %changed, -1, <>)
                 }
-                val tmp0_iterator = 0 until count.iterator()
-                while (tmp0_iterator.hasNext()) {
-                  val index = tmp0_iterator.next()
+                val <iterator> = 0 until count.iterator()
+                while (<iterator>.hasNext()) {
+                  val index = <iterator>.next()
                   val i = remember({
                     index
                   }, %composer, 0)
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/RunComposableTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/RunComposableTests.kt
index 4fe6e50..352bf6b 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/RunComposableTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/RunComposableTests.kt
@@ -33,12 +33,10 @@
 import org.jetbrains.kotlin.cli.common.setupLanguageVersionSettings
 import org.jetbrains.kotlin.config.CompilerConfiguration
 import org.junit.Assert.assertEquals
+import org.junit.Assume.assumeFalse
 import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
-@RunWith(JUnit4::class)
-class RunComposableTests : AbstractCodegenTest(useFir = false) {
+class RunComposableTests(useFir: Boolean) : AbstractCodegenTest(useFir) {
     override fun CompilerConfiguration.updateConfiguration() {
         setupLanguageVersionSettings(K2JVMCompilerArguments().apply {
             // enabling multiPlatform to use expect/actual declarations
@@ -47,7 +45,63 @@
     }
 
     @Test // Bug report: https://github.com/JetBrains/compose-jb/issues/1407
+    fun testSimpleDefaultInComposable() {
+        // TODO: Enable once https://youtrack.jetbrains.com/issue/KT-56173 is fixed.
+        assumeFalse(useFir)
+        runCompose(
+            testFunBody = """
+                results["defaultValue"] = ExpectComposable()
+                results["anotherValue"] = ExpectComposable("anotherValue")
+                results["defaultValueDefaultTransform"] = ExpectComposableWithNonComposableLambda()
+                results["anotherValueOtherTransform"] =
+                    ExpectComposableWithNonComposableLambda("anotherValue") {
+                        it + "OtherTransform"
+                    }
+            """.trimIndent(),
+            commonFiles = mapOf(
+                "Expect.kt" to """
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    expect fun ExpectComposable(
+                        value: String = "defaultValue"
+                    ): String
+
+                    @Composable
+                    expect fun ExpectComposableWithNonComposableLambda(
+                        value: String = "defaultValue",
+                        transform: (String) -> String = { it + "DefaultTransform" }
+                    ): String
+                """.trimIndent()
+            ),
+            platformFiles = mapOf("Actual.kt" to """
+                    import androidx.compose.runtime.*
+
+                    @Composable
+                    actual fun ExpectComposable(
+                        value: String
+                    ) = value
+
+                    @Composable
+                    actual fun ExpectComposableWithNonComposableLambda(
+                        value: String,
+                        transform: (String) -> String
+                    ) = transform(value)
+                """.trimIndent()
+            )
+        ) { results ->
+            assertEquals("defaultValue", results["defaultValue"])
+            assertEquals("anotherValue", results["anotherValue"])
+            assertEquals("defaultValueDefaultTransform", results["defaultValueDefaultTransform"])
+            assertEquals("anotherValueOtherTransform", results["anotherValueOtherTransform"])
+        }
+    }
+
+    @Test // Bug report: https://github.com/JetBrains/compose-jb/issues/1407
     fun testDefaultValuesFromExpectComposableFunctions() {
+        // TODO: Enable once https://youtrack.jetbrains.com/issue/KT-56173 and
+        //       https://youtrack.jetbrains.com/issue/KT-58539 are fixed.
+        assumeFalse(useFir)
         runCompose(
             testFunBody = """
                 ExpectComposable { value ->
@@ -59,7 +113,7 @@
                 results["returnDefaultValue"] = ExpectComposableWithReturn()
                 results["returnAnotherValue"] = ExpectComposableWithReturn("returnAnotherValue")
             """.trimIndent(),
-            files = mapOf(
+            commonFiles = mapOf(
                 "Expect.kt" to """
                     import androidx.compose.runtime.*
 
@@ -72,8 +126,9 @@
                     expect fun ExpectComposableWithReturn(
                         value: String = "returnDefaultValue"
                     ): String
-                """.trimIndent(),
-                "Actual.kt" to """
+                """.trimIndent()
+            ),
+            platformFiles = mapOf("Actual.kt" to """
                     import androidx.compose.runtime.*
 
                     @Composable
@@ -99,6 +154,9 @@
 
     @Test
     fun testExpectWithGetExpectedPropertyInDefaultValueExpression() {
+        // TODO: Enable once https://youtrack.jetbrains.com/issue/KT-56173 and
+        //       https://youtrack.jetbrains.com/issue/KT-58539 are fixed.
+        assumeFalse(useFir)
         runCompose(
             testFunBody = """
                 ExpectComposable { value ->
@@ -108,7 +166,7 @@
                     results["anotherValue"] = value
                 }
             """.trimIndent(),
-            files = mapOf(
+            commonFiles = mapOf(
                 "Expect.kt" to """
                     import androidx.compose.runtime.*
 
@@ -119,7 +177,8 @@
                         value: () -> String = { expectedProperty },
                         content: @Composable (v: String) -> Unit
                     )
-                """.trimIndent(),
+                """.trimIndent()),
+            platformFiles = mapOf(
                 "Actual.kt" to """
                     import androidx.compose.runtime.*
 
@@ -145,6 +204,9 @@
 
     @Test
     fun testExpectWithComposableExpressionInDefaultValue() {
+        // TODO: Enable once https://youtrack.jetbrains.com/issue/KT-56173 and
+        //       https://youtrack.jetbrains.com/issue/KT-58539 are fixed.
+        assumeFalse(useFir)
         runCompose(
             testFunBody = """
                 ExpectComposable { value ->
@@ -154,7 +216,7 @@
                     results["anotherValue"] = value
                 }
             """.trimIndent(),
-            files = mapOf(
+            commonFiles = mapOf(
                 "Expect.kt" to """
                     import androidx.compose.runtime.*
 
@@ -168,7 +230,8 @@
                         value: String = defaultValueComposable(),
                         content: @Composable (v: String) -> Unit
                     )
-                """.trimIndent(),
+                """.trimIndent()),
+            platformFiles = mapOf(
                 "Actual.kt" to """
                     import androidx.compose.runtime.*
 
@@ -189,6 +252,9 @@
 
     @Test
     fun testExpectWithTypedParameter() {
+        // TODO: Enable once https://youtrack.jetbrains.com/issue/KT-56173 and
+        //       https://youtrack.jetbrains.com/issue/KT-58539 are fixed.
+        assumeFalse(useFir)
         runCompose(
             testFunBody = """
                 ExpectComposable<String>("aeiouy") { value ->
@@ -198,7 +264,7 @@
                     results["anotherValue"] = value
                 }
             """.trimIndent(),
-            files = mapOf(
+            commonFiles = mapOf(
                 "Expect.kt" to """
                     import androidx.compose.runtime.*
 
@@ -208,7 +274,8 @@
                         composeValue: @Composable () -> T = { value },
                         content: @Composable (T) -> Unit
                     )
-                """.trimIndent(),
+                """.trimIndent()),
+            platformFiles = mapOf(
                 "Actual.kt" to """
                     import androidx.compose.runtime.*
 
@@ -230,6 +297,9 @@
 
     @Test
     fun testExpectWithRememberInDefaultValueExpression() {
+        // TODO: Enable once https://youtrack.jetbrains.com/issue/KT-56173 and
+        //       https://youtrack.jetbrains.com/issue/KT-58539 are fixed.
+        assumeFalse(useFir)
         runCompose(
             testFunBody = """
                 ExpectComposable { value ->
@@ -239,7 +309,7 @@
                     results["anotherValue"] = value
                 }
             """.trimIndent(),
-            files = mapOf(
+            commonFiles = mapOf(
                 "Expect.kt" to """
                     import androidx.compose.runtime.*
 
@@ -248,7 +318,8 @@
                         value: String = remember { "rememberedDefaultValue" },
                         content: @Composable (v: String) -> Unit
                     )
-                """.trimIndent(),
+                """.trimIndent()),
+            platformFiles = mapOf(
                 "Actual.kt" to """
                     import androidx.compose.runtime.*
 
@@ -269,6 +340,9 @@
 
     @Test
     fun testExpectWithDefaultValueUsingAnotherArgument() {
+        // TODO: Enable once https://youtrack.jetbrains.com/issue/KT-56173 and
+        //       https://youtrack.jetbrains.com/issue/KT-58539 are fixed.
+        assumeFalse(useFir)
         runCompose(
             testFunBody = """
                 ExpectComposable("AbccbA") { value ->
@@ -278,7 +352,7 @@
                     results["anotherValue"] = value
                 }
             """.trimIndent(),
-            files = mapOf(
+            commonFiles = mapOf(
                 "Expect.kt" to """
                     import androidx.compose.runtime.*
 
@@ -288,7 +362,8 @@
                         composeText: (String) -> String = { value },
                         content: @Composable (v: String) -> Unit
                     )
-                """.trimIndent(),
+                """.trimIndent()),
+            platformFiles = mapOf(
                 "Actual.kt" to """
                     import androidx.compose.runtime.*
 
@@ -310,6 +385,9 @@
 
     @Test
     fun testNonComposableFunWithComposableParam() {
+        // TODO: Enable once https://youtrack.jetbrains.com/issue/KT-56173 and
+        //       https://youtrack.jetbrains.com/issue/KT-58539 are fixed.
+        assumeFalse(useFir)
         runCompose(
             testFunBody = """
                 savedContentLambda = null
@@ -324,7 +402,7 @@
                 }
                 savedContentLambda!!.invoke()
             """.trimIndent(),
-            files = mapOf(
+            commonFiles = mapOf(
                 "Expect.kt" to """
                     import androidx.compose.runtime.*
 
@@ -334,11 +412,11 @@
                         value: String = "000",
                         content: @Composable (v: String) -> Unit
                     )
-                """.trimIndent(),
+                """.trimIndent()),
+            platformFiles = mapOf(
                 "Actual.kt" to """
                     import androidx.compose.runtime.*
 
-                    @Composable
                     actual fun ExpectFunWithComposableParam(
                         value: String,
                         content: @Composable (v: String) -> Unit
@@ -362,12 +440,13 @@
         mainImports: String = "",
         @Language("kotlin")
         testFunBody: String,
-        files: Map<String, String>, // name to source code
+        commonFiles: Map<String, String>, // name to source code
+        platformFiles: Map<String, String>, // name to source code
         accessResults: (results: HashMap<*, *>) -> Unit
     ) {
         val className = "TestFCS_${uniqueNumber++}"
 
-        val allSources = files + ("Main.kt" to """
+        val allCommonSources = commonFiles + ("Main.kt" to """
             import androidx.compose.runtime.*
             $mainImports
 
@@ -382,7 +461,7 @@
 
         """.trimIndent())
 
-        val compiledClasses = classLoader(allSources)
+        val compiledClasses = classLoader(platformFiles, allCommonSources)
         val allClassFiles = compiledClasses.allGeneratedFiles.filter {
             it.relativePath.endsWith(".class")
         }
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/SanityCheckCodegenTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/SanityCheckCodegenTests.kt
index dfbad0a..142bed6 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/SanityCheckCodegenTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/SanityCheckCodegenTests.kt
@@ -16,7 +16,6 @@
 
 package androidx.compose.compiler.plugins.kotlin
 
-import org.junit.Assume
 import org.junit.Test
 
 class SanityCheckCodegenTests(useFir: Boolean) : AbstractCodegenTest(useFir) {
@@ -126,7 +125,6 @@
     // Regression validating b/237863365
     @Test
     fun testComposableAsLastStatementInUnitReturningLambda() {
-        Assume.assumeFalse(useFir)
         testCompile(
             """
             import androidx.compose.runtime.Composable
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/StaticExpressionDetectionTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/StaticExpressionDetectionTests.kt
index 34460d1..0740f85 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/StaticExpressionDetectionTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/StaticExpressionDetectionTests.kt
@@ -286,7 +286,7 @@
         val compositionContextBody = irModule.files.last().declarations
             .filterIsInstance<IrFunction>()
             .first { it.name.identifier == "CompositionContext" }
-            .dumpSrc()
+            .dumpSrc(useFir)
             .replace('$', '%')
 
         assertChangedBits(
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/TargetAnnotationsTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/TargetAnnotationsTransformTests.kt
index eb66445..3817942 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/TargetAnnotationsTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/TargetAnnotationsTransformTests.kt
@@ -17,12 +17,9 @@
 package androidx.compose.compiler.plugins.kotlin
 
 import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
-@RunWith(JUnit4::class)
 @Suppress("SpellCheckingInspection") // Expected strings can have partial words
-class TargetAnnotationsTransformTests : AbstractIrTransformTest(useFir = false) {
+class TargetAnnotationsTransformTests(useFir: Boolean) : AbstractIrTransformTest(useFir) {
     @Test
     fun testInferUIFromCall() = verify(
         """
@@ -893,16 +890,13 @@
           }
           static val %stable: Int = 0
         }
-        val localBoxMeasurePolicy: MeasurePolicy = class <no name provided> : MeasurePolicy {
-          override fun measure(%this%MeasurePolicy: MeasureScope, <anonymous parameter 0>: List<Measurable>, constraints: Constraints): MeasureResult {
-            return %this%MeasurePolicy.layout(
-              width = constraints.minWidth,
-              height = constraints.minHeight
-            ) {
-            }
+        val localBoxMeasurePolicy: MeasurePolicy = MeasurePolicy { <unused var>: List<Measurable>, constraints: Constraints ->
+          %this%MeasurePolicy.layout(
+            width = constraints.minWidth,
+            height = constraints.minHeight
+          ) {
           }
         }
-        <no name provided>()
         @Composable
         @ComposableInferredTarget(scheme = "[androidx.compose.ui.UiComposable[androidx.compose.ui.UiComposable]]")
         fun LocalBox(modifier: Modifier?, content: @[ExtensionFunctionType] Function3<LocalBoxScope, Composer, Int, Unit>, %composer: Composer?, %changed: Int, %default: Int) {
@@ -986,12 +980,9 @@
               sourceInformationMarkerStart(%composer, <>, "C:Test.kt")
               Unit
               sourceInformationMarkerEnd(%composer)
-            }, null, class <no name provided> : MeasurePolicy {
-              override fun measure(%this%Layout: MeasureScope, <anonymous parameter 0>: List<Measurable>, <anonymous parameter 1>: Constraints): MeasureResult {
-                return error("")
-              }
-            }
-            <no name provided>(), %composer, 0, 0b0010)
+            }, null, MeasurePolicy { <unused var>: List<Measurable>, <unused var>: Constraints ->
+              error("")
+            }, %composer, 0b000110000000, 0b0010)
             if (isTraceInProgress()) {
               traceEventEnd()
             }
@@ -1015,12 +1006,9 @@
             if (isTraceInProgress()) {
               traceEventStart(<>, %dirty, -1, <>)
             }
-            Layout(content, null, class <no name provided> : MeasurePolicy {
-              override fun measure(%this%Layout: MeasureScope, <anonymous parameter 0>: List<Measurable>, <anonymous parameter 1>: Constraints): MeasureResult {
-                return error("")
-              }
-            }
-            <no name provided>(), %composer, 0b1110 and %dirty, 0b0010)
+            Layout(content, null, MeasurePolicy { <unused var>: List<Measurable>, <unused var>: Constraints ->
+              error("")
+            }, %composer, 0b000110000000 or 0b1110 and %dirty, 0b0010)
             if (isTraceInProgress()) {
               traceEventEnd()
             }
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/TraceInformationTest.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/TraceInformationTest.kt
index 9aae8db..2321f8c 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/TraceInformationTest.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/TraceInformationTest.kt
@@ -18,8 +18,6 @@
 
 import androidx.compose.compiler.plugins.kotlin.AbstractIrTransformTest.TruncateTracingInfoMode
 import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 /**
  * Verifies trace data passed to tracing. Relies on [TruncateTracingInfoMode.KEEP_INFO_STRING] to
@@ -28,8 +26,7 @@
  * More complex cases tested in other IrTransform tests that use
  * the [TruncateTracingInfoMode.KEEP_INFO_STRING].
  */
-@RunWith(JUnit4::class)
-class TraceInformationTest : AbstractIrTransformTest(useFir = false) {
+class TraceInformationTest(useFir: Boolean) : AbstractIrTransformTest(useFir) {
     @Test
     fun testBasicComposableFunctions() = verifyComposeIrTransform(
         """
@@ -90,6 +87,52 @@
     )
 
     @Test
+    fun testReadOnlyComposable() = verifyComposeIrTransform(
+        """
+            import androidx.compose.runtime.*
+
+            @Composable
+            @ReadOnlyComposable
+            internal fun someFun(a: Boolean): Boolean {
+                if (a) {
+                    return a
+                } else {
+                    return a
+                }
+            }
+        """,
+        """
+            @Composable
+            @ReadOnlyComposable
+            internal fun someFun(a: Boolean, %composer: Composer?, %changed: Int): Boolean {
+              sourceInformationMarkerStart(%composer, <>, "C(someFun):Test.kt")
+              if (isTraceInProgress()) {
+                traceEventStart(<>, %changed, -1, <>)
+              }
+              if (a) {
+                val tmp0_return = a
+                if (isTraceInProgress()) {
+                  traceEventEnd()
+                }
+                sourceInformationMarkerEnd(%composer)
+                return tmp0_return
+              } else {
+                val tmp1_return = a
+                if (isTraceInProgress()) {
+                  traceEventEnd()
+                }
+                sourceInformationMarkerEnd(%composer)
+                return tmp1_return
+              }
+              if (isTraceInProgress()) {
+                traceEventEnd()
+              }
+              sourceInformationMarkerEnd(%composer)
+            }
+        """
+    )
+
+    @Test
     fun testInlineFunctionsDonotGenerateTraceMarkers() = verifyComposeIrTransform(
         """
             import androidx.compose.runtime.*
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableCheckerTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableCheckerTests.kt
index 7cc78ae..d3ff41a 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableCheckerTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableCheckerTests.kt
@@ -1,9 +1,10 @@
 package androidx.compose.compiler.plugins.kotlin.analysis
 
 import androidx.compose.compiler.plugins.kotlin.AbstractComposeDiagnosticsTest
+import org.junit.Assume.assumeTrue
 import org.junit.Test
 
-class ComposableCheckerTests : AbstractComposeDiagnosticsTest() {
+class ComposableCheckerTests(useFir: Boolean) : AbstractComposeDiagnosticsTest(useFir) {
     @Test
     fun testCfromNC() = check(
         """
@@ -187,6 +188,77 @@
     )
 
     @Test
+    fun testGenericComposableInference1() {
+        assumeTrue(useFir)
+        check("""
+        import androidx.compose.runtime.Composable
+
+        fun <T> identity(value: T): T = value
+
+        // We should infer `ComposableFunction0<Unit>` for `T`
+        val cl = identity(@Composable {})
+        val l: () -> Unit = <!INITIALIZER_TYPE_MISMATCH!>cl<!>
+        """)
+    }
+
+    @Test
+    fun testGenericComposableInference2() {
+        assumeTrue(useFir)
+        check("""
+        import androidx.compose.runtime.Composable
+
+        @Composable fun A() {}
+        fun <T> identity(value: T): T = value
+
+        // Explicitly instantiate `T` with `ComposableFunction0<Unit>`
+        val cl = identity<@Composable () -> Unit> { A() }
+        val l: () -> Unit = <!INITIALIZER_TYPE_MISMATCH!>cl<!>
+        """)
+    }
+
+    @Test
+    fun testGenericComposableInference3() {
+        assumeTrue(useFir)
+        check("""
+        import androidx.compose.runtime.Composable
+
+        @Composable fun A() {}
+        fun <T> identity(value: T): T = value
+
+        // We should infer `T` as `ComposableFunction0<Unit>` from the context and then
+        // infer that the argument to `identity` is a composable lambda.
+        val cl: @Composable () -> Unit = identity { A() }
+        """)
+    }
+
+    @Test
+    fun testGenericComposableInference4() {
+        assumeTrue(useFir)
+        check("""
+        import androidx.compose.runtime.Composable
+
+        fun <T> identity(value: T): T = value
+
+        // We should infer `T` as `Function0<Unit>` from the context and
+        // reject the lambda which is explicitly typed as `ComposableFunction...`.
+        val cl: () -> Unit = identity(@Composable <!ARGUMENT_TYPE_MISMATCH!>{}<!>)
+        """)
+    }
+
+    @Test
+    fun testGenericComposableInference5() {
+        assumeTrue(useFir)
+        check("""
+        import androidx.compose.runtime.Composable
+
+        fun <T> identity(value: T): T = value
+
+        // We should infer `Function0<Unit>` for `T`
+        val lambda = identity<() -> Unit>(@Composable <!ARGUMENT_TYPE_MISMATCH!>{}<!>)
+        """)
+    }
+
+    @Test
     fun testCfromAnnotatedComposableFunInterface() = check(
         """
         import androidx.compose.runtime.Composable
@@ -379,13 +451,13 @@
 
     @Test
     fun testComposableReporting008() {
-        checkFail(
+        check(
             """
             import androidx.compose.runtime.*;
 
             @Composable fun Leaf() {}
 
-            fun foo() {
+            fun <!COMPOSABLE_EXPECTED!>foo<!>() {
                 val bar: @Composable ()->Unit = @Composable {
                     Leaf()
                 }
@@ -469,6 +541,7 @@
             }
         """
         )
+        val error = if (useFir) "INITIALIZER_TYPE_MISMATCH" else "TYPE_MISMATCH"
         check(
             """
             import androidx.compose.runtime.*;
@@ -477,7 +550,7 @@
             fun Leaf() {}
 
             fun foo() {
-                val myVariable: ()->Unit = <!TYPE_MISMATCH!>@Composable { Leaf() }<!>
+                val myVariable: ()->Unit = <!$error!>@Composable { Leaf() }<!>
                 System.out.println(myVariable)
             }
         """
@@ -486,6 +559,9 @@
 
     @Test
     fun testComposableReporting021() {
+        // In K1, we erroneously allowed `@Composable` annotations on non-composable inline
+        // lambdas. See b/281975618.
+        assumeTrue(useFir)
         check(
             """
             import androidx.compose.runtime.*;
@@ -496,10 +572,10 @@
             @Composable
             fun foo() {
                 val myList = listOf(1,2,3,4,5)
-                myList.forEach <!REDUNDANT_COMPOSABLE_ANNOTATION!>@Composable<!> { value: Int ->
+                myList.forEach @Composable <!ARGUMENT_TYPE_MISMATCH!>{ value: Int ->
                     Leaf()
                     System.out.println(value)
-                }
+                }<!>
             }
         """
         )
@@ -660,12 +736,13 @@
             }
         """
         )
+        val error = if (useFir) "INITIALIZER_TYPE_MISMATCH" else "TYPE_MISMATCH"
         check(
             """
             import androidx.compose.runtime.*;
 
             fun foo(v: @Composable ()->Unit) {
-                val myVariable: ()->Unit = <!TYPE_MISMATCH!>v<!>
+                val myVariable: ()->Unit = <!$error!>v<!>
                 myVariable()
             }
         """
@@ -742,6 +819,8 @@
             }
         """
         )
+        val argumentTypeMismatch = if (useFir) "ARGUMENT_TYPE_MISMATCH" else "TYPE_MISMATCH"
+        val initializerTypeMismatch = if (useFir) "INITIALIZER_TYPE_MISMATCH" else "TYPE_MISMATCH"
         check(
             """
             import androidx.compose.runtime.*;
@@ -750,7 +829,7 @@
 
             @Composable
             fun test(f: @Composable ()->Unit) {
-                val f2: @Composable ()->Unit = <!TYPE_MISMATCH!>identity (<!TYPE_MISMATCH!>f<!>)<!>;
+                val f2: @Composable ()->Unit = <!$initializerTypeMismatch!>identity (<!$argumentTypeMismatch!>f<!>)<!>;
                 f2()
             }
         """
@@ -1207,6 +1286,28 @@
     )
 
     @Test
+    fun testDisallowComposableCallPropagationWithInvoke() {
+        // The frontend distinguishes between implicit and explicit invokes, which is why this test
+        // fails in K1.
+        assumeTrue(useFir)
+        check(
+            """
+            import androidx.compose.runtime.*
+            class Foo
+            @Composable inline fun a(block1: @DisallowComposableCalls () -> Foo): Foo {
+                return block1()
+            }
+            @Composable inline fun b(<!MISSING_DISALLOW_COMPOSABLE_CALLS_ANNOTATION!>block2: () -> Foo<!>): Foo {
+              return a { block2.invoke() }
+            }
+            @Composable inline fun c(block2: @DisallowComposableCalls () -> Foo): Foo {
+              return a { block2.invoke() }
+            }
+        """
+        )
+    }
+
+    @Test
     fun testComposableLambdaToAll() = check(
         """
         import androidx.compose.runtime.*
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableDeclarationCheckerTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableDeclarationCheckerTests.kt
index 2c611cf..e6e1ac4 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableDeclarationCheckerTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableDeclarationCheckerTests.kt
@@ -19,7 +19,7 @@
 import androidx.compose.compiler.plugins.kotlin.AbstractComposeDiagnosticsTest
 import org.junit.Test
 
-class ComposableDeclarationCheckerTests : AbstractComposeDiagnosticsTest() {
+class ComposableDeclarationCheckerTests(useFir: Boolean) : AbstractComposeDiagnosticsTest(useFir) {
     @Test
     fun testPropertyWithInitializer() {
         check(
@@ -34,7 +34,7 @@
 
     @Test
     fun testComposableFunctionReferences() {
-        check(
+        check(if (!useFir) {
             """
             import androidx.compose.runtime.Composable
 
@@ -49,24 +49,49 @@
                 B(<!COMPOSABLE_FUNCTION_REFERENCE,TYPE_MISMATCH!>::A<!>)
             }
         """
-        )
+        } else {
+            // In K2, we are taking composability into account when resolving function references,
+            // so trying to resolve `::A` in a context where we expect a non-composable function
+            // type fails with an `UNRESOLVED_REFERENCE` error, instead of a
+            // `COMPOSABLE_FUNCTION_REFERENCE` error in the plugin..
+            """
+            import androidx.compose.runtime.Composable
+
+            @Composable fun A() {}
+            val aCallable: () -> Unit = ::<!UNRESOLVED_REFERENCE!>A<!>
+            val bCallable: @Composable () -> Unit = <!COMPOSABLE_FUNCTION_REFERENCE!>::A<!>
+            val cCallable = <!COMPOSABLE_FUNCTION_REFERENCE!>::A<!>
+            fun doSomething(fn: () -> Unit) { print(fn) }
+            @Composable fun B(content: @Composable () -> Unit) {
+                content()
+                <!INAPPLICABLE_CANDIDATE!>doSomething<!>(::<!UNRESOLVED_REFERENCE!>A<!>)
+                B(<!COMPOSABLE_FUNCTION_REFERENCE!>::A<!>)
+            }
+        """
+        })
     }
 
     @Test
     fun testNonComposableFunctionReferences() {
+        // This code fails for two different reasons in K1 and K2. In K1, the code fails with
+        // a TYPE_MISMATCH, since we infer a non-composable function type in a context where a
+        // composable function type is expected. In K2, we can promote non-composable function
+        // types to composable function types (as this matches the behavior for suspend functions),
+        // but we explicitly forbid composable function references.
+        val error = if (useFir) "COMPOSABLE_FUNCTION_REFERENCE" else "TYPE_MISMATCH"
         check(
             """
             import androidx.compose.runtime.Composable
 
             fun A() {}
             val aCallable: () -> Unit = ::A
-            val bCallable: @Composable () -> Unit = <!TYPE_MISMATCH!>::A<!>
+            val bCallable: @Composable () -> Unit = <!$error!>::A<!>
             val cCallable = ::A
             fun doSomething(fn: () -> Unit) { print(fn) }
             @Composable fun B(content: @Composable () -> Unit) {
                 content()
                 doSomething(::A)
-                B(<!TYPE_MISMATCH!>::A<!>)
+                B(<!$error!>::A<!>)
             }
         """
         )
@@ -112,7 +137,7 @@
 
     @Test
     fun testSuspendComposable() {
-        check(
+        check(if (!useFir) {
             """
             import androidx.compose.runtime.Composable
 
@@ -131,7 +156,29 @@
                 acceptSuspend(<!COMPOSABLE_SUSPEND_FUN,TYPE_MISMATCH!>@Composable suspend fun() { }<!>)
             }
         """
-        )
+        } else {
+            // In K2, the frontend forbids function types with multiple kinds, so
+            // `@Composable suspend` function types get turned into error types. This is the
+            // reason for the additional ARGUMENT_TYPE_MISMATCH errors.
+            """
+            import androidx.compose.runtime.Composable
+
+            @Composable suspend fun <!COMPOSABLE_SUSPEND_FUN!>Foo<!>() {}
+
+            fun acceptSuspend(fn: suspend () -> Unit) { print(fn) }
+            fun acceptComposableSuspend(fn: <!AMBIGUOUS_FUNCTION_TYPE_KIND!>@Composable suspend () -> Unit<!>) { print(fn.hashCode()) }
+
+            val foo: suspend () -> Unit = <!INITIALIZER_TYPE_MISMATCH!>@Composable {}<!>
+            val bar: suspend () -> Unit = {}
+            fun Test() {
+                val composableLambda = @Composable {}
+                acceptSuspend @Composable <!ARGUMENT_TYPE_MISMATCH!>{}<!>
+                acceptComposableSuspend @Composable <!ARGUMENT_TYPE_MISMATCH!>{}<!>
+                acceptComposableSuspend(<!ARGUMENT_TYPE_MISMATCH!>composableLambda<!>)
+                acceptSuspend(<!COMPOSABLE_SUSPEND_FUN!>@Composable suspend fun()<!> { })
+            }
+        """
+        })
     }
 
     @Test
@@ -167,6 +214,8 @@
 
     @Test
     fun testMissingComposableOnOverride() {
+        // In K1, we report the `CONFLICTING_OVERLOADS` error on properties as well as property
+        // accessors. In K2 we only report the error on property accessors.
         check(
             """
             import androidx.compose.runtime.Composable
@@ -181,7 +230,7 @@
             object FakeFoo : Foo {
                 <!CONFLICTING_OVERLOADS!>override fun composableFunction(param: Boolean)<!> = true
                 <!CONFLICTING_OVERLOADS!>@Composable override fun nonComposableFunction(param: Boolean)<!> = true
-                <!CONFLICTING_OVERLOADS!>override val nonComposableProperty: Boolean<!> <!CONFLICTING_OVERLOADS!>@Composable get()<!> = true
+                ${if (!useFir) "<!CONFLICTING_OVERLOADS!>" else ""}override val nonComposableProperty: Boolean${if (!useFir) "<!>" else ""} <!CONFLICTING_OVERLOADS!>@Composable get()<!> = true
             }
 
             interface Bar {
@@ -194,9 +243,9 @@
 
             object FakeBar : Bar {
                 <!CONFLICTING_OVERLOADS!>override fun composableFunction(param: Boolean)<!> = true
-                <!CONFLICTING_OVERLOADS!>override val composableProperty: Boolean<!> <!CONFLICTING_OVERLOADS!>get()<!> = true
+                <!CONFLICTING_OVERLOADS!>override val composableProperty: Boolean<!> = true
                 <!CONFLICTING_OVERLOADS!>@Composable override fun nonComposableFunction(param: Boolean)<!> = true
-                <!CONFLICTING_OVERLOADS!>override val nonComposableProperty: Boolean<!> <!CONFLICTING_OVERLOADS!>@Composable get()<!> = true
+                ${if (!useFir) "<!CONFLICTING_OVERLOADS!>" else ""}override val nonComposableProperty: Boolean${if (!useFir) "<!>" else ""} <!CONFLICTING_OVERLOADS!>@Composable get()<!> = true
             }
         """
         )
@@ -273,7 +322,7 @@
 
     @Test
     fun testOverrideWithoutComposeAnnotation() {
-        check(
+        check(if (!useFir) {
             """
                 import androidx.compose.runtime.Composable
                 interface Base {
@@ -284,7 +333,20 @@
                     <!CONFLICTING_OVERLOADS!>override fun compose(content: @Composable () -> Unit)<!> {}
                 }
             """
-        )
+        } else {
+            // In K2, the `@Composable` type is part of the function signature, so the `override`
+            // does not match the `compose` function in `Base`.
+            """
+                import androidx.compose.runtime.Composable
+                interface Base {
+                    fun compose(content: () -> Unit)
+                }
+
+                <!ABSTRACT_MEMBER_NOT_IMPLEMENTED!>class Impl<!> : Base {
+                    <!NOTHING_TO_OVERRIDE!>override<!> fun compose(content: @Composable () -> Unit) {}
+                }
+            """
+        })
     }
 
     @Test
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableTargetCheckerTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableTargetCheckerTests.kt
index 9563e07..0c62d13 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableTargetCheckerTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableTargetCheckerTests.kt
@@ -18,8 +18,11 @@
 
 import androidx.compose.compiler.plugins.kotlin.AbstractComposeDiagnosticsTest
 import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
 
-class ComposableTargetCheckerTests : AbstractComposeDiagnosticsTest() {
+@RunWith(JUnit4::class)
+class ComposableTargetCheckerTests : AbstractComposeDiagnosticsTest(useFir = false) {
     @Test
     fun testExplicitTargetAnnotations() = check(
         """
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposeMultiplatformCheckerTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposeMultiplatformCheckerTests.kt
new file mode 100644
index 0000000..b8fae4d
--- /dev/null
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposeMultiplatformCheckerTests.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.compiler.plugins.kotlin.analysis
+
+import androidx.compose.compiler.plugins.kotlin.AbstractComposeDiagnosticsTest
+import org.jetbrains.kotlin.config.CompilerConfiguration
+import org.jetbrains.kotlin.config.LanguageFeature
+import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
+import org.jetbrains.kotlin.config.languageVersionSettings
+import org.junit.Test
+
+class ComposeMultiplatformCheckerTests(useFir: Boolean) : AbstractComposeDiagnosticsTest(useFir) {
+    override fun CompilerConfiguration.updateConfiguration() {
+        languageVersionSettings = LanguageVersionSettingsImpl(
+            languageVersionSettings.languageVersion,
+            languageVersionSettings.apiVersion,
+            specificFeatures = hashMapOf(
+                LanguageFeature.MultiPlatformProjects to LanguageFeature.State.ENABLED
+            )
+        )
+    }
+
+    @Test
+    fun testExpectActualMatching() {
+        check(
+            """
+                import androidx.compose.runtime.Composable
+                actual fun <!MISMATCHED_COMPOSABLE_IN_EXPECT_ACTUAL!>A<!>() {}
+                @Composable actual fun <!MISMATCHED_COMPOSABLE_IN_EXPECT_ACTUAL!>B<!>() {}
+            """,
+            """
+                import androidx.compose.runtime.Composable
+                @Composable expect fun A()
+                expect fun B()
+            """,
+        )
+    }
+}
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/TryCatchComposableCheckerTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/TryCatchComposableCheckerTests.kt
index 790f010..86ec57c 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/TryCatchComposableCheckerTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/TryCatchComposableCheckerTests.kt
@@ -23,7 +23,7 @@
  * We're strongly considering supporting try-catch-finally blocks in the future.
  * If/when we do support them, these tests should be deleted.
  */
-class TryCatchComposableCheckerTests : AbstractComposeDiagnosticsTest() {
+class TryCatchComposableCheckerTests(useFir: Boolean) : AbstractComposeDiagnosticsTest(useFir) {
     @Test
     fun testTryCatchReporting001() {
         check(
@@ -121,4 +121,117 @@
         """
         )
     }
+
+    @Test
+    fun testTryCatchReporting006() {
+        check(
+            """
+            import androidx.compose.runtime.*
+            @Composable fun A() {}
+
+            @Composable
+            fun test() {
+                <!ILLEGAL_TRY_CATCH_AROUND_COMPOSABLE!>try<!> {
+                    object {
+                        init { A() }
+                    }
+                } finally {}
+            }
+        """
+        )
+    }
+
+    @Test
+    fun testTryCatchReporting007() {
+        check(
+            """
+            import androidx.compose.runtime.*
+            @Composable fun A() {}
+
+            @Composable
+            fun test() {
+                <!ILLEGAL_TRY_CATCH_AROUND_COMPOSABLE!>try<!> {
+                    object {
+                        val x = A()
+                    }
+                } finally {}
+            }
+        """
+        )
+    }
+
+    @Test
+    fun testTryCatchReporting008() {
+        check(
+            """
+            import androidx.compose.runtime.*
+
+            @Composable
+            fun test() {
+                <!ILLEGAL_TRY_CATCH_AROUND_COMPOSABLE!>try<!> {
+                    val x by remember { lazy { 0 } }
+                    print(x)
+                } finally {}
+            }
+        """
+        )
+    }
+
+    @Test
+    fun testTryCatchReporting009() {
+        check(
+            """
+            import androidx.compose.runtime.*
+            @Composable fun A() {}
+
+            @Composable
+            fun test() {
+                try {
+                    object {
+                        val x: Int
+                            @Composable get() = remember { 0 }
+                    }
+                } finally {}
+            }
+        """
+        )
+    }
+
+    @Test
+    fun testTryCatchReporting010() {
+        check(
+            """
+            import androidx.compose.runtime.*
+            @Composable fun A() {}
+
+            @Composable
+            fun test() {
+                try {
+                    class C {
+                        init { <!COMPOSABLE_INVOCATION!>A<!>() }
+                    }
+                } finally {}
+            }
+        """
+        )
+    }
+
+    @Test
+    fun testTryCatchReporting011() {
+        check(
+            """
+            import androidx.compose.runtime.*
+            @Composable fun A() {}
+
+            @Composable
+            fun test() {
+                try {
+                    @Composable fun B() {
+                        A()
+                    }
+                } finally {}
+            }
+        """
+        )
+    }
 }
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/debug/AbstractDebuggerTest.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/debug/AbstractDebuggerTest.kt
index 3c55338..7115e90 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/debug/AbstractDebuggerTest.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/debug/AbstractDebuggerTest.kt
@@ -52,16 +52,13 @@
 import org.junit.BeforeClass
 import org.junit.Rule
 import org.junit.rules.TemporaryFolder
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 private const val RUNNER_CLASS = "RunnerKt"
 private const val MAIN_METHOD = "main"
 private const val CONTENT_METHOD = "content"
 private const val TEST_CLASS = "TestKt"
 
-@RunWith(JUnit4::class)
-abstract class AbstractDebuggerTest : AbstractCodegenTest(useFir = false) {
+abstract class AbstractDebuggerTest(useFir: Boolean) : AbstractCodegenTest(useFir) {
     companion object {
         private lateinit var testServerProcess: Process
         lateinit var virtualMachine: VirtualMachine
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/debug/StepTest.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/debug/StepTest.kt
index 23bb002..597666e 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/debug/StepTest.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/debug/StepTest.kt
@@ -18,7 +18,7 @@
 
 import org.junit.Test
 
-class StepTest : AbstractDebuggerTest() {
+class StepTest(useFir: Boolean) : AbstractDebuggerTest(useFir) {
     @Test
     fun testSteppingIntoIf() {
         collectDebugEvents(
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/facade/K1CompilerFacade.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/facade/K1CompilerFacade.kt
index b3b6beb..b9e35de 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/facade/K1CompilerFacade.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/facade/K1CompilerFacade.kt
@@ -30,16 +30,18 @@
 import org.jetbrains.kotlin.psi.KtFile
 import org.jetbrains.kotlin.resolve.AnalyzingUtils
 import org.jetbrains.kotlin.resolve.BindingContext
+import org.jetbrains.kotlin.resolve.multiplatform.isCommonSource
 
 class K1AnalysisResult(
     override val files: List<KtFile>,
     val moduleDescriptor: ModuleDescriptor,
     val bindingContext: BindingContext
 ) : AnalysisResult {
-    override val diagnostics: List<AnalysisResult.Diagnostic>
-        get() = bindingContext.diagnostics.all().map {
-            AnalysisResult.Diagnostic(it.factoryName, it.textRanges)
-        }
+    override val diagnostics: Map<String, List<AnalysisResult.Diagnostic>>
+        get() = bindingContext.diagnostics.all().groupBy(
+            keySelector = { it.psiFile.name },
+            valueTransform = { AnalysisResult.Diagnostic(it.factoryName, it.textRanges) }
+        )
 }
 
 private class K1FrontendResult(
@@ -49,11 +51,20 @@
 )
 
 class K1CompilerFacade(environment: KotlinCoreEnvironment) : KotlinCompilerFacade(environment) {
-    override fun analyze(files: List<SourceFile>): K1AnalysisResult {
-        val ktFiles = files.map { it.toKtFile(environment.project) }
+    override fun analyze(
+        platformFiles: List<SourceFile>,
+        commonFiles: List<SourceFile>
+    ): K1AnalysisResult {
+        val allKtFiles = platformFiles.map { it.toKtFile(environment.project) } +
+            commonFiles.map {
+                it.toKtFile(environment.project).also { ktFile ->
+                    ktFile.isCommonSource = true
+                }
+            }
+
         val result = TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(
             environment.project,
-            ktFiles,
+            allKtFiles,
             CliBindingTrace(),
             environment.configuration,
             environment::createPackagePartProvider
@@ -65,11 +76,14 @@
             throw TestsCompilerError(e)
         }
 
-        return K1AnalysisResult(ktFiles, result.moduleDescriptor, result.bindingContext)
+        return K1AnalysisResult(allKtFiles, result.moduleDescriptor, result.bindingContext)
     }
 
-    private fun frontend(files: List<SourceFile>): K1FrontendResult {
-        val analysisResult = analyze(files)
+    private fun frontend(
+        platformFiles: List<SourceFile>,
+        commonFiles: List<SourceFile>
+    ): K1FrontendResult {
+        val analysisResult = analyze(platformFiles, commonFiles)
 
         // `analyze` only throws if the analysis itself failed, since we use it to test code
         // with errors. That's why we have to check for errors before we run psi2ir.
@@ -116,15 +130,17 @@
     }
 
     override fun compileToIr(files: List<SourceFile>): IrModuleFragment =
-        frontend(files).backendInput.irModuleFragment
+        frontend(files, listOf()).backendInput.irModuleFragment
 
-    override fun compile(files: List<SourceFile>): GenerationState =
-        try {
-            frontend(files).apply {
-                codegenFactory.generateModule(state, backendInput)
-                state.factory.done()
-            }.state
-        } catch (e: Exception) {
-            throw TestsCompilerError(e)
-        }
+    override fun compile(
+        platformFiles: List<SourceFile>,
+        commonFiles: List<SourceFile>
+    ): GenerationState = try {
+        frontend(platformFiles, commonFiles).apply {
+            codegenFactory.generateModule(state, backendInput)
+            state.factory.done()
+        }.state
+    } catch (e: Exception) {
+        throw TestsCompilerError(e)
+    }
 }
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/facade/K2CompilerFacade.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/facade/K2CompilerFacade.kt
index a0b5f13..7852b00 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/facade/K2CompilerFacade.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/facade/K2CompilerFacade.kt
@@ -19,10 +19,9 @@
 import com.intellij.openapi.project.Project
 import com.intellij.openapi.vfs.StandardFileSystems
 import com.intellij.openapi.vfs.VirtualFileManager
-import com.intellij.psi.PsiElementFinder
-import com.intellij.psi.search.GlobalSearchScope
 import com.intellij.psi.search.ProjectScope
-import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
+import java.util.concurrent.ConcurrentHashMap
+import org.jetbrains.kotlin.analyzer.common.CommonPlatformAnalyzerServices
 import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
 import org.jetbrains.kotlin.backend.jvm.JvmGeneratorExtensions
 import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory
@@ -40,82 +39,163 @@
 import org.jetbrains.kotlin.config.CommonConfigurationKeys
 import org.jetbrains.kotlin.config.CompilerConfiguration
 import org.jetbrains.kotlin.config.JVMConfigurationKeys
-import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
+import org.jetbrains.kotlin.config.languageVersionSettings
+import org.jetbrains.kotlin.constant.ConstantValue
+import org.jetbrains.kotlin.constant.EvaluatedConstTracker
 import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory
 import org.jetbrains.kotlin.diagnostics.impl.BaseDiagnosticsCollector
+import org.jetbrains.kotlin.fir.BinaryModuleData
+import org.jetbrains.kotlin.fir.DependencyListForCliModule
+import org.jetbrains.kotlin.fir.FirModuleData
+import org.jetbrains.kotlin.fir.FirModuleDataImpl
 import org.jetbrains.kotlin.fir.FirSession
-import org.jetbrains.kotlin.fir.backend.Fir2IrResult
+import org.jetbrains.kotlin.fir.backend.Fir2IrConfiguration
 import org.jetbrains.kotlin.fir.backend.jvm.FirJvmBackendClassResolver
 import org.jetbrains.kotlin.fir.backend.jvm.FirJvmBackendExtension
 import org.jetbrains.kotlin.fir.backend.jvm.JvmFir2IrExtensions
 import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar
+import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider
+import org.jetbrains.kotlin.fir.pipeline.Fir2IrActualizedResult
 import org.jetbrains.kotlin.fir.pipeline.FirResult
-import org.jetbrains.kotlin.fir.pipeline.ModuleCompilerAnalyzedOutput
-import org.jetbrains.kotlin.fir.pipeline.buildFirFromKtFiles
-import org.jetbrains.kotlin.fir.pipeline.convertToIrAndActualize
-import org.jetbrains.kotlin.fir.pipeline.runCheckers
-import org.jetbrains.kotlin.fir.pipeline.runResolution
-import org.jetbrains.kotlin.fir.session.FirSessionFactoryHelper
+import org.jetbrains.kotlin.fir.pipeline.buildResolveAndCheckFir
+import org.jetbrains.kotlin.fir.pipeline.convertToIrAndActualizeForJvm
+import org.jetbrains.kotlin.fir.session.FirJvmSessionFactory
+import org.jetbrains.kotlin.fir.session.environment.AbstractProjectEnvironment
 import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmIrMangler
 import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
-import org.jetbrains.kotlin.load.kotlin.PackagePartProvider
 import org.jetbrains.kotlin.name.Name
+import org.jetbrains.kotlin.platform.CommonPlatforms
 import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
 import org.jetbrains.kotlin.psi.KtFile
 import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices
 
 class FirAnalysisResult(
-    val moduleCompilerAnalyzedOutput: ModuleCompilerAnalyzedOutput,
+    val firResult: FirResult,
     override val files: List<KtFile>,
     val reporter: BaseDiagnosticsCollector
 ) : AnalysisResult {
-    override val diagnostics: List<AnalysisResult.Diagnostic>
-        get() = reporter.diagnostics.map {
-            AnalysisResult.Diagnostic(it.factoryName, it.textRanges)
-        }
+    override val diagnostics: Map<String, List<AnalysisResult.Diagnostic>>
+        get() = reporter.diagnostics.groupBy(
+            keySelector = { it.psiElement.containingFile.name },
+            valueTransform = { AnalysisResult.Diagnostic(it.factoryName, it.textRanges) }
+        )
 }
 
 private class FirFrontendResult(
-    val session: FirSession,
-    val firResult: Fir2IrResult,
+    val firResult: Fir2IrActualizedResult,
     val generatorExtensions: JvmGeneratorExtensions,
 )
 
 class K2CompilerFacade(environment: KotlinCoreEnvironment) : KotlinCompilerFacade(environment) {
-    init {
-        PsiElementFinder.EP.getPoint(environment.project)
-            .unregisterExtension(JavaElementFinder::class.java)
-    }
-
     private val project: Project
         get() = environment.project
 
     private val configuration: CompilerConfiguration
         get() = environment.configuration
 
-    override fun analyze(files: List<SourceFile>): FirAnalysisResult {
-        val ktFiles = files.map { it.toKtFile(project) }
-
-        val session = createSessionForTests(
-            sourceScope = GlobalSearchScope.filesScope(project, ktFiles.map { it.virtualFile })
-                .uniteWith(TopDownAnalyzerFacadeForJVM.AllJavaSourcesInProjectScope(project)),
-            librariesScope = ProjectScope.getLibrariesScope(project),
-            moduleName = configuration.get(CommonConfigurationKeys.MODULE_NAME, "main"),
-            getPackagePartProvider = environment::createPackagePartProvider
+    private fun createSourceSession(
+        moduleData: FirModuleData,
+        projectSessionProvider: FirProjectSessionProvider,
+        projectEnvironment: AbstractProjectEnvironment
+    ): FirSession {
+        return FirJvmSessionFactory.createModuleBasedSession(
+            moduleData,
+            projectSessionProvider,
+            PsiBasedProjectFileSearchScope(
+                TopDownAnalyzerFacadeForJVM.AllJavaSourcesInProjectScope(
+                    project
+                )
+            ),
+            projectEnvironment,
+            null,
+            FirExtensionRegistrar.getInstances(project),
+            configuration.languageVersionSettings,
+            configuration.get(CommonConfigurationKeys.LOOKUP_TRACKER),
+            configuration.get(CommonConfigurationKeys.ENUM_WHEN_TRACKER),
+            needRegisterJavaElementFinder = true,
         )
+    }
+
+    override fun analyze(
+        platformFiles: List<SourceFile>,
+        commonFiles: List<SourceFile>
+    ): FirAnalysisResult {
+        val rootModuleName = configuration.get(CommonConfigurationKeys.MODULE_NAME, "main")
+
+        val projectSessionProvider = FirProjectSessionProvider()
+        val binaryModuleData = BinaryModuleData.initialize(
+            Name.identifier(rootModuleName),
+            CommonPlatforms.defaultCommonPlatform,
+            CommonPlatformAnalyzerServices
+        )
+        val dependencyList = DependencyListForCliModule.build(binaryModuleData)
+        val projectEnvironment = VfsBasedProjectEnvironment(
+            project,
+            VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL),
+            environment::createPackagePartProvider
+        )
+        val librariesScope = PsiBasedProjectFileSearchScope(ProjectScope.getLibrariesScope(project))
+
+        FirJvmSessionFactory.createLibrarySession(
+            Name.identifier(rootModuleName),
+            projectSessionProvider,
+            dependencyList.moduleDataProvider,
+            projectEnvironment,
+            FirExtensionRegistrar.getInstances(project),
+            librariesScope,
+            projectEnvironment.getPackagePartProvider(librariesScope),
+            configuration.languageVersionSettings,
+            registerExtraComponents = {}
+        )
+
+        val commonModuleData = FirModuleDataImpl(
+            Name.identifier("$rootModuleName-common"),
+            dependencyList.regularDependencies,
+            dependencyList.dependsOnDependencies,
+            dependencyList.friendsDependencies,
+            CommonPlatforms.defaultCommonPlatform,
+            CommonPlatformAnalyzerServices
+        )
+
+        val platformModuleData = FirModuleDataImpl(
+            Name.identifier(rootModuleName),
+            dependencyList.regularDependencies,
+            dependencyList.dependsOnDependencies + commonModuleData,
+            dependencyList.friendsDependencies,
+            JvmPlatforms.jvm8,
+            JvmPlatformAnalyzerServices
+        )
+
+        val commonSession = createSourceSession(
+            commonModuleData,
+            projectSessionProvider,
+            projectEnvironment
+        )
+        val platformSession = createSourceSession(
+            platformModuleData,
+            projectSessionProvider,
+            projectEnvironment
+        )
+
+        val commonKtFiles = commonFiles.map { it.toKtFile(project) }
+        val platformKtFiles = platformFiles.map { it.toKtFile(project) }
+
         val reporter = DiagnosticReporterFactory.createReporter()
-        val firFiles = session.buildFirFromKtFiles(ktFiles)
-        val scopeSession = session.runResolution(firFiles).first
-        session.runCheckers(scopeSession, firFiles, reporter)
+        val commonAnalysis = buildResolveAndCheckFir(commonSession, commonKtFiles, reporter)
+        val platformAnalysis = buildResolveAndCheckFir(platformSession, platformKtFiles, reporter)
+
         return FirAnalysisResult(
-            ModuleCompilerAnalyzedOutput(session, scopeSession, firFiles),
-            ktFiles,
+            FirResult(listOf(commonAnalysis, platformAnalysis)),
+            commonKtFiles + platformKtFiles,
             reporter
         )
     }
 
-    private fun frontend(files: List<SourceFile>): FirFrontendResult {
-        val analysisResult = analyze(files)
+    private fun frontend(
+        platformFiles: List<SourceFile>,
+        commonFiles: List<SourceFile>
+    ): FirFrontendResult {
+        val analysisResult = analyze(platformFiles, commonFiles)
 
         FirDiagnosticsCompilerResultsReporter.throwFirstErrorAsException(
             analysisResult.reporter,
@@ -128,27 +208,51 @@
             JvmIrMangler
         )
 
-        val fir2IrResult = FirResult(
-            analysisResult.moduleCompilerAnalyzedOutput,
-            null
-        ).convertToIrAndActualize(
+        val fir2IrResult = analysisResult.firResult.convertToIrAndActualizeForJvm(
             fir2IrExtensions,
+            Fir2IrConfiguration(
+                configuration.languageVersionSettings,
+                configuration.getBoolean(JVMConfigurationKeys.LINK_VIA_SIGNATURES),
+                object : EvaluatedConstTracker() {
+                    private val storage = ConcurrentHashMap<
+                        String,
+                        ConcurrentHashMap<Pair<Int, Int>, ConstantValue<*>>>()
+
+                    override fun save(
+                        start: Int,
+                        end: Int,
+                        file: String,
+                        constant: ConstantValue<*>
+                    ) {
+                        storage
+                            .getOrPut(file) { ConcurrentHashMap() }
+                            .let { it[start to end] = constant }
+                    }
+
+                    override fun load(start: Int, end: Int, file: String): ConstantValue<*>? {
+                        return storage[file]?.get(start to end)
+                    }
+
+                    override fun load(file: String): Map<Pair<Int, Int>, ConstantValue<*>>? {
+                        return storage[file]
+                    }
+                }
+            ),
             IrGenerationExtension.getInstances(project),
-            configuration.getBoolean(JVMConfigurationKeys.LINK_VIA_SIGNATURES)
+            analysisResult.reporter
         )
 
-        return FirFrontendResult(
-            analysisResult.moduleCompilerAnalyzedOutput.session,
-            fir2IrResult,
-            fir2IrExtensions
-        )
+        return FirFrontendResult(fir2IrResult, fir2IrExtensions)
     }
 
     override fun compileToIr(files: List<SourceFile>): IrModuleFragment =
-        frontend(files).firResult.irModuleFragment
+        frontend(files, listOf()).firResult.irModuleFragment
 
-    override fun compile(files: List<SourceFile>): GenerationState {
-        val frontendResult = frontend(files)
+    override fun compile(
+        platformFiles: List<SourceFile>,
+        commonFiles: List<SourceFile>
+    ): GenerationState {
+        val frontendResult = frontend(platformFiles, commonFiles)
         val irModuleFragment = frontendResult.firResult.irModuleFragment
         val components = frontendResult.firResult.components
 
@@ -172,37 +276,10 @@
         codegenFactory.generateModuleInFrontendIRMode(
             generationState, irModuleFragment, components.symbolTable, components.irProviders,
             frontendResult.generatorExtensions,
-            FirJvmBackendExtension(frontendResult.session, components),
+            FirJvmBackendExtension(components, frontendResult.firResult.irActualizedResult),
             frontendResult.firResult.pluginContext
         ) {}
         generationState.factory.done()
         return generationState
     }
-
-    private fun createSessionForTests(
-        sourceScope: GlobalSearchScope,
-        librariesScope: GlobalSearchScope,
-        moduleName: String,
-        getPackagePartProvider: (GlobalSearchScope) -> PackagePartProvider,
-    ): FirSession {
-        return FirSessionFactoryHelper.createSessionWithDependencies(
-            Name.identifier(moduleName),
-            JvmPlatforms.unspecifiedJvmPlatform,
-            JvmPlatformAnalyzerServices,
-            externalSessionProvider = null,
-            VfsBasedProjectEnvironment(
-                project,
-                VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL),
-                getPackagePartProvider
-            ),
-            languageVersionSettings = LanguageVersionSettingsImpl.DEFAULT,
-            PsiBasedProjectFileSearchScope(sourceScope),
-            PsiBasedProjectFileSearchScope(librariesScope),
-            lookupTracker = null,
-            enumWhenTracker = null,
-            incrementalCompilationContext = null,
-            extensionRegistrars = FirExtensionRegistrar.getInstances(project),
-            needRegisterJavaElementFinder = true,
-        )
-    }
 }
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/facade/KotlinCompilerFacade.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/facade/KotlinCompilerFacade.kt
index 8b2807e..a09b001 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/facade/KotlinCompilerFacade.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/facade/KotlinCompilerFacade.kt
@@ -16,16 +16,16 @@
 
 package androidx.compose.compiler.plugins.kotlin.facade
 
-import androidx.compose.compiler.plugins.kotlin.ComposeComponentRegistrar
+import androidx.compose.compiler.plugins.kotlin.ComposePluginRegistrar
 import androidx.compose.compiler.plugins.kotlin.TestsCompilerError
 import com.intellij.openapi.Disposable
 import com.intellij.openapi.project.Project
 import com.intellij.openapi.util.TextRange
 import com.intellij.openapi.util.text.StringUtilRt
-import com.intellij.openapi.vfs.CharsetToolkit
 import com.intellij.psi.PsiFileFactory
 import com.intellij.psi.impl.PsiFileFactoryImpl
 import com.intellij.testFramework.LightVirtualFile
+import java.nio.charset.StandardCharsets
 import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
 import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
 import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation
@@ -38,6 +38,7 @@
 import org.jetbrains.kotlin.config.CompilerConfiguration
 import org.jetbrains.kotlin.config.JVMConfigurationKeys
 import org.jetbrains.kotlin.config.JvmTarget
+import org.jetbrains.kotlin.config.languageVersionSettings
 import org.jetbrains.kotlin.idea.KotlinLanguage
 import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
 import org.jetbrains.kotlin.ir.util.IrMessageLogger
@@ -62,7 +63,7 @@
             override fun getPath(): String = "/$name"
         }
 
-        virtualFile.charset = CharsetToolkit.UTF8_CHARSET
+        virtualFile.charset = StandardCharsets.UTF_8
         val factory = PsiFileFactory.getInstance(project) as PsiFileFactoryImpl
         val ktFile = factory.trySetupPsiForFile(
             virtualFile, KotlinLanguage.INSTANCE, true, false
@@ -86,13 +87,19 @@
     )
 
     val files: List<KtFile>
-    val diagnostics: List<Diagnostic>
+    val diagnostics: Map<String, List<Diagnostic>>
 }
 
 abstract class KotlinCompilerFacade(val environment: KotlinCoreEnvironment) {
-    abstract fun analyze(files: List<SourceFile>): AnalysisResult
+    abstract fun analyze(
+        platformFiles: List<SourceFile>,
+        commonFiles: List<SourceFile>
+    ): AnalysisResult
     abstract fun compileToIr(files: List<SourceFile>): IrModuleFragment
-    abstract fun compile(files: List<SourceFile>): GenerationState
+    abstract fun compile(
+        platformFiles: List<SourceFile>,
+        commonFiles: List<SourceFile>
+    ): GenerationState
 
     companion object {
         const val TEST_MODULE_NAME = "test-module"
@@ -116,11 +123,11 @@
                 disposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES
             )
 
-            ComposeComponentRegistrar.checkCompilerVersion(configuration)
+            ComposePluginRegistrar.checkCompilerVersion(configuration)
 
             environment.project.registerExtensions(configuration)
 
-            return if (configuration.getBoolean(CommonConfigurationKeys.USE_FIR)) {
+            return if (configuration.languageVersionSettings.languageVersion.usesK2) {
                 K2CompilerFacade(environment)
             } else {
                 K1CompilerFacade(environment)
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeFqNames.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeFqNames.kt
index 27a64eb..97aa89f 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeFqNames.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeFqNames.kt
@@ -42,11 +42,13 @@
     val ComposableTarget = classIdFor("ComposableTarget")
     val ComposeVersion = classIdFor("ComposeVersion")
     val Composer = classIdFor("Composer")
+    val DisallowComposableCalls = classIdFor("DisallowComposableCalls")
     val FunctionKeyMetaClass = internalClassIdFor("FunctionKeyMetaClass")
     val FunctionKeyMeta = internalClassIdFor("FunctionKeyMeta")
     val LiveLiteralFileInfo = internalClassIdFor("LiveLiteralFileInfo")
     val LiveLiteralInfo = internalClassIdFor("LiveLiteralInfo")
     val NoLiveLiterals = classIdFor("NoLiveLiterals")
+    val ReadOnlyComposable = classIdFor("ReadOnlyComposable")
     val State = classIdFor("State")
     val StabilityInferred = internalClassIdFor("StabilityInferred")
 }
@@ -98,8 +100,8 @@
     val ComposableInferredTargetSchemeArgument = Name.identifier("scheme")
     val CurrentComposerIntrinsic = fqNameFor("<get-currentComposer>")
     val getCurrentComposerFullName = composablesFqNameFor("<get-currentComposer>")
-    val DisallowComposableCalls = fqNameFor("DisallowComposableCalls")
-    val ReadOnlyComposable = fqNameFor("ReadOnlyComposable")
+    val DisallowComposableCalls = ComposeClassIds.DisallowComposableCalls.asSingleFqName()
+    val ReadOnlyComposable = ComposeClassIds.ReadOnlyComposable.asSingleFqName()
     val ExplicitGroupsComposable = fqNameFor("ExplicitGroupsComposable")
     val NonRestartableComposable = fqNameFor("NonRestartableComposable")
     val composableLambdaType = ComposeClassIds.ComposableLambda.asSingleFqName()
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeIrGenerationExtension.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeIrGenerationExtension.kt
index 17c8e1b..1b7b71c 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeIrGenerationExtension.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeIrGenerationExtension.kt
@@ -19,6 +19,7 @@
 import androidx.compose.compiler.plugins.kotlin.lower.ClassStabilityTransformer
 import androidx.compose.compiler.plugins.kotlin.lower.ComposableFunInterfaceLowering
 import androidx.compose.compiler.plugins.kotlin.lower.ComposableFunctionBodyTransformer
+import androidx.compose.compiler.plugins.kotlin.lower.ComposableLambdaAnnotator
 import androidx.compose.compiler.plugins.kotlin.lower.ComposableSymbolRemapper
 import androidx.compose.compiler.plugins.kotlin.lower.ComposableTargetAnnotationsTransformer
 import androidx.compose.compiler.plugins.kotlin.lower.ComposerIntrinsicTransformer
@@ -41,6 +42,7 @@
 import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsGlobalDeclarationTable
 import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerIr
 import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
+import org.jetbrains.kotlin.ir.visitors.acceptVoid
 import org.jetbrains.kotlin.platform.isJs
 import org.jetbrains.kotlin.platform.jvm.isJvm
 
@@ -54,6 +56,7 @@
     private val metricsDestination: String? = null,
     private val reportsDestination: String? = null,
     private val validateIr: Boolean = false,
+    private val useK2: Boolean = false,
 ) : IrGenerationExtension {
     var metrics: ModuleMetrics = EmptyModuleMetrics
 
@@ -72,6 +75,10 @@
         // create a symbol remapper to be used across all transforms
         val symbolRemapper = ComposableSymbolRemapper()
 
+        if (useK2) {
+            moduleFragment.acceptVoid(ComposableLambdaAnnotator(pluginContext))
+        }
+
         if (metricsDestination != null || reportsDestination != null) {
             metrics = ModuleMetricsImpl(moduleFragment.name.asString())
         }
@@ -108,7 +115,9 @@
             metrics
         ).lower(moduleFragment)
 
-        CopyDefaultValuesFromExpectLowering(pluginContext).lower(moduleFragment)
+        if (!useK2) {
+            CopyDefaultValuesFromExpectLowering(pluginContext).lower(moduleFragment)
+        }
 
         val mangler = when {
             pluginContext.platform.isJs() -> JsManglerIr
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt
index f6bbcbe..07ea0ae 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposePlugin.kt
@@ -21,9 +21,8 @@
 import androidx.compose.compiler.plugins.kotlin.k1.ComposableTargetChecker
 import androidx.compose.compiler.plugins.kotlin.k1.ComposeDiagnosticSuppressor
 import androidx.compose.compiler.plugins.kotlin.k1.ComposeTypeResolutionInterceptorExtension
+import androidx.compose.compiler.plugins.kotlin.k2.ComposeFirExtensionRegistrar
 import androidx.compose.compiler.plugins.kotlin.lower.ClassStabilityFieldSerializationPlugin
-import com.intellij.mock.MockProject
-import com.intellij.openapi.project.Project
 import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
 import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
 import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
@@ -31,13 +30,16 @@
 import org.jetbrains.kotlin.compiler.plugin.CliOption
 import org.jetbrains.kotlin.compiler.plugin.CliOptionProcessingException
 import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor
+import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar
 import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
 import org.jetbrains.kotlin.config.CompilerConfiguration
 import org.jetbrains.kotlin.config.CompilerConfigurationKey
 import org.jetbrains.kotlin.config.JVMConfigurationKeys
 import org.jetbrains.kotlin.config.KotlinCompilerVersion
+import org.jetbrains.kotlin.config.languageVersionSettings
 import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
 import org.jetbrains.kotlin.extensions.internal.TypeResolutionInterceptor
+import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrarAdapter
 import org.jetbrains.kotlin.serialization.DescriptorSerializerPlugin
 
 object ComposeConfiguration {
@@ -60,8 +62,8 @@
     val INTRINSIC_REMEMBER_OPTIMIZATION_ENABLED_KEY =
         CompilerConfigurationKey<Boolean>("Enable optimization to treat remember as an intrinsic")
     val SUPPRESS_KOTLIN_VERSION_COMPATIBILITY_CHECK = CompilerConfigurationKey<String?>(
-            "Version of Kotlin for which version compatibility check should be suppressed"
-        )
+        "Version of Kotlin for which version compatibility check should be suppressed"
+    )
     val DECOYS_ENABLED_KEY =
         CompilerConfigurationKey<Boolean>("Generate decoy methods in IR transform")
 }
@@ -195,16 +197,14 @@
 }
 
 @OptIn(ExperimentalCompilerApi::class)
-class ComposeComponentRegistrar :
-    @Suppress("DEPRECATION") org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar {
-    override fun registerProjectComponents(
-        project: MockProject,
-        configuration: CompilerConfiguration
-    ) {
+class ComposePluginRegistrar : CompilerPluginRegistrar() {
+    override val supportsK2: Boolean
+        get() = true
+
+    override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) {
         if (checkCompilerVersion(configuration)) {
-            registerCommonExtensions(project)
+            registerCommonExtensions()
             IrGenerationExtension.registerExtension(
-                project,
                 createComposeIrExtension(configuration)
             )
         }
@@ -213,7 +213,7 @@
     companion object {
         fun checkCompilerVersion(configuration: CompilerConfiguration): Boolean {
             try {
-                val KOTLIN_VERSION_EXPECTATION = "1.8.22"
+                val KOTLIN_VERSION_EXPECTATION = "1.9.0"
                 KotlinCompilerVersion.getVersion()?.let { version ->
                     val msgCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
                     val suppressKotlinVersionCheck = configuration.get(
@@ -288,33 +288,20 @@
             }
         }
 
-        fun registerCommonExtensions(project: Project) {
-            StorageComponentContainerContributor.registerExtension(
-                project,
-                ComposableCallChecker()
-            )
-            StorageComponentContainerContributor.registerExtension(
-                project,
-                ComposableDeclarationChecker()
-            )
-            StorageComponentContainerContributor.registerExtension(
-                project,
-                ComposableTargetChecker()
-            )
-            ComposeDiagnosticSuppressor.registerExtension(
-                project,
-                ComposeDiagnosticSuppressor()
-            )
+        fun ExtensionStorage.registerCommonExtensions() {
+            StorageComponentContainerContributor.registerExtension(ComposableCallChecker())
+            StorageComponentContainerContributor.registerExtension(ComposableDeclarationChecker())
+            StorageComponentContainerContributor.registerExtension(ComposableTargetChecker())
+            ComposeDiagnosticSuppressor.registerExtension(ComposeDiagnosticSuppressor())
             @Suppress("OPT_IN_USAGE_ERROR")
             TypeResolutionInterceptor.registerExtension(
-                project,
                 @Suppress("IllegalExperimentalApiUsage")
                 ComposeTypeResolutionInterceptorExtension()
             )
             DescriptorSerializerPlugin.registerExtension(
-                project,
                 ClassStabilityFieldSerializationPlugin()
             )
+            FirExtensionRegistrarAdapter.registerExtension(ComposeFirExtensionRegistrar())
         }
 
         fun createComposeIrExtension(
@@ -350,6 +337,7 @@
             val validateIr = configuration.getBoolean(
                 JVMConfigurationKeys.VALIDATE_IR
             )
+            val useK2 = configuration.languageVersionSettings.languageVersion.usesK2
 
             return ComposeIrGenerationExtension(
                 liveLiteralsEnabled = liveLiteralsEnabled,
@@ -361,6 +349,7 @@
                 metricsDestination = metricsDestination,
                 reportsDestination = reportsDestination,
                 validateIr = validateIr,
+                useK2 = useK2,
             )
         }
     }
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt
index 26443df..c20d2da 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/VersionChecker.kt
@@ -127,7 +127,7 @@
          * The maven version string of this compiler. This string should be updated before/after every
          * release.
          */
-        const val compilerVersion: String = "1.4.8"
+        const val compilerVersion: String = "1.5.1"
         private val minimumRuntimeVersion: String
             get() = runtimeVersionToMavenVersionTable[minimumRuntimeVersionInt] ?: "unknown"
     }
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/analysis/Stability.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/analysis/Stability.kt
index 1fabebc..d0ef2f0 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/analysis/Stability.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/analysis/Stability.kt
@@ -18,6 +18,7 @@
 
 import androidx.compose.compiler.plugins.kotlin.ComposeFqNames
 import androidx.compose.compiler.plugins.kotlin.lower.annotationClass
+import androidx.compose.compiler.plugins.kotlin.lower.isSyntheticComposableFunction
 import org.jetbrains.kotlin.backend.jvm.ir.isInlineClassType
 import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer
 import org.jetbrains.kotlin.ir.declarations.IrClass
@@ -346,6 +347,7 @@
         type.isUnit() ||
             type.isPrimitiveType() ||
             type.isFunctionOrKFunction() ||
+            type.isSyntheticComposableFunction() ||
             type.isString() -> Stability.Stable
 
         type.isTypeParameter() -> {
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k1/ComposableDeclarationChecker.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k1/ComposableDeclarationChecker.kt
index c47f997..7d85837 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k1/ComposableDeclarationChecker.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k1/ComposableDeclarationChecker.kt
@@ -37,6 +37,7 @@
 import org.jetbrains.kotlin.psi.KtPropertyAccessor
 import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
 import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
+import org.jetbrains.kotlin.resolve.multiplatform.findCompatibleExpectsForActual
 import org.jetbrains.kotlin.types.KotlinType
 import org.jetbrains.kotlin.types.typeUtil.supertypes
 import org.jetbrains.kotlin.util.OperatorNameConventions
@@ -128,6 +129,18 @@
             )
         }
 
+        if (descriptor.isActual) {
+            val expectDescriptor = descriptor.findCompatibleExpectsForActual().singleOrNull()
+            if (expectDescriptor != null &&
+                expectDescriptor.hasComposableAnnotation() != hasComposableAnnotation) {
+                context.trace.report(
+                    ComposeErrors.MISMATCHED_COMPOSABLE_IN_EXPECT_ACTUAL.on(
+                        declaration.nameIdentifier ?: declaration
+                    )
+                )
+            }
+        }
+
         if (hasComposableAnnotation && descriptor.modality == Modality.ABSTRACT) {
             declaration.valueParameters.forEach {
                 val defaultValue = it.defaultValue
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k1/ComposeDiagnosticSuppressor.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k1/ComposeDiagnosticSuppressor.kt
index fc11167..af2523e 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k1/ComposeDiagnosticSuppressor.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k1/ComposeDiagnosticSuppressor.kt
@@ -17,7 +17,6 @@
 package androidx.compose.compiler.plugins.kotlin.k1
 
 import com.intellij.openapi.extensions.Extensions
-import com.intellij.openapi.project.Project
 import org.jetbrains.kotlin.diagnostics.Diagnostic
 import org.jetbrains.kotlin.diagnostics.Errors
 import org.jetbrains.kotlin.psi.KtAnnotatedExpression
@@ -31,12 +30,11 @@
 
     companion object {
         fun registerExtension(
-            project: Project,
             extension: DiagnosticSuppressor
         ) {
             @Suppress("DEPRECATION")
             Extensions.getRootArea().getExtensionPoint(DiagnosticSuppressor.EP_NAME)
-                .registerExtension(extension, project)
+                .registerExtension(extension)
         }
     }
 
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k1/ComposeErrorMessages.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k1/ComposeErrorMessages.kt
index d83e84a..04d8b07 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k1/ComposeErrorMessages.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k1/ComposeErrorMessages.kt
@@ -129,6 +129,10 @@
             "Composable setValue operator is not currently supported."
         )
         MAP.put(
+            ComposeErrors.MISMATCHED_COMPOSABLE_IN_EXPECT_ACTUAL,
+            "Mismatched @Composable annotation between expect and actual declaration"
+        )
+        MAP.put(
             ComposeErrors.REDUNDANT_COMPOSABLE_ANNOTATION,
             "Invalid `@Composable` annotation on inline lambda." +
                 " This will become an error in Kotlin 2.0."
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k1/ComposeErrors.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k1/ComposeErrors.kt
index b6bc829a..14ac370 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k1/ComposeErrors.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k1/ComposeErrors.kt
@@ -164,6 +164,12 @@
     val NAMED_ARGUMENTS_NOT_ALLOWED = DiagnosticFactory0.create<PsiElement>(Severity.WARNING)
 
     @JvmField
+    val MISMATCHED_COMPOSABLE_IN_EXPECT_ACTUAL =
+        DiagnosticFactory0.create<PsiElement>(
+            Severity.ERROR
+        )
+
+    @JvmField
     val REDUNDANT_COMPOSABLE_ANNOTATION = DiagnosticFactory0.create<PsiElement>(Severity.WARNING)
 
     init {
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k2/ComposableCallChecker.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k2/ComposableCallChecker.kt
new file mode 100644
index 0000000..b498e6e
--- /dev/null
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k2/ComposableCallChecker.kt
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.compiler.plugins.kotlin.k2
+
+import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
+import org.jetbrains.kotlin.diagnostics.reportOn
+import org.jetbrains.kotlin.fir.FirElement
+import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
+import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirFunctionCallChecker
+import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirPropertyAccessExpressionChecker
+import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
+import org.jetbrains.kotlin.fir.declarations.FirAnonymousInitializer
+import org.jetbrains.kotlin.fir.declarations.FirAnonymousObject
+import org.jetbrains.kotlin.fir.declarations.FirDeclaration
+import org.jetbrains.kotlin.fir.declarations.FirFunction
+import org.jetbrains.kotlin.fir.declarations.FirProperty
+import org.jetbrains.kotlin.fir.declarations.FirPropertyAccessor
+import org.jetbrains.kotlin.fir.declarations.FirValueParameter
+import org.jetbrains.kotlin.fir.declarations.InlineStatus
+import org.jetbrains.kotlin.fir.declarations.utils.isInline
+import org.jetbrains.kotlin.fir.expressions.FirCatch
+import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
+import org.jetbrains.kotlin.fir.expressions.FirLambdaArgumentExpression
+import org.jetbrains.kotlin.fir.expressions.FirPropertyAccessExpression
+import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
+import org.jetbrains.kotlin.fir.expressions.FirTryExpression
+import org.jetbrains.kotlin.fir.expressions.impl.FirResolvedArgumentList
+import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol
+import org.jetbrains.kotlin.fir.references.toResolvedValueParameterSymbol
+import org.jetbrains.kotlin.fir.resolve.isInvoke
+import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
+import org.jetbrains.kotlin.fir.types.coneType
+import org.jetbrains.kotlin.fir.types.functionTypeKind
+
+object ComposablePropertyAccessExpressionChecker : FirPropertyAccessExpressionChecker() {
+    override fun check(
+        expression: FirPropertyAccessExpression,
+        context: CheckerContext,
+        reporter: DiagnosticReporter
+    ) {
+        val calleeFunction = expression.calleeReference.toResolvedCallableSymbol()
+            ?: return
+        if (calleeFunction.isComposable(context.session)) {
+            checkComposableCall(expression, calleeFunction, context, reporter)
+        }
+    }
+}
+
+object ComposableFunctionCallChecker : FirFunctionCallChecker() {
+    override fun check(
+        expression: FirFunctionCall,
+        context: CheckerContext,
+        reporter: DiagnosticReporter
+    ) {
+        val calleeFunction = expression.calleeReference.toResolvedCallableSymbol()
+            ?: return
+        if (calleeFunction.isComposable(context.session)) {
+            checkComposableCall(expression, calleeFunction, context, reporter)
+        } else if (calleeFunction.callableId.isInvoke()) {
+            checkInvoke(expression, context, reporter)
+        }
+    }
+}
+
+/**
+ * Check if `expression` - a call to a composable function or access to a composable property -
+ * is allowed in the current context. It is allowed if:
+ *
+ * - It is executed as part of the body of a composable function.
+ * - It is not executed as part of the body of a lambda annotated with `@DisallowComposableCalls`.
+ * - It is not inside of a `try` block.
+ * - It is a call to a readonly composable function if it is executed in the body of a function
+ *   that is annotated with `@ReadOnlyComposable`.
+ *
+ * A function is composable if:
+ * - It is annotated with `@Composable`.
+ * - It is a lambda whose type is inferred to be `ComposableFunction`.
+ * - It is an inline lambda whose enclosing function is composable.
+ */
+private fun checkComposableCall(
+    expression: FirQualifiedAccessExpression,
+    calleeFunction: FirCallableSymbol<*>,
+    context: CheckerContext,
+    reporter: DiagnosticReporter
+) {
+    context.visitCurrentScope(
+        visitInlineLambdaParameter = { parameter ->
+            if (parameter.returnTypeRef.hasDisallowComposableCallsAnnotation(context.session)) {
+                reporter.reportOn(
+                    expression.calleeReference.source,
+                    ComposeErrors.CAPTURED_COMPOSABLE_INVOCATION,
+                    parameter.symbol,
+                    parameter.containingFunctionSymbol,
+                    context
+                )
+            }
+        },
+        visitAnonymousFunction = { function ->
+            if (function.typeRef.coneType.functionTypeKind(context.session) === ComposableFunction)
+                return
+        },
+        visitFunction = { function ->
+            if (function.hasComposableAnnotation(context.session)) {
+                if (
+                    function.hasReadOnlyComposableAnnotation(context.session) &&
+                    !calleeFunction.isReadOnlyComposable(context.session)
+                ) {
+                    reporter.reportOn(
+                        expression.calleeReference.source,
+                        ComposeErrors.NONREADONLY_CALL_IN_READONLY_COMPOSABLE,
+                        context
+                    )
+                }
+                return
+            }
+            // We allow composable calls in local delegated properties.
+            // The only call this could be is a getValue/setValue in the synthesized getter/setter.
+            if (function is FirPropertyAccessor && function.propertySymbol.hasDelegate) {
+                if (function.propertySymbol.isVar) {
+                    reporter.reportOn(
+                        function.source,
+                        ComposeErrors.COMPOSE_INVALID_DELEGATE,
+                        context
+                    )
+                }
+                // Only local variables can be implicitly composable, for top-level or class-level
+                // declarations we require an explicit annotation.
+                if (!function.propertySymbol.isLocal) {
+                    reporter.reportOn(
+                        function.propertySymbol.source,
+                        ComposeErrors.COMPOSABLE_EXPECTED,
+                        context
+                    )
+                }
+                return
+            }
+            // We've found a non-composable function which contains a composable call.
+            val source = if (function is FirPropertyAccessor) {
+                function.propertySymbol.source
+            } else {
+                function.source
+            }
+            reporter.reportOn(source, ComposeErrors.COMPOSABLE_EXPECTED, context)
+        },
+        visitTryExpression = { tryExpression, container ->
+            // Only report an error if the composable call happens inside of the `try`
+            // block. Composable calls are allowed inside of `catch` and `finally` blocks.
+            if (container !is FirCatch && tryExpression.finallyBlock != container) {
+                reporter.reportOn(
+                    tryExpression.source,
+                    ComposeErrors.ILLEGAL_TRY_CATCH_AROUND_COMPOSABLE,
+                    context
+                )
+            }
+        }
+    )
+    reporter.reportOn(
+        expression.calleeReference.source,
+        ComposeErrors.COMPOSABLE_INVOCATION,
+        context
+    )
+}
+
+/**
+ * Reports an error if we are invoking a lambda parameter of an inline function in a context
+ * where composable calls are not allowed, unless the lambda parameter is itself annotated
+ * with `@DisallowComposableCalls`.
+ */
+private fun checkInvoke(
+    expression: FirQualifiedAccessExpression,
+    context: CheckerContext,
+    reporter: DiagnosticReporter
+) {
+    // Check that we're invoking a value parameter of an inline function
+    val param = (expression.dispatchReceiver as? FirPropertyAccessExpression)
+        ?.calleeReference
+        ?.toResolvedValueParameterSymbol()
+        ?: return
+    if (param.resolvedReturnTypeRef.hasDisallowComposableCallsAnnotation(context.session) ||
+        !param.containingFunctionSymbol.isInline) {
+        return
+    }
+
+    context.visitCurrentScope(
+        visitInlineLambdaParameter = { parameter ->
+            if (parameter.returnTypeRef.hasDisallowComposableCallsAnnotation(context.session)) {
+                reporter.reportOn(
+                    param.source,
+                    ComposeErrors.MISSING_DISALLOW_COMPOSABLE_CALLS_ANNOTATION,
+                    param,
+                    parameter.symbol,
+                    parameter.containingFunctionSymbol,
+                    context
+                )
+            }
+        }
+    )
+}
+
+/**
+ * Visits all (Anonymous)Functions and `try` expressions in the current scope until it finds
+ * a declaration that introduces a new scope. Elements are visited from innermost to outermost.
+ */
+private inline fun CheckerContext.visitCurrentScope(
+    visitInlineLambdaParameter: (FirValueParameter) -> Unit,
+    visitAnonymousFunction: (FirAnonymousFunction) -> Unit = {},
+    visitFunction: (FirFunction) -> Unit = {},
+    visitTryExpression: (FirTryExpression, FirElement) -> Unit = { _, _ -> }
+) {
+    for ((elementIndex, element) in containingElements.withIndex().reversed()) {
+        when (element) {
+            is FirAnonymousFunction -> {
+                if (element.inlineStatus == InlineStatus.Inline) {
+                    findValueParameterForLambdaAtIndex(elementIndex)?.let { parameter ->
+                        visitInlineLambdaParameter(parameter)
+                    }
+                }
+                visitAnonymousFunction(element)
+                if (element.inlineStatus != InlineStatus.Inline) {
+                    return
+                }
+            }
+            is FirFunction -> {
+                visitFunction(element)
+                return
+            }
+            is FirTryExpression -> {
+                val container = containingElements.getOrNull(elementIndex + 1)
+                    ?: continue
+                visitTryExpression(element, container)
+            }
+            is FirProperty -> {
+                // Coming from an initializer or delegate expression, otherwise we'd
+                // have hit a FirFunction and would already report an error.
+            }
+            is FirValueParameter -> {
+                // We're coming from a default value in a function declaration, we need to
+                // look at the enclosing function.
+            }
+            is FirAnonymousObject, is FirAnonymousInitializer -> {
+                // Anonymous objects don't change the current scope, continue.
+            }
+            // Every other declaration introduces a new scope which cannot be composable.
+            is FirDeclaration -> return
+        }
+    }
+}
+
+private fun CheckerContext.findValueParameterForLambdaAtIndex(
+    elementIndex: Int
+): FirValueParameter? {
+    val argument = containingElements.getOrNull(elementIndex - 1) as? FirLambdaArgumentExpression
+        ?: return null
+    val argumentList = containingElements.getOrNull(elementIndex - 2) as? FirResolvedArgumentList
+        ?: return null
+    return argumentList.mapping[argument]
+}
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k2/ComposableCallableReferenceChecker.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k2/ComposableCallableReferenceChecker.kt
new file mode 100644
index 0000000..f7aa35c
--- /dev/null
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k2/ComposableCallableReferenceChecker.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.compiler.plugins.kotlin.k2
+
+import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
+import org.jetbrains.kotlin.diagnostics.reportOn
+import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
+import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirCallableReferenceAccessChecker
+import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess
+import org.jetbrains.kotlin.fir.types.coneType
+import org.jetbrains.kotlin.fir.types.functionTypeKind
+
+/**
+ * Report an error on composable function references.
+ *
+ * `FirFunctionTypeKindExtension` has very limited support for custom function references and
+ * basically requires implementations to distinguish between reflective and non-reflective
+ * function types. Since there are no reflective composable function types we cannot support
+ * composable function references yet.
+ */
+object ComposableCallableReferenceChecker : FirCallableReferenceAccessChecker() {
+    override fun check(
+        expression: FirCallableReferenceAccess,
+        context: CheckerContext,
+        reporter: DiagnosticReporter
+    ) {
+        // The type of a function reference depends on the context where it is used.
+        // We could allow non-reflective composable function references, but this would be fragile
+        // and depend on details of the frontend resolution.
+        val kind = expression.typeRef.coneType.functionTypeKind(context.session)
+        if (kind == ComposableFunction || kind == KComposableFunction) {
+            reporter.reportOn(
+                expression.source,
+                ComposeErrors.COMPOSABLE_FUNCTION_REFERENCE,
+                context
+            )
+        }
+    }
+}
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k2/ComposableFunctionChecker.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k2/ComposableFunctionChecker.kt
new file mode 100644
index 0000000..677daec
--- /dev/null
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k2/ComposableFunctionChecker.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.compiler.plugins.kotlin.k2
+
+import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
+import org.jetbrains.kotlin.diagnostics.reportOn
+import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
+import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirFunctionChecker
+import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
+import org.jetbrains.kotlin.fir.declarations.FirFunction
+import org.jetbrains.kotlin.fir.declarations.getSingleCompatibleExpectForActualOrNull
+import org.jetbrains.kotlin.fir.declarations.utils.isAbstract
+import org.jetbrains.kotlin.fir.declarations.utils.isOperator
+import org.jetbrains.kotlin.fir.declarations.utils.isSuspend
+import org.jetbrains.kotlin.fir.declarations.utils.nameOrSpecialName
+import org.jetbrains.kotlin.util.OperatorNameConventions
+
+object ComposableFunctionChecker : FirFunctionChecker() {
+    override fun check(
+        declaration: FirFunction,
+        context: CheckerContext,
+        reporter: DiagnosticReporter
+    ) {
+        val isComposable = declaration.hasComposableAnnotation(context.session)
+
+        // Check overrides for mismatched composable annotations
+        for (override in declaration.getDirectOverriddenFunctions(context)) {
+            if (override.isComposable(context.session) != isComposable) {
+                reporter.reportOn(
+                    declaration.source,
+                    FirErrors.CONFLICTING_OVERLOADS,
+                    listOf(declaration.symbol, override),
+                    context
+                )
+            }
+
+            // TODO(b/282135108): Check scheme of override against declaration
+        }
+
+        // Check that `actual` composable declarations have composable expects
+        declaration.symbol.getSingleCompatibleExpectForActualOrNull()?.let { expectDeclaration ->
+            if (expectDeclaration.hasComposableAnnotation(context.session) != isComposable) {
+                reporter.reportOn(
+                    declaration.source,
+                    ComposeErrors.MISMATCHED_COMPOSABLE_IN_EXPECT_ACTUAL,
+                    context
+                )
+            }
+        }
+
+        if (!isComposable) return
+
+        // Composable suspend functions are unsupported
+        if (declaration.isSuspend) {
+            reporter.reportOn(declaration.source, ComposeErrors.COMPOSABLE_SUSPEND_FUN, context)
+        }
+
+        // Check that there are no default arguments in abstract composable functions
+        if (declaration.isAbstract) {
+            for (valueParameter in declaration.valueParameters) {
+                val defaultValue = valueParameter.defaultValue ?: continue
+                reporter.reportOn(
+                    defaultValue.source,
+                    ComposeErrors.ABSTRACT_COMPOSABLE_DEFAULT_PARAMETER_VALUE,
+                    context
+                )
+            }
+        }
+
+        // Composable main functions are not allowed.
+        if (declaration.symbol.isMain(context.session)) {
+            reporter.reportOn(declaration.source, ComposeErrors.COMPOSABLE_FUN_MAIN, context)
+        }
+
+        // Disallow composable setValue operators
+        if (declaration.isOperator &&
+            declaration.nameOrSpecialName == OperatorNameConventions.SET_VALUE
+        ) {
+            reporter.reportOn(declaration.source, ComposeErrors.COMPOSE_INVALID_DELEGATE, context)
+        }
+    }
+}
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k2/ComposablePropertyChecker.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k2/ComposablePropertyChecker.kt
new file mode 100644
index 0000000..eb11c28
--- /dev/null
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k2/ComposablePropertyChecker.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.compiler.plugins.kotlin.k2
+
+import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
+import org.jetbrains.kotlin.diagnostics.reportOn
+import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
+import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirPropertyChecker
+import org.jetbrains.kotlin.fir.declarations.FirProperty
+import org.jetbrains.kotlin.fir.declarations.utils.hasBackingField
+
+object ComposablePropertyChecker : FirPropertyChecker() {
+    override fun check(
+        declaration: FirProperty,
+        context: CheckerContext,
+        reporter: DiagnosticReporter
+    ) {
+        // `@Composable` is only applicable to property getters, but in K1 we were also checking
+        // properties with the annotation on the setter.
+        if (declaration.getter?.hasComposableAnnotation(context.session) != true &&
+            declaration.setter?.hasComposableAnnotation(context.session) != true) {
+            return
+        }
+
+        if (declaration.isVar) {
+            reporter.reportOn(declaration.source, ComposeErrors.COMPOSABLE_VAR, context)
+        }
+
+        if (declaration.hasBackingField) {
+            reporter.reportOn(
+                declaration.source,
+                ComposeErrors.COMPOSABLE_PROPERTY_BACKING_FIELD,
+                context
+            )
+        }
+    }
+}
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k2/ComposeErrorMessages.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k2/ComposeErrorMessages.kt
new file mode 100644
index 0000000..11081af
--- /dev/null
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k2/ComposeErrorMessages.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.compiler.plugins.kotlin.k2
+
+import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactoryToRendererMap
+import org.jetbrains.kotlin.diagnostics.rendering.BaseDiagnosticRendererFactory
+import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers
+
+object ComposeErrorMessages : BaseDiagnosticRendererFactory() {
+    override val MAP = KtDiagnosticFactoryToRendererMap("Compose").also { map ->
+        map.put(
+            ComposeErrors.COMPOSABLE_INVOCATION,
+            "@Composable invocations can only happen from the context of a @Composable function"
+        )
+
+        map.put(
+            ComposeErrors.COMPOSABLE_EXPECTED,
+            "Functions which invoke @Composable functions must be marked with the @Composable " +
+                "annotation"
+        )
+
+        map.put(
+            ComposeErrors.NONREADONLY_CALL_IN_READONLY_COMPOSABLE,
+            "Composables marked with @ReadOnlyComposable can only call other @ReadOnlyComposable " +
+                "composables"
+        )
+
+        map.put(
+            ComposeErrors.CAPTURED_COMPOSABLE_INVOCATION,
+            "Composable calls are not allowed inside the {0} parameter of {1}",
+            FirDiagnosticRenderers.VARIABLE_NAME,
+            FirDiagnosticRenderers.DECLARATION_NAME
+        )
+
+        map.put(
+            ComposeErrors.ILLEGAL_TRY_CATCH_AROUND_COMPOSABLE,
+            "Try catch is not supported around composable function invocations."
+        )
+
+        map.put(
+            ComposeErrors.MISSING_DISALLOW_COMPOSABLE_CALLS_ANNOTATION,
+            "Parameter {0} cannot be inlined inside of lambda argument {1} of {2} " +
+                "without also being annotated with @DisallowComposableCalls",
+            FirDiagnosticRenderers.VARIABLE_NAME,
+            FirDiagnosticRenderers.VARIABLE_NAME,
+            FirDiagnosticRenderers.DECLARATION_NAME,
+        )
+
+        map.put(
+            ComposeErrors.ABSTRACT_COMPOSABLE_DEFAULT_PARAMETER_VALUE,
+            "Abstract Composable functions cannot have parameters with default values"
+        )
+
+        map.put(
+            ComposeErrors.COMPOSABLE_SUSPEND_FUN,
+            "Composable function cannot be annotated as suspend"
+        )
+
+        map.put(
+            ComposeErrors.COMPOSABLE_FUN_MAIN,
+            "Composable main functions are not currently supported"
+        )
+
+        map.put(
+            ComposeErrors.COMPOSABLE_FUNCTION_REFERENCE,
+            "Function References of @Composable functions are not currently supported"
+        )
+
+        map.put(
+            ComposeErrors.COMPOSABLE_PROPERTY_BACKING_FIELD,
+            "Composable properties are not able to have backing fields"
+        )
+
+        map.put(
+            ComposeErrors.COMPOSABLE_VAR,
+            "Composable properties are not able to have backing fields"
+        )
+
+        map.put(
+            ComposeErrors.COMPOSE_INVALID_DELEGATE,
+            "Composable setValue operator is not currently supported."
+        )
+
+        map.put(
+            ComposeErrors.MISMATCHED_COMPOSABLE_IN_EXPECT_ACTUAL,
+            "Mismatched @Composable annotation between expect and actual declaration"
+        )
+    }
+}
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k2/ComposeErrors.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k2/ComposeErrors.kt
new file mode 100644
index 0000000..2c2ceaa
--- /dev/null
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k2/ComposeErrors.kt
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.compiler.plugins.kotlin.k2
+
+import com.intellij.lang.LighterASTNode
+import com.intellij.openapi.util.TextRange
+import com.intellij.psi.PsiElement
+import com.intellij.util.diff.FlyweightCapableTreeStructure
+import org.jetbrains.kotlin.diagnostics.LightTreePositioningStrategies
+import org.jetbrains.kotlin.diagnostics.LightTreePositioningStrategy
+import org.jetbrains.kotlin.diagnostics.PositioningStrategies
+import org.jetbrains.kotlin.diagnostics.PositioningStrategy
+import org.jetbrains.kotlin.diagnostics.SourceElementPositioningStrategies
+import org.jetbrains.kotlin.diagnostics.SourceElementPositioningStrategy
+import org.jetbrains.kotlin.diagnostics.error0
+import org.jetbrains.kotlin.diagnostics.error2
+import org.jetbrains.kotlin.diagnostics.error3
+import org.jetbrains.kotlin.diagnostics.findChildByType
+import org.jetbrains.kotlin.diagnostics.markElement
+import org.jetbrains.kotlin.diagnostics.rendering.RootDiagnosticRendererFactory
+import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
+import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.psi.KtNamedDeclaration
+import org.jetbrains.kotlin.psi.KtTryExpression
+
+object ComposeErrors {
+    // error goes on the composable call in a non-composable function
+    val COMPOSABLE_INVOCATION by error0<PsiElement>()
+
+    // error goes on the non-composable function with composable calls
+    val COMPOSABLE_EXPECTED by error0<PsiElement>(
+        SourceElementPositioningStrategies.DECLARATION_NAME
+    )
+
+    val NONREADONLY_CALL_IN_READONLY_COMPOSABLE by error0<PsiElement>()
+
+    val CAPTURED_COMPOSABLE_INVOCATION by
+        error2<PsiElement, FirVariableSymbol<*>, FirCallableSymbol<*>>()
+
+    // composable calls are not allowed in try expressions
+    // error goes on the `try` keyword
+    val ILLEGAL_TRY_CATCH_AROUND_COMPOSABLE by error0<KtTryExpression>(
+        ComposeSourceElementPositioningStrategies.TRY_KEYWORD
+    )
+
+    val MISSING_DISALLOW_COMPOSABLE_CALLS_ANNOTATION by error3<
+        PsiElement,
+        FirValueParameterSymbol, // unmarked
+        FirValueParameterSymbol, // marked
+        FirCallableSymbol<*>>()
+
+    val ABSTRACT_COMPOSABLE_DEFAULT_PARAMETER_VALUE by error0<PsiElement>()
+
+    val COMPOSABLE_SUSPEND_FUN by error0<PsiElement>(
+        SourceElementPositioningStrategies.DECLARATION_NAME
+    )
+
+    val COMPOSABLE_FUN_MAIN by error0<PsiElement>(
+        SourceElementPositioningStrategies.DECLARATION_NAME
+    )
+
+    val COMPOSABLE_FUNCTION_REFERENCE by error0<PsiElement>()
+
+    val COMPOSABLE_PROPERTY_BACKING_FIELD by error0<PsiElement>(
+        SourceElementPositioningStrategies.DECLARATION_NAME
+    )
+
+    val COMPOSABLE_VAR by error0<PsiElement>(SourceElementPositioningStrategies.DECLARATION_NAME)
+
+    val COMPOSE_INVALID_DELEGATE by error0<PsiElement>(
+        ComposeSourceElementPositioningStrategies.DECLARATION_NAME_OR_DEFAULT
+    )
+
+    val MISMATCHED_COMPOSABLE_IN_EXPECT_ACTUAL by error0<PsiElement>(
+        SourceElementPositioningStrategies.DECLARATION_NAME
+    )
+
+    init {
+        RootDiagnosticRendererFactory.registerFactory(ComposeErrorMessages)
+    }
+}
+
+object ComposeSourceElementPositioningStrategies {
+    private val PSI_TRY_KEYWORD: PositioningStrategy<KtTryExpression> =
+        object : PositioningStrategy<KtTryExpression>() {
+            override fun mark(element: KtTryExpression): List<TextRange> {
+                element.tryKeyword?.let {
+                    return markElement(it)
+                }
+                return PositioningStrategies.DEFAULT.mark(element)
+            }
+    }
+
+    private val LIGHT_TREE_TRY_KEYWORD: LightTreePositioningStrategy =
+        object : LightTreePositioningStrategy() {
+        override fun mark(
+            node: LighterASTNode,
+            startOffset: Int,
+            endOffset: Int,
+            tree: FlyweightCapableTreeStructure<LighterASTNode>
+        ): List<TextRange> {
+            val target = tree.findChildByType(node, KtTokens.TRY_KEYWORD) ?: node
+            return markElement(target, startOffset, endOffset, tree, node)
+        }
+    }
+
+    private val PSI_DECLARATION_NAME_OR_DEFAULT: PositioningStrategy<PsiElement> =
+        object : PositioningStrategy<PsiElement>() {
+            override fun mark(element: PsiElement): List<TextRange> {
+                if (element is KtNamedDeclaration) {
+                    return PositioningStrategies.DECLARATION_NAME.mark(element)
+                }
+                return PositioningStrategies.DEFAULT.mark(element)
+            }
+        }
+
+    val TRY_KEYWORD = SourceElementPositioningStrategy(
+        LIGHT_TREE_TRY_KEYWORD,
+        PSI_TRY_KEYWORD
+    )
+
+    val DECLARATION_NAME_OR_DEFAULT = SourceElementPositioningStrategy(
+        LightTreePositioningStrategies.DECLARATION_NAME,
+        PSI_DECLARATION_NAME_OR_DEFAULT
+    )
+}
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k2/ComposeFirExtensions.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k2/ComposeFirExtensions.kt
new file mode 100644
index 0000000..a3bb7764
--- /dev/null
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k2/ComposeFirExtensions.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.compiler.plugins.kotlin.k2
+
+import androidx.compose.compiler.plugins.kotlin.ComposeClassIds
+import org.jetbrains.kotlin.builtins.functions.FunctionTypeKind
+import org.jetbrains.kotlin.fir.FirSession
+import org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationCheckers
+import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirFunctionChecker
+import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirPropertyChecker
+import org.jetbrains.kotlin.fir.analysis.checkers.expression.ExpressionCheckers
+import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirCallableReferenceAccessChecker
+import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirFunctionCallChecker
+import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirPropertyAccessExpressionChecker
+import org.jetbrains.kotlin.fir.analysis.extensions.FirAdditionalCheckersExtension
+import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar
+import org.jetbrains.kotlin.fir.extensions.FirFunctionTypeKindExtension
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.name.Name
+
+class ComposeFirExtensionRegistrar : FirExtensionRegistrar() {
+    override fun ExtensionRegistrarContext.configurePlugin() {
+        +::ComposableFunctionTypeKindExtension
+        +::ComposeFirCheckersExtension
+    }
+}
+
+class ComposableFunctionTypeKindExtension(
+    session: FirSession
+) : FirFunctionTypeKindExtension(session) {
+    override fun FunctionTypeKindRegistrar.registerKinds() {
+        registerKind(ComposableFunction, KComposableFunction)
+    }
+}
+
+object ComposableFunction : FunctionTypeKind(
+    FqName.topLevel(Name.identifier("androidx.compose.runtime.internal")),
+    "ComposableFunction",
+    ComposeClassIds.Composable,
+    isReflectType = false
+) {
+    override val prefixForTypeRender: String
+        get() = "@Composable"
+
+    override fun reflectKind(): FunctionTypeKind = KComposableFunction
+}
+
+object KComposableFunction : FunctionTypeKind(
+    FqName.topLevel(Name.identifier("androidx.compose.runtime.internal")),
+    "KComposableFunction",
+    ComposeClassIds.Composable,
+    isReflectType = true
+) {
+    override fun nonReflectKind(): FunctionTypeKind = ComposableFunction
+}
+
+class ComposeFirCheckersExtension(session: FirSession) : FirAdditionalCheckersExtension(session) {
+    override val declarationCheckers: DeclarationCheckers = object : DeclarationCheckers() {
+        override val functionCheckers: Set<FirFunctionChecker> =
+            setOf(ComposableFunctionChecker)
+
+        override val propertyCheckers: Set<FirPropertyChecker> =
+            setOf(ComposablePropertyChecker)
+    }
+
+    override val expressionCheckers: ExpressionCheckers = object : ExpressionCheckers() {
+        override val functionCallCheckers: Set<FirFunctionCallChecker> =
+            setOf(ComposableFunctionCallChecker)
+
+        override val propertyAccessExpressionCheckers: Set<FirPropertyAccessExpressionChecker> =
+            setOf(ComposablePropertyAccessExpressionChecker)
+
+        override val callableReferenceAccessCheckers: Set<FirCallableReferenceAccessChecker> =
+            setOf(ComposableCallableReferenceChecker)
+    }
+}
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k2/FirUtils.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k2/FirUtils.kt
new file mode 100644
index 0000000..a844125
--- /dev/null
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/k2/FirUtils.kt
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.compiler.plugins.kotlin.k2
+
+import androidx.compose.compiler.plugins.kotlin.ComposeClassIds
+import org.jetbrains.kotlin.fir.FirAnnotationContainer
+import org.jetbrains.kotlin.fir.FirSession
+import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
+import org.jetbrains.kotlin.fir.analysis.checkers.getAnnotationStringParameter
+import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
+import org.jetbrains.kotlin.fir.containingClassLookupTag
+import org.jetbrains.kotlin.fir.declarations.FirFunction
+import org.jetbrains.kotlin.fir.declarations.FirPropertyAccessor
+import org.jetbrains.kotlin.fir.declarations.hasAnnotation
+import org.jetbrains.kotlin.fir.declarations.utils.isOverride
+import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
+import org.jetbrains.kotlin.fir.expressions.FirReturnExpression
+import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol
+import org.jetbrains.kotlin.fir.resolve.toSymbol
+import org.jetbrains.kotlin.fir.scopes.getDirectOverriddenFunctions
+import org.jetbrains.kotlin.fir.scopes.getDirectOverriddenProperties
+import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
+import org.jetbrains.kotlin.fir.symbols.SymbolInternals
+import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirPropertyAccessorSymbol
+import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
+import org.jetbrains.kotlin.fir.types.ConeKotlinType
+import org.jetbrains.kotlin.fir.types.ProjectionKind
+import org.jetbrains.kotlin.fir.types.coneType
+import org.jetbrains.kotlin.fir.types.isArrayType
+import org.jetbrains.kotlin.fir.types.isString
+import org.jetbrains.kotlin.fir.types.isUnit
+import org.jetbrains.kotlin.fir.types.type
+import org.jetbrains.kotlin.name.StandardClassIds
+
+fun FirAnnotationContainer.hasComposableAnnotation(session: FirSession): Boolean =
+    hasAnnotation(ComposeClassIds.Composable, session)
+
+fun FirBasedSymbol<*>.hasComposableAnnotation(session: FirSession): Boolean =
+    hasAnnotation(ComposeClassIds.Composable, session)
+
+fun FirAnnotationContainer.hasReadOnlyComposableAnnotation(session: FirSession): Boolean =
+    hasAnnotation(ComposeClassIds.ReadOnlyComposable, session)
+
+fun FirBasedSymbol<*>.hasReadOnlyComposableAnnotation(session: FirSession): Boolean =
+    hasAnnotation(ComposeClassIds.ReadOnlyComposable, session)
+
+fun FirAnnotationContainer.hasDisallowComposableCallsAnnotation(session: FirSession): Boolean =
+    hasAnnotation(ComposeClassIds.DisallowComposableCalls, session)
+
+fun FirCallableSymbol<*>.isComposable(session: FirSession): Boolean =
+    when (this) {
+        is FirFunctionSymbol<*> ->
+            hasComposableAnnotation(session)
+        is FirPropertySymbol ->
+            getterSymbol?.let {
+                it.hasComposableAnnotation(session) || it.isComposableDelegate(session)
+            } ?: false
+        else -> false
+    }
+
+fun FirCallableSymbol<*>.isReadOnlyComposable(session: FirSession): Boolean =
+    when (this) {
+        is FirFunctionSymbol<*> ->
+            hasReadOnlyComposableAnnotation(session)
+        is FirPropertySymbol ->
+            getterSymbol?.hasReadOnlyComposableAnnotation(session) ?: false
+        else -> false
+    }
+
+@OptIn(SymbolInternals::class)
+private fun FirPropertyAccessorSymbol.isComposableDelegate(session: FirSession): Boolean {
+    if (!propertySymbol.hasDelegate) return false
+    return ((fir
+        .body
+        ?.statements
+        ?.singleOrNull() as? FirReturnExpression)
+        ?.result as? FirFunctionCall)
+        ?.calleeReference
+        ?.toResolvedCallableSymbol()
+        ?.isComposable(session)
+        ?: false
+}
+
+fun FirFunction.getDirectOverriddenFunctions(
+    context: CheckerContext
+): List<FirFunctionSymbol<*>> {
+    if (!isOverride && (this as? FirPropertyAccessor)?.propertySymbol?.isOverride != true)
+        return listOf()
+
+    val scope = (containingClassLookupTag()
+        ?.toSymbol(context.session) as? FirClassSymbol<*>)
+        ?.unsubstitutedScope(context)
+        ?: return listOf()
+
+    return when (val symbol = symbol) {
+        is FirNamedFunctionSymbol -> {
+            scope.processFunctionsByName(symbol.name) {}
+            scope.getDirectOverriddenFunctions(symbol, true)
+        }
+        is FirPropertyAccessorSymbol -> {
+            scope.getDirectOverriddenProperties(symbol.propertySymbol, true).mapNotNull {
+                if (symbol.isGetter) it.getterSymbol else it.setterSymbol
+            }
+        }
+        else -> listOf()
+    }
+}
+
+// TODO: Replace this with the FIR MainFunctionDetector once it lands upstream!
+fun FirFunctionSymbol<*>.isMain(session: FirSession): Boolean {
+    if (this !is FirNamedFunctionSymbol) return false
+    if (typeParameterSymbols.isNotEmpty()) return false
+    if (!resolvedReturnType.isUnit) return false
+    if (jvmNameAsString(session) != "main") return false
+
+    val parameterTypes = explicitParameterTypes
+    when (parameterTypes.size) {
+        0 -> {
+            /*
+            assert(DescriptorUtils.isTopLevelDeclaration(descriptor)) { "main without parameters works only for top-level" }
+            val containingFile = DescriptorToSourceUtils.getContainingFile(descriptor)
+            // We do not support parameterless entry points having JvmName("name") but different real names
+            // See more at https://github.com/Kotlin/KEEP/blob/master/proposals/enhancing-main-convention.md#parameterless-main
+            if (descriptor.name.asString() != "main") return false
+            if (containingFile?.declarations?.any { declaration -> isMainWithParameter(declaration, checkJvmStaticAnnotation) } == true) {
+                return false
+            }*/
+        }
+        1 -> {
+            val type = parameterTypes.single()
+            if (!type.isArrayType || type.typeArguments.size != 1) return false
+            val elementType = type.typeArguments[0].takeIf { it.kind != ProjectionKind.IN }?.type
+                ?: return false
+            if (!elementType.isString) return false
+        }
+        else -> return false
+    }
+    /*
+    if (DescriptorUtils.isTopLevelDeclaration(descriptor)) return true
+
+    val containingDeclaration = descriptor.containingDeclaration
+    return containingDeclaration is ClassDescriptor
+            && containingDeclaration.kind.isSingleton
+            && (descriptor.hasJvmStaticAnnotation() || !checkJvmStaticAnnotation)
+     */
+    return true
+}
+
+private fun FirNamedFunctionSymbol.jvmNameAsString(session: FirSession): String =
+    getAnnotationStringParameter(StandardClassIds.Annotations.JvmName, session)
+        ?: name.asString()
+
+private val FirFunctionSymbol<*>.explicitParameterTypes: List<ConeKotlinType>
+    get() = resolvedContextReceivers.map { it.typeRef.coneType } +
+        listOfNotNull(receiverParameter?.typeRef?.coneType) +
+        valueParameterSymbols.map { it.resolvedReturnType }
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt
index 8da7e7c..066e0d3 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/AbstractComposeLowering.kt
@@ -64,11 +64,13 @@
 import org.jetbrains.kotlin.ir.expressions.IrExpression
 import org.jetbrains.kotlin.ir.expressions.IrFunctionExpression
 import org.jetbrains.kotlin.ir.expressions.IrGetEnumValue
+import org.jetbrains.kotlin.ir.expressions.IrGetField
 import org.jetbrains.kotlin.ir.expressions.IrGetObjectValue
 import org.jetbrains.kotlin.ir.expressions.IrGetValue
 import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
 import org.jetbrains.kotlin.ir.expressions.IrReturn
 import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
+import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall
 import org.jetbrains.kotlin.ir.expressions.IrVararg
 import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl
 import org.jetbrains.kotlin.ir.expressions.impl.IrBranchImpl
@@ -293,7 +295,9 @@
             return true
         val function = symbol.owner
         return function.name == OperatorNameConventions.INVOKE &&
-            function.parentClassOrNull?.defaultType?.isFunction() == true
+            function.parentClassOrNull?.defaultType?.let {
+                it.isFunction() || it.isSyntheticComposableFunction()
+            } ?: false
     }
 
     fun IrCall.isComposableCall(): Boolean {
@@ -311,7 +315,9 @@
         // `Function3<T1, Composer, Int, T2>`. After this lowering runs we have to check the
         // `attributeOwnerId` to recover the original type.
         val receiver = dispatchReceiver?.let { it.attributeOwnerId as? IrExpression ?: it }
-        return receiver?.type?.hasComposableAnnotation() == true
+        return receiver?.type?.let {
+            it.hasComposableAnnotation() || it.isSyntheticComposableFunction()
+        } ?: false
     }
 
     fun IrCall.isComposableSingletonGetter(): Boolean {
@@ -881,8 +887,12 @@
                     else -> false
                 }
             }
-            is IrFunctionExpression ->
+            is IrFunctionExpression,
+            is IrTypeOperatorCall ->
                 context.irTrace[ComposeWritableSlices.IS_STATIC_FUNCTION_EXPRESSION, this] ?: false
+            is IrGetField ->
+                // K2 sometimes produces `IrGetField` for reads from constant properties
+                symbol.owner.correspondingPropertySymbol?.owner?.isConst == true
             else -> false
         }
     }
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunInterfaceLowering.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunInterfaceLowering.kt
index cb8f689..9bacca8 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunInterfaceLowering.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunInterfaceLowering.kt
@@ -16,15 +16,20 @@
 
 package androidx.compose.compiler.plugins.kotlin.lower
 
+import androidx.compose.compiler.plugins.kotlin.hasComposableAnnotation
 import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
 import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
+import org.jetbrains.kotlin.descriptors.Modality
 import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
 import org.jetbrains.kotlin.ir.expressions.IrExpression
 import org.jetbrains.kotlin.ir.expressions.IrFunctionExpression
-import org.jetbrains.kotlin.ir.expressions.IrTypeOperator
+import org.jetbrains.kotlin.ir.expressions.IrTypeOperator.IMPLICIT_CAST
+import org.jetbrains.kotlin.ir.expressions.IrTypeOperator.SAM_CONVERSION
 import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall
+import org.jetbrains.kotlin.ir.types.IrType
 import org.jetbrains.kotlin.ir.types.IrTypeSystemContextImpl
 import org.jetbrains.kotlin.ir.types.classOrNull
+import org.jetbrains.kotlin.ir.util.functions
 import org.jetbrains.kotlin.ir.util.isLambda
 import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
 import org.jetbrains.kotlin.platform.jvm.isJvm
@@ -40,32 +45,10 @@
         }
     }
 
-    private fun isFunInterfaceConversion(expression: IrTypeOperatorCall): Boolean {
-        val argument = expression.argument
-        val operator = expression.operator
-        val type = expression.typeOperand
-        val functionClass = type.classOrNull
-        return operator == IrTypeOperator.SAM_CONVERSION &&
-            argument is IrFunctionExpression &&
-            argument.origin.isLambda &&
-            functionClass != null &&
-            functionClass.owner.isFun
-        // IMPORTANT(b/178663739):
-        // We are transforming not just SAM conversions for composable fun interfaces, but ALL
-        // fun interfaces temporarily until KT-44622 gets fixed in the version of kotlin we
-        // are using, which should be in 1.4.30.
-        // Once it does, we should either add the below additional condition to this predicate,
-        // or, if possible, remove this lowering all together if kotlin's lowering works for
-        // composable fun interfaces as well.
-        //
-        // functionClass.functions.single {
-        //    it.owner.modality == Modality.ABSTRACT
-        // }.owner.annotations.hasAnnotation(ComposeFqNames.Composable)
-    }
-
     override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression {
-        if (isFunInterfaceConversion(expression)) {
-            val argument = expression.argument.transform(this, null) as IrFunctionExpression
+        val functionExpr = expression.findSamFunctionExpr()
+        if (functionExpr != null && expression.typeOperand.isComposableFunInterface()) {
+            val argument = functionExpr.transform(this, null) as IrFunctionExpression
             val superType = expression.typeOperand
             val superClass = superType.classOrNull ?: error("Expected non-null class")
             return FunctionReferenceBuilder(
@@ -81,3 +64,38 @@
         return super.visitTypeOperator(expression)
     }
 }
+
+private fun IrType.isComposableFunInterface(): Boolean =
+    classOrNull
+        ?.functions
+        ?.single { it.owner.modality == Modality.ABSTRACT }
+        ?.owner
+        ?.hasComposableAnnotation() == true
+
+internal fun IrTypeOperatorCall.findSamFunctionExpr(): IrFunctionExpression? {
+    val argument = argument
+    val operator = operator
+    val type = typeOperand
+    val functionClass = type.classOrNull
+
+    val isFunInterfaceConversion = operator == SAM_CONVERSION &&
+        functionClass != null &&
+        functionClass.owner.isFun
+
+    return if (isFunInterfaceConversion) {
+        // if you modify this logic, make sure to update wrapping of type operators
+        // in ComposerLambdaMemoization.kt
+        when {
+            argument is IrFunctionExpression && argument.origin.isLambda -> argument
+            // some expressions are wrapped with additional implicit cast operator
+            // unwrapping that allows to avoid SAM conversion that capture FunctionN and box params.
+            argument is IrTypeOperatorCall && argument.operator == IMPLICIT_CAST -> {
+                val functionExpr = argument.argument
+                functionExpr as? IrFunctionExpression
+            }
+            else -> null
+        }
+    } else {
+        null
+    }
+}
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
index 53d1808..e334859 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
@@ -929,9 +929,16 @@
                 returnVar?.let { irReturn(declaration.symbol, irGet(it)) }
             )
         )
-        if (elideGroups && !hasExplicitGroups && collectSourceInformation) {
+        if (elideGroups && !hasExplicitGroups) {
             scope.realizeEndCalls {
-                irSourceInformationMarkerEnd(body, scope)
+                irComposite(
+                    statements = listOfNotNull(
+                        if (emitTraceMarkers) irTraceEventEnd() else null,
+                        if (collectSourceInformation)
+                            irSourceInformationMarkerEnd(body, scope)
+                        else null
+                    )
+                )
             }
         }
 
@@ -2031,7 +2038,10 @@
 
             val name = declaration.kotlinFqName
             val file = declaration.file.name
-            val line = declaration.file.fileEntry.getLineNumber(declaration.startOffset)
+            // FIXME: This should probably use `declaration.startOffset`, but the K2 implementation
+            //        is unfinished (i.e., in K2 the start offset of an annotated function could
+            //        point at the annotation instead of the start of the function).
+            val line = declaration.file.fileEntry.getLineNumber(startOffset)
             val traceInfo = "$name ($file:$line)" // TODO(174715171) decide on what to log
             val dirty = scope.dirty
             val changed = scope.changedParameter
@@ -2680,6 +2690,9 @@
                     }
                 }
             }
+            arg is IrVararg -> {
+                meta.stability = stabilityOf(arg.varargElementType)
+            }
         }
     }
 
@@ -3855,16 +3868,16 @@
                 for (param in function.valueParameters) {
                     val paramName = param.name.asString()
                     when {
-                        paramName.startsWith("_context_receiver_") -> Unit
                         paramName == KtxNameConventions.COMPOSER_PARAMETER.identifier ->
                             composerParameter = param
                         paramName.startsWith(KtxNameConventions.DEFAULT_PARAMETER.identifier) ->
                             defaultParams += param
                         paramName.startsWith(KtxNameConventions.CHANGED_PARAMETER.identifier) ->
                             changedParams += param
-                        paramName.startsWith("\$anonymous\$parameter") -> Unit
-                        paramName.startsWith("\$name\$for\$destructuring") -> Unit
-                        paramName.startsWith("\$noName_") -> Unit
+                        paramName.startsWith("\$context_receiver_") ||
+                        paramName.startsWith("\$anonymous\$parameter") ||
+                        paramName.startsWith("\$name\$for\$destructuring") ||
+                        paramName.startsWith("\$noName_") ||
                         paramName == "\$this" -> Unit
                         else -> realValueParamCount++
                     }
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableLambdaAnnotator.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableLambdaAnnotator.kt
new file mode 100644
index 0000000..f05b363
--- /dev/null
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableLambdaAnnotator.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.compiler.plugins.kotlin.lower
+
+import androidx.compose.compiler.plugins.kotlin.ComposeClassIds
+import androidx.compose.compiler.plugins.kotlin.hasComposableAnnotation
+import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
+import org.jetbrains.kotlin.ir.IrElement
+import org.jetbrains.kotlin.ir.declarations.IrFunction
+import org.jetbrains.kotlin.ir.expressions.IrFunctionExpression
+import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
+import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
+import org.jetbrains.kotlin.ir.util.constructors
+import org.jetbrains.kotlin.ir.util.defaultType
+import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
+import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
+
+/**
+ * In K1, the frontend used to annotate inferred composable lambdas with `@Composable`.
+ * The K2 frontend instead uses a different type for composable lambdas. This pass adds
+ * the annotation, since the backend expects it.
+ */
+class ComposableLambdaAnnotator(context: IrPluginContext) : IrElementVisitorVoid {
+    override fun visitElement(element: IrElement) {
+        element.acceptChildrenVoid(this)
+    }
+
+    override fun visitFunctionExpression(expression: IrFunctionExpression) {
+        if (expression.type.isSyntheticComposableFunction()) {
+            expression.function.mark()
+        }
+        super.visitFunctionExpression(expression)
+    }
+
+    override fun visitFunctionReference(expression: IrFunctionReference) {
+        if (expression.type.isSyntheticComposableFunction()) {
+            expression.symbol.owner.mark()
+        }
+        super.visitFunctionReference(expression)
+    }
+
+    private val composableSymbol = context.referenceClass(ComposeClassIds.Composable)!!
+
+    private fun IrFunction.mark() {
+        if (!hasComposableAnnotation()) {
+            annotations = annotations + IrConstructorCallImpl.fromSymbolOwner(
+                composableSymbol.owner.defaultType,
+                composableSymbol.constructors.single(),
+            )
+        }
+    }
+}
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableTypeRemapper.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableTypeRemapper.kt
index 83a8184..3908999 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableTypeRemapper.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableTypeRemapper.kt
@@ -17,6 +17,7 @@
 package androidx.compose.compiler.plugins.kotlin.lower
 
 import androidx.compose.compiler.plugins.kotlin.ComposeFqNames
+import androidx.compose.compiler.plugins.kotlin.hasComposableAnnotation
 import androidx.compose.compiler.plugins.kotlin.lower.decoys.isDecoy
 import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
 import org.jetbrains.kotlin.backend.common.extensions.IrPluginContextImpl
@@ -49,7 +50,6 @@
 import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
 import org.jetbrains.kotlin.ir.types.impl.IrTypeAbbreviationImpl
 import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
-import org.jetbrains.kotlin.ir.types.isClassWithFqName
 import org.jetbrains.kotlin.ir.types.typeOrNull
 import org.jetbrains.kotlin.ir.util.DeepCopySymbolRemapper
 import org.jetbrains.kotlin.ir.util.SymbolRemapper
@@ -63,7 +63,6 @@
 import org.jetbrains.kotlin.ir.util.parentClassOrNull
 import org.jetbrains.kotlin.ir.util.patchDeclarationParents
 import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
-import org.jetbrains.kotlin.name.FqName
 import org.jetbrains.kotlin.types.Variance
 
 internal class DeepCopyIrTreeWithRemappedComposableTypes(
@@ -369,16 +368,6 @@
         scopeStack.pop()
     }
 
-    private fun IrType.isComposable(): Boolean {
-        return annotations.hasAnnotation(ComposeFqNames.Composable)
-    }
-
-    private val IrConstructorCall.annotationClass
-        get() = this.symbol.owner.returnType.classifierOrNull
-
-    private fun List<IrConstructorCall>.hasAnnotation(fqName: FqName): Boolean =
-        any { it.annotationClass?.isClassWithFqName(fqName.toUnsafe()) ?: false }
-
     @OptIn(ObsoleteDescriptorBasedAPI::class)
     private fun IrType.isFunction(): Boolean {
         val classifier = classifierOrNull ?: return false
@@ -388,10 +377,13 @@
         return true
     }
 
+    private fun IrType.isComposableFunction(): Boolean {
+        return isSyntheticComposableFunction() || (isFunction() && hasComposableAnnotation())
+    }
+
     override fun remapType(type: IrType): IrType {
         if (type !is IrSimpleType) return type
-        if (!type.isFunction()) return underlyingRemapType(type)
-        if (!type.isComposable()) return underlyingRemapType(type)
+        if (!type.isComposableFunction()) return underlyingRemapType(type)
         // do not convert types for decoys
         if (scopeStack.peek()?.isDecoy() == true) {
             return underlyingRemapType(type)
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerLambdaMemoization.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerLambdaMemoization.kt
index 3a8ba5a..166307c 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerLambdaMemoization.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerLambdaMemoization.kt
@@ -69,11 +69,14 @@
 import org.jetbrains.kotlin.ir.expressions.IrFunctionExpression
 import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
 import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
+import org.jetbrains.kotlin.ir.expressions.IrTypeOperator
+import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall
 import org.jetbrains.kotlin.ir.expressions.IrValueAccessExpression
 import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
 import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
 import org.jetbrains.kotlin.ir.expressions.impl.IrGetObjectValueImpl
 import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
+import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl
 import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
 import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
 import org.jetbrains.kotlin.ir.symbols.IrSymbol
@@ -509,8 +512,70 @@
         return result
     }
 
+    override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression {
+        // SAM conversions are handled by Kotlin compiler
+        // We only need to make sure that remember is handled correctly around type operator
+        if (
+            expression.operator != IrTypeOperator.SAM_CONVERSION ||
+                currentFunctionContext?.canRemember != true
+        ) {
+            return super.visitTypeOperator(expression)
+        }
+
+        // Unwrap function from type operator
+        val originalFunctionExpression =
+            expression.findSamFunctionExpr() ?: return super.visitTypeOperator(expression)
+
+        // Record capture variables for this scope
+        val collector = CaptureCollector()
+        startCollector(collector)
+        // Handle inside of the function expression
+        val result = super.visitFunctionExpression(originalFunctionExpression)
+        stopCollector(collector)
+
+        // If the ancestor converted this then return
+        val newFunctionExpression = result as? IrFunctionExpression ?: return result
+
+        // Construct new type operator call to wrap remember around.
+        val newArgument = when (val argument = expression.argument) {
+            is IrFunctionExpression -> newFunctionExpression
+            is IrTypeOperatorCall -> {
+                require(
+                    argument.operator == IrTypeOperator.IMPLICIT_CAST &&
+                        argument.argument == originalFunctionExpression
+                ) {
+                    "Only implicit cast is supported inside SAM conversion"
+                }
+                IrTypeOperatorCallImpl(
+                    argument.startOffset,
+                    argument.endOffset,
+                    argument.type,
+                    argument.operator,
+                    argument.typeOperand,
+                    newFunctionExpression
+                )
+            }
+            else -> error("Unknown ")
+        }
+
+        val expressionToRemember =
+            IrTypeOperatorCallImpl(
+                expression.startOffset,
+                expression.endOffset,
+                expression.type,
+                IrTypeOperator.SAM_CONVERSION,
+                expression.typeOperand,
+                newArgument
+            )
+        return rememberExpression(
+            currentFunctionContext!!,
+            expressionToRemember,
+            collector.captures.toList()
+        )
+    }
+
     private fun visitNonComposableFunctionExpression(
-        expression: IrFunctionExpression
+        expression: IrFunctionExpression,
     ): IrExpression {
         val functionContext = currentFunctionContext
             ?: return super.visitFunctionExpression(expression)
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerParamTransformer.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerParamTransformer.kt
index fd4a4db..1debcc3 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerParamTransformer.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposerParamTransformer.kt
@@ -192,10 +192,10 @@
                 }
                 symbol.owner.withComposerParamIfNeeded()
             }
-            symbol.owner.hasComposableAnnotation() ->
-                symbol.owner.withComposerParamIfNeeded()
             isComposableLambdaInvoke() ->
                 symbol.owner.lambdaInvokeWithComposerParam()
+            symbol.owner.hasComposableAnnotation() ->
+                symbol.owner.withComposerParamIfNeeded()
             // Not a composable call
             else -> return this
         }
@@ -594,8 +594,9 @@
                     try {
                         // we don't want to pass the composer parameter in to composable calls
                         // inside of nested scopes.... *unless* the scope was inlined.
-                        isNestedScope =
-                            if (declaration.isNonComposableInlinedLambda()) wasNested else true
+                        isNestedScope = wasNested ||
+                            !inlineLambdaInfo.isInlineLambda(declaration) ||
+                            declaration.hasComposableAnnotation()
                         return super.visitFunction(declaration)
                     } finally {
                         isNestedScope = wasNested
@@ -632,9 +633,6 @@
         }
     }
 
-    private fun IrFunction.isNonComposableInlinedLambda(): Boolean =
-        inlineLambdaInfo.isInlineLambda(this) && !hasComposableAnnotation()
-
     /**
      * With klibs, composable functions are always deserialized from IR instead of being restored
      * into stubs.
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/IrInlineReferenceLocator.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/IrInlineReferenceLocator.kt
index 7870f61..3803019 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/IrInlineReferenceLocator.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/IrInlineReferenceLocator.kt
@@ -19,7 +19,6 @@
 
 import androidx.compose.compiler.plugins.kotlin.ComposeFqNames
 import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
-import org.jetbrains.kotlin.backend.jvm.ir.isInlineParameter
 import org.jetbrains.kotlin.ir.IrElement
 import org.jetbrains.kotlin.ir.declarations.IrConstructor
 import org.jetbrains.kotlin.ir.declarations.IrFunction
@@ -31,9 +30,14 @@
 import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
 import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
 import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
+import org.jetbrains.kotlin.ir.types.IrType
+import org.jetbrains.kotlin.ir.types.classFqName
+import org.jetbrains.kotlin.ir.types.isNullable
 import org.jetbrains.kotlin.ir.util.constructedClass
 import org.jetbrains.kotlin.ir.util.hasAnnotation
+import org.jetbrains.kotlin.ir.util.isFunction
 import org.jetbrains.kotlin.ir.util.isLambda
+import org.jetbrains.kotlin.ir.util.isSuspendFunction
 import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
 import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
 import org.jetbrains.kotlin.ir.visitors.acceptVoid
@@ -110,3 +114,18 @@
 private val IrStatementOrigin?.isLambdaBlockOrigin: Boolean
     get() = isLambda || this == IrStatementOrigin.ADAPTED_FUNCTION_REFERENCE ||
         this == IrStatementOrigin.SUSPEND_CONVERSION
+
+// This is copied from JvmIrInlineUtils.kt in the Kotlin compiler, since we
+// need to check for synthetic composable functions.
+private fun IrValueParameter.isInlineParameter(): Boolean =
+    index >= 0 && !isNoinline && (type.isFunction() || type.isSuspendFunction() ||
+        type.isSyntheticComposableFunction()) &&
+        // Parameters with default values are always nullable, so check the expression too.
+        // Note that the frontend has a diagnostic for nullable inline parameters, so actually
+        // making this return `false` requires using `@Suppress`.
+        (!type.isNullable() || defaultValue?.expression?.type?.isNullable() == false)
+
+fun IrType.isSyntheticComposableFunction() =
+    classFqName?.asString()?.startsWith(
+        "androidx.compose.runtime.internal.ComposableFunction"
+    ) == true
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/IrSourcePrinter.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/IrSourcePrinter.kt
index ccf22cf..04c7f2c 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/IrSourcePrinter.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/IrSourcePrinter.kt
@@ -121,9 +121,9 @@
 import org.jetbrains.kotlin.types.Variance
 import org.jetbrains.kotlin.utils.Printer
 
-fun IrElement.dumpSrc(): String {
+fun IrElement.dumpSrc(useFir: Boolean = false): String {
     val sb = StringBuilder()
-    accept(IrSourcePrinterVisitor(sb, "%tab%"), null)
+    accept(IrSourcePrinterVisitor(sb, "%tab%", useFir), null)
     return sb
         .toString()
         // replace tabs at beginning of line with white space
@@ -147,6 +147,7 @@
 class IrSourcePrinterVisitor(
     out: Appendable,
     indentUnit: String = "  ",
+    private val useFir: Boolean = false,
 ) : IrElementVisitorVoid {
     private val printer = Printer(out, indentUnit)
     private var currentScope: Scope = Scope()
@@ -212,7 +213,7 @@
             print("noinline ")
         }
         declaration.printAnnotations()
-        print(declaration.name)
+        print(declaration.normalizedName)
         print(": ")
         print(declaration.type.renderSrc())
         declaration.defaultValue?.let { it ->
@@ -533,7 +534,7 @@
                     trailingLambda = arg
                 } else {
                     arguments.add(arg)
-                    paramNames.add(param.name.asString())
+                    paramNames.add(param.normalizedName)
                 }
             } else {
                 useParameterNames = true
@@ -615,10 +616,12 @@
             IrTypeOperator.NOT_INSTANCEOF -> {
                 expression.argument.print()
             }
-            IrTypeOperator.CAST, IrTypeOperator.IMPLICIT_CAST, IrTypeOperator.SAFE_CAST -> {
+            IrTypeOperator.CAST, IrTypeOperator.SAFE_CAST, IrTypeOperator.IMPLICIT_CAST -> {
                 expression.argument.print()
             }
             IrTypeOperator.SAM_CONVERSION -> {
+                print(expression.type.renderSrc())
+                print(" ")
                 expression.argument.print()
             }
             IrTypeOperator.IMPLICIT_NOTNULL -> {
@@ -818,6 +821,9 @@
         get() = name.asString() == SpecialNames.ANONYMOUS_STRING ||
             origin == IrDeclarationOrigin.ADAPTER_FOR_CALLABLE_REFERENCE
 
+    private val IrFunction.isDelegatedPropertySetter: Boolean
+        get() = isSetter && origin == IrDeclarationOrigin.DELEGATED_PROPERTY_ACCESSOR
+
     private fun IrExpression.isLastStatementIn(statements: List<IrStatement>): Boolean {
         val lastStatement = statements.lastOrNull()
         return when {
@@ -832,9 +838,11 @@
 
     override fun visitReturn(expression: IrReturn) {
         val value = expression.value
-        // Only print the return statement directly if it is not the last statement in a lambda
+        // Only print the return statement directly if it is not the last statement in a lambda,
+        // or a delegated property setter. The latter have a superfluous "return" in K1.
         val returnTarget = expression.returnTargetSymbol.owner
-        if (returnTarget !is IrFunction || !returnTarget.isLambda ||
+        if (returnTarget !is IrFunction ||
+            (!returnTarget.isLambda && (useFir || !returnTarget.isDelegatedPropertySetter)) ||
             !expression.isLastStatementIn(returnTarget)) {
             val suffix = returnTargetToCall[returnTarget.symbol]?.let {
                 "@${it.symbol.owner.name}"
@@ -914,7 +922,7 @@
             declaration.isVar -> print("var ")
             else -> print("val ")
         }
-        print(declaration.name)
+        print(declaration.normalizedName)
         declaration.initializer?.let {
             print(" = ")
             it.print()
@@ -927,7 +935,7 @@
 
     override fun visitGetValue(expression: IrGetValue) {
         val owner = expression.symbol.owner
-        print(owner.name)
+        print(owner.normalizedName)
 
         if (
             owner.parent != currentScope.owner &&
@@ -992,7 +1000,7 @@
     }
 
     override fun visitSetValue(expression: IrSetValue) {
-        print(expression.symbol.owner.name)
+        print(expression.symbol.owner.normalizedName)
         print(" = ")
         expression.value.print()
     }
@@ -1526,7 +1534,7 @@
             if (owner is IrFunction) {
                 (0 until expectedCount).map {
                     if (it < owner.valueParameters.size)
-                        owner.valueParameters[it].name.asString()
+                        owner.valueParameters[it].normalizedName
                     else
                         "${it + 1}"
                 }
@@ -1558,6 +1566,18 @@
         }
     }
 
+    // Names for temporary variables and synthesized parameters are not consistent between
+    // K1 and K2. This function returns the same name for both frontends.
+    private val IrValueDeclaration.normalizedName: String
+        get() = when {
+            // FIR generates both <iterator> and tmp0_for_iterator...
+            origin == IrDeclarationOrigin.FOR_LOOP_ITERATOR -> "<iterator>"
+            !useFir && origin == IrDeclarationOrigin.UNDERSCORE_PARAMETER -> "<unused var>"
+            !useFir && name.asString().endsWith("_elvis_lhs") -> "<elvis>"
+            !useFir && name.asString() == "\$this\$null" -> "<this>"
+            else -> name.asString()
+        }
+
     override fun visitElement(element: IrElement) {
         print("<<${element::class.java.simpleName}>>")
     }
diff --git a/compose/compiler/compiler-hosted/src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar b/compose/compiler/compiler-hosted/src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar
new file mode 100644
index 0000000..c98e915
--- /dev/null
+++ b/compose/compiler/compiler-hosted/src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar
@@ -0,0 +1 @@
+androidx.compose.compiler.plugins.kotlin.ComposePluginRegistrar
\ No newline at end of file
diff --git a/compose/compiler/compiler-hosted/src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar b/compose/compiler/compiler-hosted/src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
deleted file mode 100644
index 100b36b..0000000
--- a/compose/compiler/compiler-hosted/src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
+++ /dev/null
@@ -1 +0,0 @@
-androidx.compose.compiler.plugins.kotlin.ComposeComponentRegistrar
\ No newline at end of file
diff --git a/compose/compiler/compiler/integration-tests/src/test/kotlin/androidx/compose/compiler/test/CompilerPluginRuntimeVersionCheckTest.kt b/compose/compiler/compiler/integration-tests/src/test/kotlin/androidx/compose/compiler/test/CompilerPluginRuntimeVersionCheckTest.kt
index 9a44385..228e4ac 100644
--- a/compose/compiler/compiler/integration-tests/src/test/kotlin/androidx/compose/compiler/test/CompilerPluginRuntimeVersionCheckTest.kt
+++ b/compose/compiler/compiler/integration-tests/src/test/kotlin/androidx/compose/compiler/test/CompilerPluginRuntimeVersionCheckTest.kt
@@ -122,8 +122,6 @@
     }
 
     private fun setupProjectBuildGradle() {
-        val kotlinGradlePlugin =
-            "org.jetbrains.kotlin:kotlin-gradle-plugin:${projectSetup.props.kotlinVersion}"
         val repositoriesBlock = buildString {
             appendLine("repositories {")
             appendLine("maven { url \"${projectSetup.props.tipOfTreeMavenRepoPath}\" }")
@@ -145,7 +143,7 @@
                 $repositoriesBlock
                 dependencies {
                     classpath "${projectSetup.props.agpDependency}"
-                    classpath "$kotlinGradlePlugin"
+                    classpath "${projectSetup.props.kgpDependency}"
                 }
             }
 
diff --git a/compose/desktop/desktop/build.gradle b/compose/desktop/desktop/build.gradle
index 3d2c88c..ed903b5 100644
--- a/compose/desktop/desktop/build.gradle
+++ b/compose/desktop/desktop/build.gradle
@@ -18,30 +18,24 @@
 import androidx.build.BuildOnServerKt
 import androidx.build.KmpPlatformsKt
 import androidx.build.LibraryType
+import androidx.build.PlatformIdentifier
+import androidx.build.Publish
 import androidx.build.RunApiTasks
 
 plugins {
     id("AndroidXPlugin")
-    id("java") // https://github.com/gradle/gradle/issues/18622
     id("AndroidXComposePlugin")
-    id("kotlin-multiplatform")
 }
 
+// Compose Desktop is qualified on "desktop" but actually builds as "jvm".
 def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
 
-dependencies {
-}
+androidXMultiplatform {
+    if (desktopEnabled) jvm()
 
-kotlin {
-    if (desktopEnabled) {
-        jvm() {
-            withJava()
-        }
-    }
-
-    if (desktopEnabled) {
-        sourceSets {
-            commonMain.dependencies {
+    sourceSets {
+        commonMain {
+            dependencies {
                 implementation(libs.kotlinStdlibCommon)
                 implementation(project(":compose:ui:ui-util"))
                 api(project(":compose:foundation:foundation"))
@@ -50,17 +44,30 @@
                 api(project(":compose:ui:ui"))
                 api(project(":compose:ui:ui-tooling-preview"))
             }
+        }
 
-            jvmMain.dependencies {
-                implementation(libs.kotlinStdlib)
-                implementation(libs.kotlinStdlibJdk8)
-                implementation(libs.kotlinCoroutinesCore)
+        commonTest {
+            dependencies {
+            }
+        }
+
+        if (desktopEnabled) {
+            jvmMain {
+                dependsOn(commonMain)
+                dependencies {
+                    implementation(libs.kotlinStdlib)
+                    implementation(libs.kotlinStdlibJdk8)
+                    implementation(libs.kotlinCoroutinesCore)
+                }
             }
 
             jvmTest {
+                dependsOn(commonTest)
+
                 resources.srcDirs += new File(AndroidXConfig.getExternalProjectPath(project),
                         "noto-fonts/other/")
                 resources.srcDirs += "src/jvmTest/res"
+
                 dependencies {
                     implementation(libs.kotlinCoroutinesTest)
                     implementation(libs.skikoCurrentOs)
@@ -73,14 +80,6 @@
     }
 }
 
-if (!desktopEnabled) {
-    kotlin {
-        jvm("disabled") {
-            withJava()
-        }
-    }
-}
-
 File getGoldenPath(Project project) {
     if (System.getenv("COMPOSE_DESKTOP_GITHUB_BUILD") != null) {
         def externalPath = SupportConfigKt.getExternalProjectPath(project)
@@ -91,6 +90,7 @@
 }
 
 if (desktopEnabled) {
+    // Set golden images path based on whether this is the GitHub or AndroidX repo.
     tasks.findByName("jvmTest").configure {
         systemProperties["GOLDEN_PATH"] = getGoldenPath(project).toString()
     }
@@ -99,11 +99,13 @@
 androidx {
     name = "Compose Desktop"
     type = LibraryType.PUBLISHED_LIBRARY
+    publish = desktopEnabled ? Publish.SNAPSHOT_AND_RELEASE : Publish.NONE
     inceptionYear = "2020"
     legacyDisableKotlinStrictApiMode = true
-    runApiTasks = new RunApiTasks.No(
-            "API tracking for desktop isn't supported at the moment, see b/163110180"
-    )
+    runApiTasks = new RunApiTasks.No("Not supported yet, see b/163110180")
+
+    // Project parser does not support dynamic configuration of `publish`.
+    runProjectParser = false
 }
 
 def jvmOs(container, name, skikoDep) {
@@ -133,18 +135,22 @@
     }
 }
 
-afterEvaluate {
-    publishing {
-        publications {
-            jvmOs(it, "linux-x64", libs.skikoLinuxX64.get())
-            jvmOs(it, "linux-arm64", libs.skikoLinuxArm64.get())
-            jvmOs(it, "macos-x64", libs.skikoMacOsX64.get())
-            jvmOs(it, "macos-arm64", libs.skikoMacOsArm64.get())
-            jvmOs(it, "windows-x64", libs.skikoWindowsX64.get())
+if (desktopEnabled) {
+    // Set up additional publications containing native Skiko dependencies.
+    afterEvaluate {
+        publishing {
+            publications {
+                jvmOs(it, "linux-x64", libs.skikoLinuxX64.get())
+                jvmOs(it, "linux-arm64", libs.skikoLinuxArm64.get())
+                jvmOs(it, "macos-x64", libs.skikoMacOsX64.get())
+                jvmOs(it, "macos-arm64", libs.skikoMacOsArm64.get())
+                jvmOs(it, "windows-x64", libs.skikoWindowsX64.get())
+            }
         }
     }
 }
 
 if (desktopEnabled) {
+    // Validate desktop build by explicitly building the jvmJar task as part of bOS.
     BuildOnServerKt.addToBuildOnServer(project, "${project.path}:jvmJar")
 }
diff --git a/compose/desktop/desktop/samples/build.gradle b/compose/desktop/desktop/samples/build.gradle
index 8ce657b..5b95cd0 100644
--- a/compose/desktop/desktop/samples/build.gradle
+++ b/compose/desktop/desktop/samples/build.gradle
@@ -20,31 +20,50 @@
 
 plugins {
     id("AndroidXPlugin")
-    id("java")
     id("AndroidXComposePlugin")
-    id("kotlin-multiplatform")
 }
 
+// Compose Desktop is qualified on "desktop" but actually builds as "jvm".
 def desktopEnabled = KmpPlatformsKt.enableDesktop(project)
 
-if (desktopEnabled) {
-    kotlin {
-        jvm()
+androidXMultiplatform {
+    if (desktopEnabled) jvm()
 
-        sourceSets {
+    sourceSets {
+        commonMain {
+            dependencies {
+            }
+        }
+
+        commonTest {
+            dependencies {
+            }
+        }
+
+        if (desktopEnabled) {
             jvmMain {
+                dependsOn(commonMain)
+
                 resources.srcDirs += new File(AndroidXConfig.getExternalProjectPath(project),
                         "noto-fonts/other/")
                 resources.srcDirs += "src/jvmMain/res"
+
+                dependencies {
+                    implementation(libs.skikoCurrentOs)
+                    implementation(project(":compose:desktop:desktop"))
+                }
             }
 
-            jvmMain.dependencies {
-                implementation(libs.skikoCurrentOs)
-                implementation(project(":compose:desktop:desktop"))
+            jvmTest {
+                dependsOn(commonTest)
+                dependencies {
+                }
             }
         }
     }
+}
 
+if (desktopEnabled) {
     task run1(type: JavaExec) {
         dependsOn(":compose:desktop:desktop:jar")
         main = "androidx.compose.desktop.examples.example1.Main_jvmKt"
@@ -106,15 +125,9 @@
     task run {
         dependsOn("run1")
     }
-
-
-    BuildOnServerKt.addToBuildOnServer(project, "${project.path}:jvmJar")
 }
 
-if (!desktopEnabled) {
-    kotlin {
-        jvm("disabled") {
-            withJava()
-        }
-    }
+if (desktopEnabled) {
+    // Validate desktop build by explicitly building the jvmJar task as part of bOS.
+    BuildOnServerKt.addToBuildOnServer(project, "${project.path}:jvmJar")
 }
diff --git a/compose/docs/compose-component-api-guidelines.md b/compose/docs/compose-component-api-guidelines.md
new file mode 100644
index 0000000..e337d3a
--- /dev/null
+++ b/compose/docs/compose-component-api-guidelines.md
@@ -0,0 +1,1297 @@
+# API Guidelines for `@Composable` components in Jetpack Compose
+
+## Last updated: July 19, 2023
+
+Set of guidelines and recommendations for building scalable and user-friendly @Composable components.
+
+The requirement level of each of these guidelines is specified using the terms set forth in [RFC2119](https://www.ietf.org/rfc/rfc2119.txt) for each of the following developer audiences. If an audience is not specifically named with a requirement level for a guideline, it should be assumed that the guideline is OPTIONAL for that audience.
+
+### Jetpack Compose framework development
+
+Contributions to the androidx.compose libraries and tools generally follow these guidelines to a strict degree to promote consistency, setting expectations and examples for consumer code at all layers.
+
+### Library development based on Jetpack Compose
+
+It is expected and desired that an ecosystem of external libraries will come to exist that target Jetpack Compose, exposing a public API of `@Composable` functions and supporting types for consumption by apps and other libraries. While it is desirable for these libraries to follow these guidelines to the same degree as Jetpack Compose framework development would, organizational priorities and local consistency may make it appropriate for some purely stylistic guidelines to be relaxed.
+
+### App development based on Jetpack Compose
+
+App development is often subject to strong organizational priorities and norms and requirements to integrate with existing app architecture. This may call for not only stylistic deviation from these guidelines but structural deviation as well. Where possible, alternative approaches for app development will be listed in this document that may be more appropriate in these situations.
+
+## Table of content
+- [Note on vocabulary in this doc](#note-on-vocabulary-in-this-doc)
+- [Before you create a component](#before-you-create-a-component)
+    - [Component’s purpose](#components-purpose)
+    - [Component layering](#component-layering)
+    - [Do you need a component?](#do-you-need-a-component)
+    - [Component or Modifier](#component-or-modifier)
+- [Name of a Component](#name-of-a-component)
+    - [BasicComponent vs Component](#basiccomponent-vs-component)
+    - [Design, Usecase or Company/Project specific prefixes](#design-usecase-or-companyproject-specific-prefixes)
+- [Component dependencies](#component-dependencies)
+    - [Prefer multiple components over style classes](#prefer-multiple-components-over-style-classes)
+    - [Explicit vs implicit dependencies](#explicit-vs-implicit-dependencies)
+- [Component parameters](#component-parameters)
+    - [Parameters vs. Modifier on the component](#parameters-vs-modifier-on-the-component)
+    - [`modifier` parameter](#modifier-parameter)
+    - [Parameters order](#parameters-order)
+    - [Nullable parameter](#nullable-parameter)
+    - [Default expressions](#default-expressions)
+    - [MutableState\<T\> as a parameter](#mutablestatet-as-a-parameter)
+    - [State\<T\> as a parameter](#statet-as-a-parameter)
+    - [Slot parameters](#slot-parameters)
+        - [What are slots](#what-are-slots)
+        - [Why slots](#why-slots)
+        - [Single “content” slot overloads](#single-content-slot-overloads)
+        - [Layout strategy scope for slot APIs](#layout-strategy-scope-for-slot-apis)
+        - [Lifecycle expectations for slot parameters](#lifecycle-expectations-for-slot-parameters)
+        - [DSL based slots](#dsl-based-slots)
+- [Component-related classes and functions](#component-related-classes-and-functions)
+    - [State](#state)
+    - [ComponentDefault object](#componentdefault-object)
+    - [ComponentColor/ComponentElevation objects](#componentcolorcomponentelevation-objects)
+- [Documentation for the component](#documentation-for-the-component)
+    - [Documentation structure and ordering](#documentation-structure-and-ordering)
+    - [Documentation example](#documentation-example)
+- [Accessibility of the component](#accessibility-of-the-component)
+    - [Semantics merging](#semantics-merging)
+    - [Accessibility related parameters](#accessibility-related-parameters)
+    - [Accessibility tuning](#accessibility-tuning)
+- [Evolution of the Component APIs](#evolution-of-the-component-apis)
+
+## Note on vocabulary in this doc
+
+**@Composable component** - A @Composable function that returns `Unit` and emits the UI when it is composed in a hierarchy (later: component).
+
+**Developer** - a person who creates a component that is to be used by a user in an application or in another component library.
+
+**User** - the user of the component - a person who uses the component in a composable hierarchy to show some ui to the end-user.
+
+**End-user** - the person who will use the application created by the user of your component.
+
+These guidelines outline the best practices for developing UI components using Jetpack Compose. Best practices ensure that the API of the components is:
+
+*   **Scalable long term**: the author is able to evolve the API to cause the least amount of friction to users.
+*   **Consistent across other components**: developers can use existing knowledge and patterns to work with new components that are created by different authors.
+*   **Guide developers towards the happy path**: components will encourage the right practices and usages and disallow the incorrect usage where possible.
+
+## Before you create a component
+
+When creating a new component:
+
+*   Make sure there’s a single problem the component solves. Split components into subcomponents and building blocks until each solves a single problem users have.
+*   Make sure you need a component and it brings value that justifies the long term support and evolution of its APIs. Often developers might find that it is easier for users to write the component code themselves so they can adjust later.
+
+### Component’s purpose
+
+Consider the value a new component adds and the problem it solves. Each component should solve only **one** problem, and each problem should be solved in **one** place. If your component solves more than one problem look for opportunities to split it into layers or subcomponents. With the benefit of smaller, concise and use case targeted API comes the easy of use and clear understanding of the component contract.
+
+Lower level building blocks and components usually add certain new single functionality and are easy to combine together. Higher level components serve a purpose of combining building blocks to provide an opinionated, ready to use behavior.
+
+**DON’T**
+```
+// avoid multipurpose components: for example, this button solves more than 1 problem
+@Composable
+fun Button(
+    // problem 1: button is a clickable rectangle
+    onClick: () -> Unit = {},
+    // problem 2: button is a check/uncheck checkbox-like component
+    checked: Boolean = false,
+    onCheckedChange: (Boolean) -> Unit,
+) { ... }
+```
+
+**Do:**
+```
+@Composable
+fun Button(
+    // problem 1: button is a clickable rectangle
+    onClick: () -> Unit,
+) { ... }
+
+@Composable
+fun ToggleButton(
+    // problem 1: button is a check/uncheck checkbox-like component
+    checked: Boolean,
+    onCheckedChange: (Boolean) -> Unit,
+) { ... }
+```
+
+### Component layering
+
+When creating components, provide various layers of single purpose building blocks first that are needed for the component to work. Increase level of opinion ingrained and reduce the amount of customisations as you go from low level APIs to higher level. Higher level components should provide more opinionated defaults and fewer customisation options.
+
+`@Composable` component creation was designed to be a low-effort operation in Compose so that users can create their own single purpose components and adjust them as needed.
+
+**Do:**
+```
+// single purpose building blocks component
+@Composable
+fun Checkbox(...) { ... }
+
+@Composable
+fun Text(...) { ... }
+
+@Composable
+fun Row(...) { ... }
+
+// high level component that is more opinionated combination of lower level blocks
+@Composable
+fun CheckboxRow(...) {
+    Row {
+        Checkbox(...)
+        Spacer(...)
+        Text(...)
+    }
+}
+```
+
+### Do you need a component?
+
+Question the need for creating the component in the first place. With high-level components that can be combined from building blocks, there has to be a strong reason for it to exist. Lower level components should solve a real problem that users have.
+
+Try to create a Component from the publicly available building blocks. This provides the sense of what it feels like to be a developer who needs your component. If it looks simple, readable and doesn’t require hidden knowledge to make - this means users can do it themselves.
+
+Consider the value your component brings to users if they choose it over doing it themselves. Consider the burden a component puts on a user who would need to learn new APIs to use them.
+
+For example, a developer wants to create a `RadioGroup` component. In order to accommodate various requirements such as vertical and horizontal layouts, different types of data and decorations, the API might look like this:
+```
+@Composable
+fun <T> RadioGroup(
+    // `options` are a generic type
+    options: List<T>,
+    // horizontal or vertical
+    orientation: Orientation,
+    // some adjustments around content layout
+    contentPadding: PaddingValues,
+    modifier: Modifier = Modifier,
+    optionContent: @Composable (T) -> Unit
+) { ... }
+```
+
+While doing this, look first at how users would write it themselves using the available building blocks:
+```
+// Modifier.selectableGroup adds semantics of a radio-group like behavior
+// accessibility services will treat it as a parent of various options
+Column(Modifier.selectableGroup()) {
+   options.forEach { item ->
+       Row(
+           modifier = Modifier.selectable(
+               selected = (select.value == item),
+               onClick = { select.value = item }
+           ),
+           verticalAlignment = Alignment.CenterVertically
+       ) {
+           Text(item.toString())
+           RadioButton(
+               selected = (select.value == item),
+               onClick = { select.value = item }
+           )
+       }
+   }
+}
+```
+
+Now, developers should make a conscious decision on whether the `RadioGroup` API is worth it. In this particular example, users utilize familiar building blocks such as `Row`, `Text` and other basic tools. They also gain the flexibility to define the layouts needed or add any decorations and customisations. The case might be made to not to introduce any `RadioGroup` APIs at all.
+
+Shipping a component is costly, involving at least the development, testing, long term support and subsequent evolution of the API.
+
+### Component or Modifier
+
+Make a component if it has a distinct UI that cannot be applied to other components or if the component wants to make structural changes in the UI (add/remove other components).
+
+Make the feature to be a Modifier instead if the bit of functionality can be applied to any arbitrary **single** component to add extra behavior. This is especially important when the functionality has undefined behavior when applied to a few UI components at the same time.
+
+**DON’T**
+```
+@Composable
+fun Padding(allSides: Dp) {
+    // impl
+}
+
+// usage
+Padding(12.dp) {
+    // 1. Is it a padding around both card and picture or for each one?
+    // 2. What are the layout expectations for card and picture?
+    // 3. What if there is no content (no card and picture at all)?
+    UserCard()
+    UserPicture()
+}
+```
+
+**Do:**
+```
+fun Modifier.padding(allSides: Dp): Modifier = // implementation
+
+// usage
+UserCard(modifier = Modifier.padding(12.dp))
+```
+
+If the bit of functionality can be applied to any composable, but it has to alter the hierarchy of composables, it has to be a Component, since Modifiers cannot change the hierarchy:
+
+**Do**
+```
+@Composable
+fun AnimatedVisibility(
+    visibile: Boolean,
+    modifier: Modifier = Modifier,
+    content: @Composable () -> Unit
+) {
+    // impl
+}
+
+// usage: AnimatedVisibility has to have power to remove/add UserCard
+// to hierarchy depending on the visibility flag
+AnimatedVisibility(visible = false) {
+    UserCard()
+}
+```
+
+## Name of a Component
+
+Please, refer to the corresponding [Compose API guidelines](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-api-guidelines.md#naming-unit-composable-functions-as-entities) section for naming conventions. However, there are more detailed considerations to keep in mind.
+
+**Jetpack Compose framework development** MUST follow the rules in this section.
+
+**Library development** MUST follow the section below.
+
+**App development** MAY follow the rules below.
+
+### BasicComponent vs Component
+
+Consider `Basic*` prefix for components that provide barebones functionality with no decoration and/or with no design-system based visual opinions. This is a signal that users are expected to wrap it in their own decoration, as the component is not expected to be used as-is. As a counterpart to that, `Component` name without a prefix can represent components that are ready to use and are decorated according to some design specification.
+
+**Do:**
+```
+// component that has no decoration, but basic functionality
+@Composable
+fun BasicTextField(
+    value: TextFieldValue,
+    onValueChange: (TextFieldValue) -> Unit,
+    modifier: Modifier = Modifier,
+    ...
+)
+
+// ready to use component with decorations
+@Composable
+fun TextField(
+    value: TextFieldValue,
+    onValueChange: (TextFieldValue) -> Unit,
+    modifier: Modifier = Modifier,
+    ...
+)
+```
+
+### Design, Usecase or Company/Project specific prefixes
+
+Avoid `CompanyName` (`GoogleButton`) or Module (`WearButton`) prefixes where possible and consider use-case or domain specific names if needed to. If the component you are building is a part of component library built using ``compose-foundation`` or ``compose-ui`` building blocks as a basis, the majority of the non-prefixed names should be available to developers without clashes: `com.companyname.ui.Button` or `com.companyname.ui.Icon`. Simple names make sure these components feel first-class when used.
+
+If wrapping existing components or building on top of another design system, consider names that are derived from the use case first: `ScalingLazyColumn`, `CurvedText`. If impossible or the use case clashes with the existing component, module/library prefix can be used e.g. `GlideImage.`
+
+If your design system specification introduces a number of similar components with different appearances, consider using specification prefixes: `ContainedButton`, `OutlinedButton`, `SuggestionChip`, etc. Using prefixes helps you avoid “style” patterns and keep the API simple. See "[ComponentColor/ComponentElevation](#componentcolorcomponentelevation-objects)" section for more details.
+
+If you have a set of components with prefixes, consider choosing the default component, which is the one most likely to be used, and keep it without the prefix.
+
+**Do**
+```
+// This button is called ContainedButton in the spec
+// It has no prefix because it is the most common one
+@Composable
+fun Button(...) {}
+
+// Other variations of buttons below:
+@Composable
+fun OutlinedButton(...) {}
+
+@Composable
+fun TextButton(...) {}
+
+@Composable
+fun GlideImage(...) {}
+```
+
+**Also do (if your library is based on compose-foundation)**
+```
+// package com.company.project
+// depends on foundation, DOES NOT depend on material or material3
+
+@Composable
+fun Button(...) {} // simple name that feel like a first-class button
+
+@Composable
+fun TextField(...) {} // simple name that feel like a first-class TF
+
+```
+
+## Component dependencies
+
+**Jetpack Compose framework development** MUST follow the rules in this section.
+
+**Library development** SHOULD follow the section below.
+
+**App development** MAY follow the rules below.
+
+### Prefer multiple components over style classes
+
+Express dependencies in a granular, semantically meaningful way. Avoid grab-bag style parameters and classes, akin to `ComponentStyle` or `ComponentConfiguration`.
+
+When a certain subset of components of the same type need to have the same configurations or stylistical visual appearance, users should be encouraged to create their own semantically meaningful version of a component. This can be done either by wrapping the component or forking it and using lower-level building blocks. This is the component developer’s responsibility to make sure that both of those ways are low cost operations.
+
+Instead of relying on the `ComponentStyle` to specify different component variations in the component library, consider providing separate `@Composable` functions named differently to signify the difference in styling and use cases for those components.
+
+**DON’T**
+
+```
+// library code
+class ButtonStyles(
+    /* grab bag of different parameters like colors, paddings, borders */
+    background: Color,
+    border: BorderStroke,
+    textColor: Color,
+    shape: Shape,
+    contentPadding: PaddingValues
+)
+
+val PrimaryButtonStyle = ButtonStyle(...)
+val SecondaryButtonStyle = ButtonStyle(...)
+val AdditionalButtonStyle = ButtonStyle(...)
+
+@Composable
+fun Button(
+    onClick: () -> Unit,
+    style: ButtonStyle = SecondaryButtonStyle
+) {
+    // impl
+}
+
+// usage
+val myLoginStyle = ButtonStyle(...)
+Button(style = myLoginStyle)
+```
+
+**Do:**
+```
+// library code
+@Composable
+fun PrimaryButton(
+    onClick: () -> Unit,
+    background: Color,
+    border: BorderStroke,
+    // other relevant parameters
+) {
+    // impl
+}
+
+@Composable
+fun SecondaryButton(
+    onClick: () -> Unit,
+    background: Color,
+    border: BorderStroke,
+    // other relevant parameters
+) {
+    // impl
+}
+
+// usage 1:
+PrimaryButton(onClick = { loginViewModel.login() }, border = NoBorder)
+// usage 2:
+@Composable
+fun MyLoginButton(
+    onClick: () -> Unit
+) {
+    // delegate to and wrap other components or its building blocks
+    SecondaryButton(
+        onClick,
+        background = MyLoginGreen,
+        border = LoginStroke
+    )
+}
+```
+
+### Explicit vs implicit dependencies
+
+Prefer explicit inputs and configuration options in your components, such as function parameters. Explicit inputs for the component make it easy to predict the component's behavior, adjust it, test and use.
+
+Avoid implicit inputs provided via `CompositionLocal` or other similar mechanisms. Those inputs add complexity to the components and every usage of it and make it hard to track where customisation comes from for users. To avoid implicit dependencies, make it easy for users to create their own opinionated components with a subset of explicit inputs they wish to customize.
+
+**DON’T**
+```
+// avoid composition locals for component specific customisations
+// they are implicit. Components become difficult to change, test, use.
+val LocalButtonBorder = compositionLocalOf<BorderStroke>(...)
+
+@Composable
+fun Button(
+    onClick: () -> Unit,
+) {
+    val border = LocalButtonBorder.current
+}
+
+```
+
+**Do:**
+```
+@Composable
+fun Button(
+    onClick: () -> Unit,
+    // explicitly asking for explicit parameter that might have
+    // reasonable default value
+    border: BorderStroke = ButtonDefaults.borderStroke,
+) {
+    // impl
+}
+```
+
+Consider using `CompositionLocal` to provide a global app or screen styling if needed. For example, design theming or typography in the material library can be implicitly specified for the whole app or screen. When doing so, make sure that those CompositionLocals are being read in the default expressions on the component parameters, so users can override them.
+
+Since those objects rarely change and cover big subtrees of components of different kinds, the flexibility of app-wide customisation is usually worth the aforementioned downsides of the implicit inputs. In cases like this, components should be discouraged to read this `CompositionLocal` in implementation and instead read it in the default expressions, so it is easy to override when customizing or wrapping the component.
+
+**DON’T**
+```
+// this is ok: theme is app global, but...
+class Theme(val mainAppColor: Color)
+val LocalAppTheme = compositionLocalOf { Theme(Color.Green) }
+
+@Composable
+fun Button(
+    onClick: () -> Unit,
+) {
+    // reading theme in implementation makes it impossible to opt out
+    val buttonColor = LocalAppTheme.current.mainAppColor
+    Box(modifier = Modifier.background(buttonColor)) { ... }
+}
+
+```
+
+**Do:**
+```
+// this is ok: theme is app global
+class Theme(val mainAppColor: Color)
+val LocalAppTheme = compositionLocalOf { Theme(Color.Green) }
+
+@Composable
+fun Button(
+    onClick: () -> Unit,
+    // easy to see where the values comes from and change it
+    backgroundColor: Color = LocalAppTheme.current.mainAppColor
+) {
+    Box(modifier = Modifier.background(backgroundColor)) { ... }
+}
+```
+
+_There’s a [blogpost](https://medium.com/androiddevelopers/pushing-the-right-buttons-in-jetpack-compose-124cb4b17197) published that describes the reasoning in depth in the chapter “Maintaining API consistency”._
+
+## Component parameters
+
+Set of considerations regarding parameters of `@Composable` component.
+
+**Jetpack Compose framework development** MUST follow the rules in this section below.
+
+**Compose library development** SHOULD follow the rules in the sections below.
+
+**App development** SHOULD follow.
+
+### Parameters vs. Modifier on the component
+
+Do not introduce optional parameters that add optional behavior that could otherwise be added via Modifier. Parameters should allow to set or customize the behavior that exists internally in the component.
+
+**DON’T:**
+```
+@Composable
+fun Image(
+    bitmap: ImageBitmap,
+    // not core functionality, click can be added via Modifier.clickable
+    onClick: () -> Unit = {},
+    modifier: Modifier = Modifier,
+    // can be specified via `Modifier.clip(CircleShape)`
+    clipToCircle: Boolean = false
+)
+```
+
+**Do:**
+```
+@Composable
+fun Button(
+    onClick: () -> Unit,
+    // modifier param specified so that width, padding etc can be added
+    modifier: Modifier = Modifier,
+    // button is a colored rect that clicks, so background
+    // considered as a core functionality, OK as a param
+    backgroundColor: Color = MaterialTheme.colors.primary
+)
+```
+
+### `modifier` parameter
+
+Every component that emits UI should have a modifier parameter. Make sure that modifier parameter:
+
+* Has the type `Modifier`.
+    *   Type Modifier ensures that any Modifier can be passed to the component.
+*   Is the first optional parameter.
+    *   If a component has non-zero default size - modifier should be optional, since the component is self sufficient. For components with zero default size modifier parameter can be a required param.
+    *   Since the modifier is recommended for any component and is used often, placing it first ensures that it can be set without a named parameter and provides a consistent place for this parameter in any component.
+*   Has a no-op default value `Modifier`.
+    *   No-op default value ensures that no functionality will be lost when users provide their own modifiers for the component.
+*   Is the only parameter of type Modifier in the parameter list.
+    *   Since modifiers intend to modify the external behaviors and appearance of the component, one modifier parameter should be sufficient. Consider asking for specific parameters or reconsidering the layering of the component (e.g. braking component into two) instead.
+*   Is applied once as a first modifier in the chain to the root-most layout in the component implementation.
+    *   Since modifiers intend to modify the external behaviors and appearance of the component, they must be applied to the outer-most layout and be the first modifiers in the chain. It is ok to chain other modifiers to the modifier that is passed as a parameter.
+
+**Why?** Modifiers are the essential part of compose, users have expectations about their behavior and API. Essentially, modifiers provide a way to modify the external component behavior and appearance, while component implementation will be responsible for the internal behavior and appearance.
+
+**DON’T:**
+```
+@Composable
+fun Icon(
+    bitmap: ImageBitmap,
+    // no modifier parameter
+    tint: Color = Color.Black
+)
+```
+
+**DON’T:**
+```
+@Composable
+fun Icon(
+    bitmap: ImageBitmap,
+    tint: Color = Color.Black,
+    // 1: modifier is not the first optional parameter
+    // 2: padding will be lost as soon as the user sets its own modifier
+    modifier: Modifier = Modifier.padding(8.dp)
+)
+```
+
+**DON’T:**
+```
+@Composable
+fun CheckboxRow(
+    checked: Boolean,
+    onCheckedChange: (Boolean) -> Unit,
+    // DON'T - modifier is intended to specify the external behavior of
+    // the CheckboxRow itself, not its subparts. Make them slots instead
+    rowModifier: Modifier = Modifier,
+    checkboxModifier: Modifier = Modifier
+)
+```
+
+**DON’T:**
+```
+@Composable
+fun IconButton(
+    buttonBitmap: ImageBitmap,
+    modifier: Modifier = Modifier,
+    tint: Color = Color.Black
+) {
+    Box(Modifier.padding(16.dp)) {
+        Icon(
+            buttonBitmap,
+            // modifier should be applied to the outer-most layout
+            // and be the first one in the chain
+            modifier = Modifier.aspectRatio(1f).then(modifier),
+            tint = tint
+        )
+    }
+}
+```
+
+**Do:**
+```
+@Composable
+fun IconButton(
+    buttonBitmap: ImageBitmap,
+    // good: first optional parameter, single of its kind
+    modifier: Modifier = Modifier,
+    tint: Color = Color.Black
+) {
+    // good: applied before other modifiers to the outer layout
+    Box(modifier.padding(16.dp)) {
+        Icon(buttonBitmap, modifier = Modifier.aspectRatio(1f), tint = tint)
+    }
+}
+```
+
+**Also Do:**
+```
+@Composable
+fun ColoredCanvas(
+    // ok: canvas has no intrinsic size, asking for size modifiers
+    modifier: Modifier,
+    color: Color = Color.White,
+    ...
+) {
+    // good: applied before other modifiers to the outer layout
+    Box(modifier.background(color)) {
+        ...
+    }
+}
+```
+
+### Parameters order
+
+The order of parameters in a component must be as follows:
+
+1. Required parameters.
+2. Single` modifier: Modifier = Modifier`.
+3. Optional parameters.
+4. (optional) trailing `@Composable` lambda.
+
+**Why?** Required parameters indicate the contract of the component, since they have to be passed and are necessary for the component to work properly. By placing required parameters first, API clearly indicates the requirements and contract of the said component. Optional parameters represent some customisation and additional capabilities of the component, and don’t require immediate attention of the user.
+
+Explanation for the order of the parameters:
+
+1. Required parameters. Parameters that don’t have default values and the user is required to pass the values for those parameters in order to use the components. Coming first, they allow users to set them without using named parameters.
+2. `modifier: Modifier = Modifier`. Modifiers should come as a first optional parameter in a @composable function. It must be named `modifier` and have a default value of `Modifier`. There should be only one modifier parameter and it should be applied to the root-most layout in the implementation. See "[modifier parameter](#modifier-parameter)" section for more information.
+3. Optional parameters. Parameters that have default values that will be used if not overridden by the user of the component. Coming after required parameters and a `modifier` parameter, they do not require the user to make an immediate choice and allow one-by-one override using named parameters.
+4. (optional) trailing `@Composable` lambda representing the main content of the component, usually named `content`. It can have a default value. Having non-@composable trailing lambda (e.g. `onClick`) might be misleading as it is a user expectation to have a trailing lambda in a component to be `@Composable`. For `LazyColumn` and other DSL-like exceptions, it is ok to have non-@composable lambda since it still represents the main content.
+
+Think about the order of parameters inside the “required” and “optional” subgroups as well. Similar to the split between required and optional parameters, it is beneficial for the reader and user of the API to see the data, or “what” part of the component first, while metadata, customisation, the “how” of the component should come after.
+
+It makes sense to group parameters semantically within the required or optional groups. If you have a number of color parameters (`backgroundColor` and `contentColor`), consider placing them next to each other to make it easy for the user to see customisation options.
+
+**Do**
+```
+@Composable
+fun Icon(
+    // image bitmap and contentDescription are required
+    // bitmap goes first since it is the required data for the icon
+    bitmap: ImageBitmap,
+    // contentDescription follows as required, but it is a "metadata", so
+    // it goes after the "data" above.
+    contentDescription: String?,
+    // modifier is the first optional parameter
+    modifier: Modifier = Modifier,
+    // tint is optional, default value uses theme-like composition locals
+    // so it's clear where it's coming from and to change it
+    tint: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
+)
+```
+
+**Do**
+```
+@Composable
+fun LazyColumn(
+    // no required parameters beyond content, modifier is the first optional
+    modifier: Modifier = Modifier,
+    // state is important and is a "data": second optional parameter
+    state: LazyListState = rememberLazyListState(),
+    contentPadding: PaddingValues = PaddingValues(0.dp),
+    reverseLayout: Boolean = false,
+    // arrangement and alignment go one-by-one since they are related
+    verticalArrangement: Arrangement.Vertical =
+        if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
+    horizontalAlignment: Alignment.Horizontal = Alignment.Start,
+    flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
+    userScrollEnabled: Boolean = true,
+    // trailing lambda with content
+    content: LazyListScope.() -> Unit
+)
+```
+
+### Nullable parameter
+
+Make conscious choices between the semantical meaning of the parameter or its absence. There’s a difference between default value, empty value and absent value. A conscious choice has to be made when choosing the right semantic for the API.
+
+*   Nullability of the parameters should be introduced as a signal to allow users to express “absence” of the parameter and corresponding component’s capabilities.
+*   Avoid making parameters nullable to utilize `null` as a “use default in the implementation” signal.
+*   Avoid making parameter nullable to signal that the value exists, but is empty, prefer a meaningful empty default value.
+
+**DON’T**
+```
+@Composable
+fun IconCard(
+    bitmap: ImageBitmap,
+    // avoid having null as a signal to gather default
+    elevation: Dp? = null
+) {
+    // instead of implementation based default resolution, provide meaningful default
+    val resolvedElevation = elevation ?: DefaultElevation
+}
+```
+
+**Do:**
+```
+@Composable
+fun IconCard(
+    bitmap: ImageBitmap,
+    elevation: Dp = 8.dp
+) { ... }
+```
+
+
+**Or Do (null is meaningful here):**
+```
+@Composable
+fun IconCard(
+    bitmap: ImageBitmap,
+    // null description is NOT the same as "" description
+    // since when it is null - we don't add any accessibility info.
+    contentDescription: String?
+) { ... }
+```
+
+### Default expressions
+
+Developers should make sure that default expressions on optional parameters are publicly available and meaningful. Best practices:
+
+*   Default expression does not contain private/internal calls. This allows users that wrap/extend components to provide the same default. Alternatively, this default can be used by the users in the if statement: `if (condition) default else myUserValue`.
+*   Default should have meaningful value, it should be clear what the default value is. Avoid using `null` as a marker to use the default value internally. Refer to null as the “absence” of the value (per "[nullable parameter](#nullable-parameter)" section). Absence of the value (null) is a valid default in this case.
+*   Use `ComponentDefaults` objects to name-space defaults values if you have a number of them.
+
+**DON’T**
+```
+@Composable
+fun IconCard(
+    bitmap: ImageBitmap,
+    //backgroundColor has meaningful default, but it is inaccessible to users
+    backgroundColor: Color = DefaultBackgroundColor,
+    // avoid having null as a signal to gather default
+    elevation: Dp? = null
+) {
+    // instead of implementation based default resolution, provide meaningful default
+    val resolvedElevation = elevation ?: DefaultElevation
+}
+
+// this default expression is private.
+// Users unable to access it when wrapping your component.
+private val DefaultBackgroundColor = Color.Red
+private val DefaultElevation = 8.dp
+```
+
+**Do:**
+```
+@Composable
+fun IconCard(
+    bitmap: ImageBitmap,
+    //all params have meaningful defaults that are accessible
+    backgroundColor: Color = IconCardDefaults.BackgroundColor,
+    elevation: Dp = IconCardDefaults.Elevation
+) { ... }
+
+// defaults namespaced in the ComponentNameDefaults object and public
+object IconCardDefaults {
+    val BackgroundColor = Color.Red
+    val Elevation = 8.dp
+}
+```
+
+**Note:** If your component has a limited number of parameters that have short and predictable defaults (``elevation = 0.dp``), `ComponentDefaults` object might be omitted in favor of simple inline constants.
+
+### MutableState\<T\> as a parameter
+
+Parameters of type `MutableState<T>` are discouraged since it promotes joint ownership over a state between a component and its user. If possible, consider making the component stateless and concede the state change to the caller. If mutation of the parent’s owned property is required in the component, consider creating a `ComponentState` class with the domain specific meaningful field that is backed by `mutableStateOf()`.
+
+When a component accepts `MutableState` as a parameter, it gains the ability to change it. This results in the split ownership of the state, and the usage side that owns the state now has no control over how and when it will be changed from within the component’s implementation.
+
+**DON’T**
+```
+@Composable
+fun Scroller(
+    offset: MutableState<Float>
+) {}
+```
+
+**Do (stateless version, if possible):**
+```
+@Composable
+fun Scroller(
+    offset: Float,
+    onOffsetChange: (Float) -> Unit,
+) {}
+```
+
+**Or do (state-based component version, if stateless not possible):**
+```
+class ScrollerState {
+    val offset: Float by mutableStateOf(0f)
+}
+
+@Composable
+fun Scroller(
+    state: ScrollerState
+) {}
+```
+
+### State\<T\> as a parameter
+
+Parameters of type `State<T> `are discouraged since it unnecessarily narrows the type of objects that can be passed in the function. Given `param: State<Float>`, there are two better alternatives available, depending on the use case:
+
+1. `param: Float`. If the parameter doesn’t change often, or is being read immediately in the component (composition), developers can provide just a plain parameter and recompose the component when it changes.
+2. `param: () -> Float`. To delay reading the value until a later time via `param.invoke()`, lambda might be provided as a parameter. This allows the developers of the component to read the value only when/if it is needed and avoid unnecessary work. For example, if the value is only read during drawing operation, [only redraw will occur](https://developer.android.com/jetpack/compose/phases#state-reads). This leaves the flexibility to the user to provide any expression, including the `State<T>`’s read:
+    1. `param = { myState.value }` - read the `State<T>`’s value
+    2. `param = { justValueWithoutState }` - plain value not backed by the `State<T>`
+    3. `param = { myObject.offset }` - user can have a custom state object where the field (e.g. ``offset``) is backed by the `mutableStateOf()`
+
+**DON’T**
+```
+fun Badge(position: State<Dp>) {}
+
+// not possible since only State<T> is allowed
+Badge(position = scrollState.offset) // DOES NOT COMPILE
+```
+
+**Do:**
+```
+fun Badge(position: () -> Dp) {}
+
+// works ok
+Badge(position = { scrollState.offset })
+```
+
+### Slot parameters
+
+#### What are slots
+
+Slot is a `@Composable` lambda parameter that specifies a certain sub hierarchy of the component. Content slot in a Button might look like this:
+```
+@Composable
+fun Button(
+    onClick: () -> Unit,
+    content: @Composable () -> Unit
+) {}
+
+// usage
+Button(onClick = { /* handle the click */}) {
+    Icon(...)
+}
+```
+
+This pattern allows the button to have no opinion on the content, while playing the role of drawing the necessary decoration around, handling clicks and showing ripples.
+
+#### Why slots
+
+It might be tempting to write the button as follows:
+
+**DON’T**
+```
+@Composable
+fun Button(
+    onClick: () -> Unit,
+    text: String? = null,
+    icon: ImageBitmap? = null
+) {}
+```
+
+Where either text or icon or both are present, leaving the button to arrange the show. While it handles basic use cases or sample usages well, it has some fundamental flexibility flaws:
+
+*   **Restricts styling choice:** by using only `String`, Button disallows users to use   `AnnotatedString` or other sources of text information, if required. To provide some styling, Button will have to accept `TextStyle` parameters as well, plus some other ones. This will bloat the API of the button quickly.
+*   **Restricts component choice:** While Button might want to show a text, `String` might not be enough. If a user has their own `MyTextWithLogging()` component, they might want to use it in a button to do some additional logic like logging events and such. This is impossible with the String API unless the user forks the Button.
+*   **Overloads explosion:** If we want some flexibility, for example accepting both ImageBitmap and VectorPainter as icons, we have to provide an overload for that. We can multiply it for every such parameter (`text` being `String` or `AnnotatedString` or `CharSequence`), resulting in the big number of overloads we have to provide in order to cater the users’ use cases.
+*   **Restricts component layout capabilities:** In the example above, the Button is opinionated about the arrangement between text and icon. If a user has a special icon that they want to put with a custom arrangement (e.g. on a button’s text baseline or with 4dp additional padding) - they won’t be able to do it.
+
+Slot APIs in components are free from these problems, as a user can pass any component with any styling in a slot. Slots come with the price of simple usages being a bit more verbose, but this downside disappears quickly as soon as a real-application usage begins.
+
+**Do**
+```
+@Composable
+fun Button(
+    onClick: () -> Unit,
+    text: @Composable () -> Unit,
+    icon: @Composable () -> Unit
+) {}
+```
+
+#### Single “content” slot overloads
+
+For components that are responsible for layouting of multiple slot APIs it accepts, consider providing an overload with a single slot, usually named `content`. This allows for more flexibility on the usage side when needed as it is possible to change the slot layout logic.
+
+**Do**
+```
+@Composable
+fun Button(
+    onClick: () -> Unit,
+    content: @Composable () -> Unit
+) {}
+
+// usage
+Button(onClick = { /* handle the click */}) {
+    Row {
+        Icon(...)
+        Text(...)
+   }
+}
+```
+
+#### Layout strategy scope for slot APIs
+
+If applicable, consider choosing an appropriate layout strategy for the slot lambda. This is especially important for single `content` overloads. In the example above, developers of the Button might notice that most common usage patterns include: single text, single icon, icon and text in a row, text then icon in a row. It might make sense to provide `RowScope` in a content slot, making it easier for the user to use the button
+
+**Do**
+```
+@Composable
+fun Button(
+    onClick: () -> Unit,
+    content: @Composable RowScope.() -> Unit
+) {}
+
+// usage
+Button(onClick = { /* handle the click */ }) { // this: RowScope
+    Icon(...)
+    Text(...)
+}
+```
+
+`ColumnScope` or `BoxScope` are good candidates for other types of layout strategies for components. The author of the component SHOULD always think about what will happen if multiple components are passed in a slot and consider communicating this behaviour to a user via scopes (`RowScope` in a Button example above).
+
+#### Lifecycle expectations for slot parameters
+
+Developers should ensure that the lifecycle of the visible and composed slot parameter composables is either the same as the composable that accepts that slot, or is tied to visibility of the slot in the viewport.
+
+`@Composable` components that are passed in the slot should not be disposed of and composed again on the structural or visual changes in the parent component.
+
+If in need to make structural changes internally that affect slot composables lifecycle, use `remember{}` and `movableContentOf()`
+
+**DON’T**
+```
+@Composable
+fun PreferenceItem(
+    checked: Boolean
+    content: @Composable () -> Unit
+) {
+    // don't: this logic will dispose and compose again from scratch the content() composable on the `checked` boolean change
+    if (checked) {
+        Row {
+            Text("Checked")
+            content()
+        }
+    } else {
+        Column {
+            Text("Unchecked")
+            content()
+        }
+    }
+}
+```
+
+**Do**
+```
+@Composable
+fun PreferenceItem(
+    checked: Boolean
+    content: @Composable () -> Unit
+) {
+    Layout({ Text("Preference item"), content }) {
+        // custom layout that relayouts the same instance of `content`
+        // when `checked` changes
+    }
+}
+```
+
+**Or Do**
+```
+@Composable
+fun PreferenceItem(
+    checked: Boolean,
+    content: @Composable () -> Unit
+) {
+    // this call preserves the lifecycle of `content` between row and column
+    val movableContent = remember(content) { movableContentOf(content)}
+    if (checked) {
+        Row {
+            Text("Checked")
+            movableContent()
+        }
+    } else {
+        Column {
+            Text("Unchecked")
+            movableContent()
+        }
+    }
+}
+```
+
+It is expected that slots that become absent from the UI or leave the view port will be  disposed of and composed again when they become visible:
+
+**Do:**
+```
+@Composable
+fun PreferenceRow(
+    checkedContent: @Composable () -> Unit,
+    checked: Boolean
+) {
+    // since checkedContent() is only visible in the checked state
+    // it is ok for this slot to be disposed when not present
+    // and be composed again when present again
+    if (checked) {
+        Row {
+            Text("Checked")
+            checkedContent()
+        }
+    } else {
+        Column {
+            Text("Unchecked")
+        }
+    }
+}
+```
+
+#### DSL based slots
+
+Avoid DSL based slots and APIs where possible and prefer simple slot `@Composable` lambdas. While giving the developers control over what the user might place in the particular slot, DSL API still restricts the choice of component and layout capabilities. Moreover, the DSL introduces the new API overhead for users to learn and for developers to support.
+
+**DON’T**
+```
+@Composable
+fun TabRow(
+    tabs: TabRowScope.() -> Unit
+) {}
+
+interface TabRowScope {
+    // can be a string
+    fun tab(string: String)
+    // Can be a @composable as well
+    fun tab(tabContent: @Composable () -> Unit)
+}
+```
+
+Instead of DSL, consider relying on plain slots with parameters. This allows the users to operate with tools they already know while not sacrificing any flexibility.
+
+**Do instead:**
+```
+@Composable
+fun TabRow(
+    tabs: @Composable () -> Unit
+) {}
+
+@Composable
+fun Tab(...) {}
+
+// usage
+TabRow {
+    tabsData.forEach { data ->
+        Tab(...)
+    }
+}
+```
+
+DSL for defining content of the component or its children should be perceived as an exception. There are some cases that benefit from the DSL approach, notably when the component wants to lazily show and compose only the subset of children (e.g. `LazyRow`, `LazyColumn`).
+
+**Allowed, since laziness and flexibility with different data types is needed:**
+```
+@Composable
+fun LazyColumn(
+    content: LazyListScope.() -> Unit
+) {}
+
+// usage: DSL is fine since it allows Lazycolumn to lazily compose the subset of children
+LazyColumn {
+    // allow to define different types of children and treat them differently
+    // since sticky header can act both like an item and a sticky header
+    stickyHeader {
+        Text("Header")
+    }
+    items(...) {
+        Text($index)
+    }
+}
+```
+
+Even in such cases like with `LazyColumn` it is possible to define the API structure without DSL, so simple version should be considered first
+
+**Do. Simpler, easier to learn and use API that still provides laziness of children composition:**
+```
+@Composable
+fun HorizontalPager(
+    // pager still lazily composes pages when needed
+    // but the api is simpler and easier to use; no need for DSL
+    pageContent: @Composable (pageIndex: Int) -> Unit
+) {}
+```
+
+## Component-related classes and functions
+
+**Jetpack Compose framework development** MUST follow the rules in this section.
+
+**Library development** SHOULD follow the section below.
+
+**App development** MAY follow the rules below.
+
+### State
+
+For core design practices with state, visit [corresponding section in compose api guidelines](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-api-guidelines.md#compose-api-design-patterns).
+
+### ComponentDefault object
+
+All component default expressions should either be inline or live in the top level object called `ComponentDefaults`, where `Component` is a real component name. Refer to the “[Default expressions](#default-expressions)” section for details.
+
+### ComponentColor/ComponentElevation objects
+
+Consider a simple if-else expression in the default statements for a simple branching logic, or a dedicated `ComponentColor`/`ComponentElevation` class that clearly defines the inputs that a particular Color/Elevation can be reflected against.
+
+There’s a number of ways to provide and/or allow customisation of a certain single type of parameters (e.g. colors, dp) depending on the state of the component (e.g. enabled/disabled, focused/hovered/pressed).
+
+**Do (if color choosing logic is simple)**
+```
+@Composable
+fun Button(
+    onClick: () -> Unit,
+    enabled: Boolean = true,
+    backgroundColor =
+        if (enabled) ButtonDefaults.enabledBackgroundColor
+        else ButtonDefaults.disabledBackgroundColor,
+    elevation =
+        if (enabled) ButtonDefaults.enabledElevation
+        else ButtonDefaults.disabledElevation,
+    content: @Composable RowScope.() -> Unit
+) {}
+```
+
+While this works well, those expressions can grow pretty quickly and pollute the API space. That’s why it might be sensible to isolate this to a domain and parameter specific class.
+
+**Do (if color conditional logic is more complicated)**
+```
+class ButtonColors(
+    backgroundColor: Color,
+    disabledBackgroundColor: Color,
+    contentColor: Color,
+    disabledContentColor: Color
+) {
+    fun backgroundColor(enabled: Boolean): Color { ... }
+
+    fun contentColor(enabled: Boolean): Color { ... }
+}
+
+object ButtonDefaults {
+    // default factory for the class
+    // can be @Composable to access the theme composition locals
+    fun buttonColors(
+        backgroundColor: Color = ...,
+        disabledBackgroundColor: Color = ...,
+        contentColor: Color = ...,
+        disabledContentColor: Color = ...
+    ): ButtonColors { ... }
+}
+
+@Composable
+fun Button(
+    onClick: () -> Unit,
+    enabled: Boolean = true,
+    colors: ButtonColors = ButtonDefaults.buttonColors(),
+    content: @Composable RowScope.() -> Unit
+) {
+    val resolvedBackgroundColor = colors.backgroundColor(enabled)
+}
+```
+
+This way, while not introducing the overhead and complexities of the “styles” pattern, we isolate the configuration of a specific part of the component. Additionally, unlike plain default expression, `ComponentColors` or `ComponentElevation` classes allow for more granular control, where the user can specify the enabled and disabled colors/elevation separately.
+
+**Note:** This approach is different from styles that are discouraged in compose "[no styles](#prefer-multiple-components-over-style-classes)" chapter for rationale. `ComponentColor` and other such classes target a certain type of functionality of the component, allowing for definition of the color against explicit inputs. The instances of this class must be passed as an explicit parameter for the component.
+
+**Note:** While `ComponentColors` and `ComponentElevation` are the most common patterns, there are other component parameters that can be isolated in the similar fashion.
+
+## Documentation for the component
+
+**Jetpack Compose framework development** SHOULD follow the rules in this section below.
+
+**Compose library development** MAY follow the rules in the sections below.
+
+**App development** MAY follow.
+
+Documentation for `@Composable` components should follow JetBrains’s [ktdoc guidelines and syntax](https://kotlinlang.org/docs/kotlin-doc.html#kdoc-syntax). Additionally, documentation must communicate a component's capabilities to developers via multiple channels: description of the component purpose, parameters and expectations about those parameters, usage examples.
+
+### Documentation structure and ordering
+
+Every component should have following documentation structure:
+
+1. One-liner paragraph summarizing the component and what it does.
+2. Paragraphs going more into the detail of components, outlining the capabilities, behavior and might include one or more of:
+    * `@sample` tag providing an example of the usage for this components and its states, default, etc. if you don't have access to `@sample` functionality, consider inline examples in the ktdoc.
+    * `@see` tags pointing to other related apis.
+    * Links to design or other materials to help to use the components to its full potential.
+3. Description for each parameter of the component, starting with `@param paramname`.
+    * Developers might decide to optionally omit the documentation for the trailing `@Composable` `content` lambda as it is always implied to be the main content slot for the component.
+
+### Documentation example
+
+**Do**
+```
+/**
+* Material Design badge box.
+*
+* A badge represents dynamic information such as a number of pending requests in a navigation bar. Badges can be icon only or contain short text.
+*
+* ![Badge image](https://developer.android.com/images/reference/androidx/compose/material3/badge.png)
+*
+* A common use case is to display a badge with navigation bar items.
+* For more information, see [Navigation Bar](https://m3.material.io/components/navigation-bar/overview)
+*
+* A simple icon with badge example looks like:
+* @sample androidx.compose.material3.samples.NavigationBarItemWithBadge
+*
+* @param badge the badge to be displayed - typically a [Badge]
+* @param modifier the [Modifier] to be applied to this BadgedBox
+* @param content the anchor to which this badge will be positioned
+*/
+@ExperimentalMaterial3Api
+@Composable
+fun BadgedBox(
+    badge: @Composable BoxScope.() -> Unit,
+    modifier: Modifier = Modifier,
+    content: @Composable BoxScope.() -> Unit,
+)
+```
+
+## Accessibility of the component
+
+Consider using foundation building blocks like `Modifier.clickable` or `Image` for better accessibility. Those building blocks will provide good defaults when possible, or will explicitly ask for needed information. Accessibility needs to be manually handled when using ui-level blocks, such as `Layout` or `Modifier.pointerInput`. This section contains best practices regarding accessible API design and accessibility implementation tuning.
+
+### Semantics merging
+
+Jetpack Compose uses semantics merging for accessibility purposes. This way, `Button` with the content slot doesn’t have to set the text for accessibility service to announce. Instead, the content’s semantics (`Icon`’s contentDescription or `Text`’s text) will be merged into the button. Refer to the [official documentation](https://developer.android.com/jetpack/compose/semantics#merged-vs-unmerged) for more info.
+
+To manually create a node that will merge all of its children, you can set a `Modifier.semantics(mergeDescendants = true)` modifier to your component. This will force all non-merging children to collect and pass the data to your component, so it will be treated as a single entity. Some foundation-layer modifiers merge descendants by default (example: `Modifier.clickable` or `Modifier.toggleable`).
+
+### Accessibility related parameters
+
+For especially common accessibility needs, developers might want to accept some accessibility-related parameters to let users help to provide better accessibility. This is especially true for leaf components like `Image` or `Icon`. `Image` has a required parameter `contentDescription` to signal to the user the need to pass the necessary description for an image. When developing components, developers need to make a conscious decision on what to build in in the implementation vs what to ask from the user via parameters.
+
+Note that if you follow the normal best practice of providing an ordinary Modifier parameter and put it on your root layout element, this on its own provides a large amount of implicit accessibility customizability.  Because the user of your component can provide their own `Modifier.semantics` which will apply to your component.  In addition, this also provides a way for developers to override a portion of your component’s default semantics: if there are two `SemanticsProperties` with identical keys on one modifier chain, Compose resolves the conflict by having the first one win and the later ones ignored.
+
+Therefore, you don’t need to add a parameter for every possible semantics your component might need.  You should reserve them for especially common cases where it would be inconvenient to write out the `semantics` block every time, or use cases where for some reason the Modifier mechanism doesn’t work (for example, you need to add semantics to an inner child of your component).
+
+### Accessibility tuning
+
+While basic accessibility capabilities will be granted by using foundation layer building blocks, there’s a potential for developers to make the component more accessible.
+
+There are specific semantics expected for individual categories of components: simple components typically require 1-3 semantics, whereas more complex components like text fields, scroll containers or time/date pickers require a very rich set of semantics to function correctly with screenreaders.  When developing a new custom component, first consider which of the existing standard Compose components it’s most similar to, and imitating the semantics provided by that component’s implementation, and the exact foundation building blocks it uses. Go from there to fine-tune and add more semantical actions and/or properties when needed.
+
+## Evolution of the Component APIs
+
+**Jetpack Compose framework development** MUST follow the rules in this section below.
+
+**Compose library development** MUST follow the rules in the sections below.
+
+**App development** MAY follow.
+
+Refer to the [kotlin backwards compatibility](https://kotlinlang.org/docs/jvm-api-guidelines-backward-compatibility.html) guidelines for additional information.
+
+Since every compose is a function, the following rules apply to the component API changes:
+
+*   Parameters of the functions MUST NOT be removed.
+*   Newly added parameter to existing functions MUST have default expressions.
+*   New parameters MAY be added as a last parameter, or second to last in cases of trailing lambdas.
+    *   The developer might decide to put the new parameter closer to other parameters that are semantically close to a new one. Keep in mind that this might break source compatibility if the user uses the component without named parameters.
+
+The workflow to add a new parameter to a component:
+
+1. Create a new overload with the new parameter containing the default.
+2. Deprecate the existing function with `DeprecationLevel.Hidden` for binary compatibility.
+3. Make the deprecated version to call your new one.
+
+**Do:**
+```
+// existing API we want to extend
+@Deprecated(
+    "Maintained for compatibility purposes. Use another overload",
+    level = DeprecationLevel.HIDDEN
+)
+@Composable
+fun Badge(color: Color) {}
+
+// new overload has to be created
+@Composable
+fun Badge(
+    color: Color,
+    // default should be provided
+    secondaryColor: Color = Color.Blue
+) {}
+```
diff --git a/compose/foundation/foundation-layout/api/current.ignore b/compose/foundation/foundation-layout/api/current.ignore
new file mode 100644
index 0000000..0092041
--- /dev/null
+++ b/compose/foundation/foundation-layout/api/current.ignore
@@ -0,0 +1,5 @@
+// Baseline format: 1.0
+RemovedMethod: androidx.compose.foundation.layout.WindowInsets_androidKt#getConsumeWindowInsets(androidx.compose.ui.platform.ComposeView):
+    Removed method androidx.compose.foundation.layout.WindowInsets_androidKt.getConsumeWindowInsets(androidx.compose.ui.platform.ComposeView)
+RemovedMethod: androidx.compose.foundation.layout.WindowInsets_androidKt#setConsumeWindowInsets(androidx.compose.ui.platform.ComposeView, boolean):
+    Removed method androidx.compose.foundation.layout.WindowInsets_androidKt.setConsumeWindowInsets(androidx.compose.ui.platform.ComposeView,boolean)
diff --git a/compose/foundation/foundation-layout/api/current.txt b/compose/foundation/foundation-layout/api/current.txt
index 9d8d6f4..bcc012a 100644
--- a/compose/foundation/foundation-layout/api/current.txt
+++ b/compose/foundation/foundation-layout/api/current.txt
@@ -70,7 +70,7 @@
   }
 
   public final class AspectRatioKt {
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier aspectRatio(androidx.compose.ui.Modifier, float ratio, optional boolean matchHeightConstraintsFirst);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier aspectRatio(androidx.compose.ui.Modifier, @FloatRange(from=0.0, fromInclusive=false) float ratio, optional boolean matchHeightConstraintsFirst);
   }
 
   public final class BoxKt {
@@ -108,7 +108,7 @@
     method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier align(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Horizontal alignment);
     method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignBy(androidx.compose.ui.Modifier, androidx.compose.ui.layout.VerticalAlignmentLine alignmentLine);
     method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignBy(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.Measured,java.lang.Integer> alignmentLineBlock);
-    method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, float weight, optional boolean fill);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, @FloatRange(from=0.0, fromInclusive=false) float weight, optional boolean fill);
   }
 
   @SuppressCompatibility @kotlin.RequiresOptIn(message="The API of this layout is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalLayoutApi {
@@ -197,14 +197,14 @@
     method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignBy(androidx.compose.ui.Modifier, androidx.compose.ui.layout.HorizontalAlignmentLine alignmentLine);
     method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignBy(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.Measured,java.lang.Integer> alignmentLineBlock);
     method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignByBaseline(androidx.compose.ui.Modifier);
-    method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, float weight, optional boolean fill);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, @FloatRange(from=0.0, fromInclusive=false) float weight, optional boolean fill);
   }
 
   public final class SizeKt {
     method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier defaultMinSize(androidx.compose.ui.Modifier, optional float minWidth, optional float minHeight);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxHeight(androidx.compose.ui.Modifier, optional float fraction);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxSize(androidx.compose.ui.Modifier, optional float fraction);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxWidth(androidx.compose.ui.Modifier, optional float fraction);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxSize(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
     method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier height(androidx.compose.ui.Modifier, float height);
     method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier heightIn(androidx.compose.ui.Modifier, optional float min, optional float max);
     method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier requiredHeight(androidx.compose.ui.Modifier, float height);
@@ -318,7 +318,7 @@
     method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static boolean getAreSystemBarsVisible(androidx.compose.foundation.layout.WindowInsets.Companion);
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getCaptionBar(androidx.compose.foundation.layout.WindowInsets.Companion);
     method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getCaptionBarIgnoringVisibility(androidx.compose.foundation.layout.WindowInsets.Companion);
-    method public static boolean getConsumeWindowInsets(androidx.compose.ui.platform.ComposeView);
+    method public static boolean getConsumeWindowInsets(androidx.compose.ui.platform.AbstractComposeView);
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getDisplayCutout(androidx.compose.foundation.layout.WindowInsets.Companion);
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getIme(androidx.compose.foundation.layout.WindowInsets.Companion);
     method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getImeAnimationSource(androidx.compose.foundation.layout.WindowInsets.Companion);
@@ -340,7 +340,7 @@
     method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static boolean isCaptionBarVisible(androidx.compose.foundation.layout.WindowInsets.Companion);
     method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static boolean isImeVisible(androidx.compose.foundation.layout.WindowInsets.Companion);
     method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static boolean isTappableElementVisible(androidx.compose.foundation.layout.WindowInsets.Companion);
-    method public static void setConsumeWindowInsets(androidx.compose.ui.platform.ComposeView, boolean);
+    method public static void setConsumeWindowInsets(androidx.compose.ui.platform.AbstractComposeView, boolean);
   }
 
 }
diff --git a/compose/foundation/foundation-layout/api/restricted_current.ignore b/compose/foundation/foundation-layout/api/restricted_current.ignore
new file mode 100644
index 0000000..0092041
--- /dev/null
+++ b/compose/foundation/foundation-layout/api/restricted_current.ignore
@@ -0,0 +1,5 @@
+// Baseline format: 1.0
+RemovedMethod: androidx.compose.foundation.layout.WindowInsets_androidKt#getConsumeWindowInsets(androidx.compose.ui.platform.ComposeView):
+    Removed method androidx.compose.foundation.layout.WindowInsets_androidKt.getConsumeWindowInsets(androidx.compose.ui.platform.ComposeView)
+RemovedMethod: androidx.compose.foundation.layout.WindowInsets_androidKt#setConsumeWindowInsets(androidx.compose.ui.platform.ComposeView, boolean):
+    Removed method androidx.compose.foundation.layout.WindowInsets_androidKt.setConsumeWindowInsets(androidx.compose.ui.platform.ComposeView,boolean)
diff --git a/compose/foundation/foundation-layout/api/restricted_current.txt b/compose/foundation/foundation-layout/api/restricted_current.txt
index 875c895..0135061 100644
--- a/compose/foundation/foundation-layout/api/restricted_current.txt
+++ b/compose/foundation/foundation-layout/api/restricted_current.txt
@@ -70,7 +70,7 @@
   }
 
   public final class AspectRatioKt {
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier aspectRatio(androidx.compose.ui.Modifier, float ratio, optional boolean matchHeightConstraintsFirst);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier aspectRatio(androidx.compose.ui.Modifier, @FloatRange(from=0.0, fromInclusive=false) float ratio, optional boolean matchHeightConstraintsFirst);
   }
 
   public final class BoxKt {
@@ -111,7 +111,7 @@
     method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier align(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Horizontal alignment);
     method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignBy(androidx.compose.ui.Modifier, androidx.compose.ui.layout.VerticalAlignmentLine alignmentLine);
     method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignBy(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.Measured,java.lang.Integer> alignmentLineBlock);
-    method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, float weight, optional boolean fill);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, @FloatRange(from=0.0, fromInclusive=false) float weight, optional boolean fill);
   }
 
   @SuppressCompatibility @kotlin.RequiresOptIn(message="The API of this layout is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalLayoutApi {
@@ -204,14 +204,14 @@
     method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignBy(androidx.compose.ui.Modifier, androidx.compose.ui.layout.HorizontalAlignmentLine alignmentLine);
     method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignBy(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.Measured,java.lang.Integer> alignmentLineBlock);
     method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignByBaseline(androidx.compose.ui.Modifier);
-    method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, float weight, optional boolean fill);
+    method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, @FloatRange(from=0.0, fromInclusive=false) float weight, optional boolean fill);
   }
 
   public final class SizeKt {
     method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier defaultMinSize(androidx.compose.ui.Modifier, optional float minWidth, optional float minHeight);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxHeight(androidx.compose.ui.Modifier, optional float fraction);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxSize(androidx.compose.ui.Modifier, optional float fraction);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxWidth(androidx.compose.ui.Modifier, optional float fraction);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxSize(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier fillMaxWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
     method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier height(androidx.compose.ui.Modifier, float height);
     method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier heightIn(androidx.compose.ui.Modifier, optional float min, optional float max);
     method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier requiredHeight(androidx.compose.ui.Modifier, float height);
@@ -325,7 +325,7 @@
     method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static boolean getAreSystemBarsVisible(androidx.compose.foundation.layout.WindowInsets.Companion);
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getCaptionBar(androidx.compose.foundation.layout.WindowInsets.Companion);
     method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getCaptionBarIgnoringVisibility(androidx.compose.foundation.layout.WindowInsets.Companion);
-    method public static boolean getConsumeWindowInsets(androidx.compose.ui.platform.ComposeView);
+    method public static boolean getConsumeWindowInsets(androidx.compose.ui.platform.AbstractComposeView);
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getDisplayCutout(androidx.compose.foundation.layout.WindowInsets.Companion);
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getIme(androidx.compose.foundation.layout.WindowInsets.Companion);
     method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static androidx.compose.foundation.layout.WindowInsets getImeAnimationSource(androidx.compose.foundation.layout.WindowInsets.Companion);
@@ -347,7 +347,7 @@
     method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static boolean isCaptionBarVisible(androidx.compose.foundation.layout.WindowInsets.Companion);
     method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static boolean isImeVisible(androidx.compose.foundation.layout.WindowInsets.Companion);
     method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static boolean isTappableElementVisible(androidx.compose.foundation.layout.WindowInsets.Companion);
-    method public static void setConsumeWindowInsets(androidx.compose.ui.platform.ComposeView, boolean);
+    method public static void setConsumeWindowInsets(androidx.compose.ui.platform.AbstractComposeView, boolean);
   }
 
 }
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/IntrinsicTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/IntrinsicTest.kt
index dcc13ab..09bf890 100644
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/IntrinsicTest.kt
+++ b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/IntrinsicTest.kt
@@ -49,6 +49,76 @@
 @RunWith(AndroidJUnit4::class)
 class IntrinsicTest : LayoutTest() {
     @Test
+    fun testMaxIntrinsic_HandleNegative() = with(density) {
+        val positionedLatch = CountDownLatch(2)
+        val size = Ref<IntSize>()
+        val position = Ref<Offset>()
+        val sizeTwo = Ref<IntSize>()
+        val positionTwo = Ref<Offset>()
+        val measurePolicy = object : MeasurePolicy {
+            override fun MeasureScope.measure(
+                measurables: List<Measurable>,
+                constraints: Constraints
+            ): MeasureResult {
+                return layout(0, 0) {}
+            }
+
+            override fun IntrinsicMeasureScope.minIntrinsicHeight(
+                measurables: List<IntrinsicMeasurable>,
+                width: Int
+            ): Int {
+                return -1
+            }
+
+            override fun IntrinsicMeasureScope.minIntrinsicWidth(
+                measurables: List<IntrinsicMeasurable>,
+                height: Int
+            ): Int {
+                return -1
+            }
+
+            override fun IntrinsicMeasureScope.maxIntrinsicHeight(
+                measurables: List<IntrinsicMeasurable>,
+                width: Int
+            ): Int {
+                return -1
+            }
+
+            override fun IntrinsicMeasureScope.maxIntrinsicWidth(
+                measurables: List<IntrinsicMeasurable>,
+                height: Int
+            ): Int {
+                return -1
+            }
+        }
+        show {
+            Column {
+                Layout(modifier = Modifier
+                    .width(IntrinsicSize.Min)
+                    .height(IntrinsicSize.Min)
+                    .saveLayoutInfo(
+                        size = size,
+                        position = position,
+                        positionedLatch = positionedLatch
+                    ), measurePolicy = measurePolicy)
+                Layout(modifier = Modifier
+                    .width(IntrinsicSize.Max)
+                    .height(IntrinsicSize.Max)
+                    .saveLayoutInfo(
+                        size = sizeTwo,
+                        position = positionTwo,
+                        positionedLatch = positionedLatch
+                    ), measurePolicy = measurePolicy)
+            }
+        }
+        assertTrue(positionedLatch.await(1, TimeUnit.SECONDS))
+        assertEquals(IntSize(0.dp.roundToPx(), 0.dp.roundToPx()), size.value)
+        assertEquals(IntSize(0.dp.roundToPx(), 0.dp.roundToPx()), sizeTwo.value)
+        assertEquals(Offset(0f, 0f), position.value)
+        assertEquals(Offset(0f, 0f), positionTwo.value)
+    }
+
+    @Test
     fun testMinIntrinsicWidth() = with(density) {
         val positionedLatch = CountDownLatch(2)
         val minIntrinsicWidthSize = Ref<IntSize>()
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/RowColumnTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/RowColumnTest.kt
index fd13029..91c442b 100644
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/RowColumnTest.kt
+++ b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/RowColumnTest.kt
@@ -3838,6 +3838,53 @@
     }
 
     @Test
+    fun testRow_withNoItems_hasCorrectIntrinsicMeasurements() = with(density) {
+        testIntrinsics(
+            @Composable {
+                Row(
+                    Modifier.width(IntrinsicSize.Max).height(IntrinsicSize.Max),
+                    horizontalArrangement = Arrangement.spacedBy(
+                        48.dp
+                    ),
+                ) { }
+            },
+            @Composable {
+                Row(
+                    Modifier.width(IntrinsicSize.Min).height(IntrinsicSize.Min),
+                    horizontalArrangement = Arrangement.spacedBy(
+                        48.dp
+                    ),
+                ) { }
+            },
+            @Composable {
+                Column(
+                    Modifier.width(IntrinsicSize.Max).height(IntrinsicSize.Max),
+                    verticalArrangement = Arrangement.spacedBy(
+                        48.dp
+                    ),
+                ) { }
+            },
+            @Composable {
+                Column(
+                    Modifier.width(IntrinsicSize.Min).height(IntrinsicSize.Min),
+                    verticalArrangement = Arrangement.spacedBy(
+                        48.dp
+                    ),
+                ) { }
+            },
+        ) { minIntrinsicWidth, minIntrinsicHeight, maxIntrinsicWidth, maxIntrinsicHeight ->
+            // Min width.
+            assertEquals(0.toDp().roundToPx(), minIntrinsicWidth(0.toDp().roundToPx()))
+            // Min height.
+            assertEquals(0.toDp().roundToPx(), minIntrinsicHeight(0.toDp().roundToPx()))
+            // Max width.
+            assertEquals(0.toDp().roundToPx(), maxIntrinsicWidth(0.toDp().roundToPx()))
+            // Max height.
+            assertEquals(0.toDp().roundToPx(), maxIntrinsicHeight(0.toDp().roundToPx()))
+        }
+    }
+
+    @Test
     fun testRow_withWeightChildren_hasCorrectIntrinsicMeasurements() = with(density) {
         testIntrinsics(
             @Composable {
diff --git a/compose/foundation/foundation-layout/src/androidMain/kotlin/androidx/compose/foundation/layout/WindowInsets.android.kt b/compose/foundation/foundation-layout/src/androidMain/kotlin/androidx/compose/foundation/layout/WindowInsets.android.kt
index 8cad072..ed81e0f 100644
--- a/compose/foundation/foundation-layout/src/androidMain/kotlin/androidx/compose/foundation/layout/WindowInsets.android.kt
+++ b/compose/foundation/foundation-layout/src/androidMain/kotlin/androidx/compose/foundation/layout/WindowInsets.android.kt
@@ -28,6 +28,7 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.snapshots.Snapshot
 import androidx.compose.ui.R
+import androidx.compose.ui.platform.AbstractComposeView
 import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.platform.LocalView
 import androidx.compose.ui.unit.Density
@@ -111,7 +112,7 @@
  *
  * This property should be set prior to first composition.
  */
-var ComposeView.consumeWindowInsets: Boolean
+var AbstractComposeView.consumeWindowInsets: Boolean
     get() = getTag(R.id.consume_window_insets_tag) as? Boolean ?: true
     set(value) {
         setTag(R.id.consume_window_insets_tag, value)
@@ -469,7 +470,8 @@
     )
 
     /**
-     * `true` unless the `ComposeView` [ComposeView.consumeWindowInsets] is set to `false`.
+     * `true` unless the `AbstractComposeView` [AbstractComposeView.consumeWindowInsets] is set to
+     * `false`.
      */
     val consumes = (view.parent as? View)?.getTag(R.id.consume_window_insets_tag)
         as? Boolean ?: true
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AspectRatio.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AspectRatio.kt
index 03ba1e45..bcfcde8 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AspectRatio.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AspectRatio.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.foundation.layout
 
+import androidx.annotation.FloatRange
 import androidx.compose.runtime.Stable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.IntrinsicMeasurable
@@ -52,7 +53,7 @@
  */
 @Stable
 fun Modifier.aspectRatio(
-    /*@FloatRange(from = 0.0, fromInclusive = false)*/
+    @FloatRange(from = 0.0, fromInclusive = false)
     ratio: Float,
     matchHeightConstraintsFirst: Boolean = false
 ) = this.then(
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Column.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Column.kt
index 10f1128..c243943 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Column.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Column.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.foundation.layout
 
+import androidx.annotation.FloatRange
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
@@ -139,7 +140,7 @@
      */
     @Stable
     fun Modifier.weight(
-        /*@FloatRange(from = 0.0, fromInclusive = false)*/
+        @FloatRange(from = 0.0, fromInclusive = false)
         weight: Float,
         fill: Boolean = true
     ): Modifier
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Intrinsic.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Intrinsic.kt
index b957ba4..37fe411 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Intrinsic.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Intrinsic.kt
@@ -162,11 +162,12 @@
         measurable: Measurable,
         constraints: Constraints
     ): Constraints {
-        val measuredWidth = if (width == IntrinsicSize.Min) {
+        var measuredWidth = if (width == IntrinsicSize.Min) {
             measurable.minIntrinsicWidth(constraints.maxHeight)
         } else {
             measurable.maxIntrinsicWidth(constraints.maxHeight)
         }
+        if (measuredWidth < 0) { measuredWidth = 0 }
         return Constraints.fixedWidth(measuredWidth)
     }
 
@@ -217,11 +218,12 @@
         measurable: Measurable,
         constraints: Constraints
     ): Constraints {
-        val measuredHeight = if (height == IntrinsicSize.Min) {
+        var measuredHeight = if (height == IntrinsicSize.Min) {
             measurable.minIntrinsicHeight(constraints.maxWidth)
         } else {
             measurable.maxIntrinsicHeight(constraints.maxWidth)
         }
+        if (measuredHeight < 0) { measuredHeight = 0 }
         return Constraints.fixedHeight(measuredHeight)
     }
 
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Row.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Row.kt
index 02bd8ec..c9b7612 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Row.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Row.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.foundation.layout
 
+import androidx.annotation.FloatRange
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
@@ -140,7 +141,7 @@
      */
     @Stable
     fun Modifier.weight(
-        /*@FloatRange(from = 0.0, fromInclusive = false)*/
+        @FloatRange(from = 0.0, fromInclusive = false)
         weight: Float,
         fill: Boolean = true
     ): Modifier
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnImpl.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnImpl.kt
index ce9028a..39edd26 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnImpl.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnImpl.kt
@@ -557,6 +557,7 @@
     crossAxisAvailable: Int,
     mainAxisSpacing: Int
 ): Int {
+    if (children.isEmpty()) return 0
     var weightUnitSpace = 0
     var fixedSpace = 0
     var totalWeight = 0f
@@ -581,6 +582,7 @@
     mainAxisAvailable: Int,
     mainAxisSpacing: Int
 ): Int {
+    if (children.isEmpty()) return 0
     var fixedSpace = min((children.size - 1) * mainAxisSpacing, mainAxisAvailable)
     var crossAxisMax = 0
     var totalWeight = 0f
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Size.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Size.kt
index b0a80c7..6e8b669 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Size.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Size.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.foundation.layout
 
+import androidx.annotation.FloatRange
 import androidx.compose.runtime.Stable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -455,7 +456,7 @@
  * @sample androidx.compose.foundation.layout.samples.FillHalfWidthModifier
  */
 @Stable
-fun Modifier.fillMaxWidth(/*@FloatRange(from = 0.0, to = 1.0)*/ fraction: Float = 1f) =
+fun Modifier.fillMaxWidth(@FloatRange(from = 0.0, to = 1.0) fraction: Float = 1f) =
     this.then(if (fraction == 1f) FillWholeMaxWidth else FillElement.width(fraction))
 
 private val FillWholeMaxWidth = FillElement.width(1f)
@@ -475,7 +476,7 @@
  * @sample androidx.compose.foundation.layout.samples.FillHalfHeightModifier
  */
 @Stable
-fun Modifier.fillMaxHeight(/*@FloatRange(from = 0.0, to = 1.0)*/ fraction: Float = 1f) =
+fun Modifier.fillMaxHeight(@FloatRange(from = 0.0, to = 1.0) fraction: Float = 1f) =
     this.then(if (fraction == 1f) FillWholeMaxHeight else FillElement.height(fraction))
 
 private val FillWholeMaxHeight = FillElement.height(1f)
@@ -499,7 +500,7 @@
  * @sample androidx.compose.foundation.layout.samples.FillHalfSizeModifier
  */
 @Stable
-fun Modifier.fillMaxSize(/*@FloatRange(from = 0.0, to = 1.0)*/ fraction: Float = 1f) =
+fun Modifier.fillMaxSize(@FloatRange(from = 0.0, to = 1.0) fraction: Float = 1f) =
     this.then(if (fraction == 1f) FillWholeMaxSize else FillElement.size(fraction))
 
 private val FillWholeMaxSize = FillElement.size(1f)
@@ -916,7 +917,8 @@
 
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
-        if (javaClass != other?.javaClass) return false
+        if (other === null) return false
+        if (this::class != other::class) return false
 
         other as WrapContentElement
 
diff --git a/compose/foundation/foundation/api/current.txt b/compose/foundation/foundation/api/current.txt
index 77d79cc..3b0d6b1 100644
--- a/compose/foundation/foundation/api/current.txt
+++ b/compose/foundation/foundation/api/current.txt
@@ -2,7 +2,7 @@
 package androidx.compose.foundation {
 
   public final class BackgroundKt {
-    method public static androidx.compose.ui.Modifier background(androidx.compose.ui.Modifier, androidx.compose.ui.graphics.Brush brush, optional androidx.compose.ui.graphics.Shape shape, optional float alpha);
+    method public static androidx.compose.ui.Modifier background(androidx.compose.ui.Modifier, androidx.compose.ui.graphics.Brush brush, optional androidx.compose.ui.graphics.Shape shape, optional @FloatRange(from=0.0, to=1.0) float alpha);
     method public static androidx.compose.ui.Modifier background(androidx.compose.ui.Modifier, long color, optional androidx.compose.ui.graphics.Shape shape);
   }
 
@@ -193,7 +193,7 @@
 
   public final class ProgressSemanticsKt {
     method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier progressSemantics(androidx.compose.ui.Modifier);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier progressSemantics(androidx.compose.ui.Modifier, float value, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier progressSemantics(androidx.compose.ui.Modifier, float value, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional @IntRange(from=0L) int steps);
   }
 
   public final class ScrollKt {
@@ -257,7 +257,7 @@
     method public T getCurrentValue();
     method public float getLastVelocity();
     method public float getOffset();
-    method public float getProgress();
+    method @FloatRange(from=0.0, to=1.0) public float getProgress();
     method public T getTargetValue();
     method public boolean isAnimationRunning();
     method public float requireOffset();
@@ -269,7 +269,7 @@
     property public final boolean isAnimationRunning;
     property public final float lastVelocity;
     property public final float offset;
-    property public final float progress;
+    property @FloatRange(from=0.0, to=1.0) public final float progress;
     property public final T targetValue;
     field public static final androidx.compose.foundation.gestures.AnchoredDraggableState.Companion Companion;
   }
@@ -609,9 +609,9 @@
 
   @androidx.compose.foundation.lazy.LazyScopeMarker @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface LazyItemScope {
     method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public androidx.compose.ui.Modifier animateItemPlacement(androidx.compose.ui.Modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec);
-    method public androidx.compose.ui.Modifier fillParentMaxHeight(androidx.compose.ui.Modifier, optional float fraction);
-    method public androidx.compose.ui.Modifier fillParentMaxSize(androidx.compose.ui.Modifier, optional float fraction);
-    method public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier, optional float fraction);
+    method public androidx.compose.ui.Modifier fillParentMaxHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+    method public androidx.compose.ui.Modifier fillParentMaxSize(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+    method public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
   }
 
   public interface LazyListItemInfo {
@@ -660,7 +660,7 @@
 
   @androidx.compose.runtime.Stable public final class LazyListState implements androidx.compose.foundation.gestures.ScrollableState {
     ctor public LazyListState(optional int firstVisibleItemIndex, optional int firstVisibleItemScrollOffset);
-    method public suspend Object? animateScrollToItem(int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public suspend Object? animateScrollToItem(@IntRange(from=0L) int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public float dispatchRawDelta(float delta);
     method public int getFirstVisibleItemIndex();
     method public int getFirstVisibleItemScrollOffset();
@@ -668,7 +668,7 @@
     method public androidx.compose.foundation.lazy.LazyListLayoutInfo getLayoutInfo();
     method public boolean isScrollInProgress();
     method public suspend Object? scroll(androidx.compose.foundation.MutatePriority scrollPriority, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
-    method public suspend Object? scrollToItem(int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public suspend Object? scrollToItem(@IntRange(from=0L) int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
     property public boolean canScrollBackward;
     property public boolean canScrollForward;
     property public final int firstVisibleItemIndex;
@@ -801,7 +801,7 @@
 
   @androidx.compose.runtime.Stable public final class LazyGridState implements androidx.compose.foundation.gestures.ScrollableState {
     ctor public LazyGridState(optional int firstVisibleItemIndex, optional int firstVisibleItemScrollOffset);
-    method public suspend Object? animateScrollToItem(int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public suspend Object? animateScrollToItem(@IntRange(from=0L) int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public float dispatchRawDelta(float delta);
     method public int getFirstVisibleItemIndex();
     method public int getFirstVisibleItemScrollOffset();
@@ -809,7 +809,7 @@
     method public androidx.compose.foundation.lazy.grid.LazyGridLayoutInfo getLayoutInfo();
     method public boolean isScrollInProgress();
     method public suspend Object? scroll(androidx.compose.foundation.MutatePriority scrollPriority, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
-    method public suspend Object? scrollToItem(int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public suspend Object? scrollToItem(@IntRange(from=0L) int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
     property public boolean canScrollBackward;
     property public boolean canScrollForward;
     property public final int firstVisibleItemIndex;
@@ -1055,6 +1055,13 @@
 
 package androidx.compose.foundation.pager {
 
+  @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public sealed interface PageInfo {
+    method public int getIndex();
+    method public int getOffset();
+    property public abstract int index;
+    property public abstract int offset;
+  }
+
   @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public interface PageSize {
     method public int calculateMainAxisPageSize(androidx.compose.ui.unit.Density, int availableSpace, int pageSpacing);
   }
@@ -1084,6 +1091,29 @@
     method @Deprecated @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void VerticalPager(int pageCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.pager.PagerState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.pager.PageSize pageSize, optional int beyondBoundsPageCount, optional float pageSpacing, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, optional androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> pageContent);
   }
 
+  @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public sealed interface PagerLayoutInfo {
+    method public int getAfterContentPadding();
+    method public int getBeforeContentPadding();
+    method public androidx.compose.foundation.gestures.Orientation getOrientation();
+    method public int getPageSize();
+    method public int getPageSpacing();
+    method public boolean getReverseLayout();
+    method public int getViewportEndOffset();
+    method public long getViewportSize();
+    method public int getViewportStartOffset();
+    method public java.util.List<androidx.compose.foundation.pager.PageInfo> getVisiblePagesInfo();
+    property public abstract int afterContentPadding;
+    property public abstract int beforeContentPadding;
+    property public abstract androidx.compose.foundation.gestures.Orientation orientation;
+    property public abstract int pageSize;
+    property public abstract int pageSpacing;
+    property public abstract boolean reverseLayout;
+    property public abstract int viewportEndOffset;
+    property public abstract long viewportSize;
+    property public abstract int viewportStartOffset;
+    property public abstract java.util.List<androidx.compose.foundation.pager.PageInfo> visiblePagesInfo;
+  }
+
   @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public sealed interface PagerScope {
   }
 
@@ -1107,6 +1137,7 @@
     method public final int getInitialPage();
     method public final float getInitialPageOffsetFraction();
     method public final androidx.compose.foundation.interaction.InteractionSource getInteractionSource();
+    method public final androidx.compose.foundation.pager.PagerLayoutInfo getLayoutInfo();
     method public final float getOffsetFractionForPage(int page);
     method public abstract int getPageCount();
     method public final int getSettledPage();
@@ -1122,6 +1153,7 @@
     property public final float initialPageOffsetFraction;
     property public final androidx.compose.foundation.interaction.InteractionSource interactionSource;
     property public boolean isScrollInProgress;
+    property public final androidx.compose.foundation.pager.PagerLayoutInfo layoutInfo;
     property public abstract int pageCount;
     property public final int settledPage;
     property public final int targetPage;
@@ -1191,7 +1223,7 @@
     method public static androidx.compose.foundation.shape.AbsoluteCutCornerShape AbsoluteCutCornerShape(optional float topLeft, optional float topRight, optional float bottomRight, optional float bottomLeft);
     method public static androidx.compose.foundation.shape.AbsoluteCutCornerShape AbsoluteCutCornerShape(optional float topLeft, optional float topRight, optional float bottomRight, optional float bottomLeft);
     method public static androidx.compose.foundation.shape.AbsoluteCutCornerShape AbsoluteCutCornerShape(int percent);
-    method public static androidx.compose.foundation.shape.AbsoluteCutCornerShape AbsoluteCutCornerShape(optional int topLeftPercent, optional int topRightPercent, optional int bottomRightPercent, optional int bottomLeftPercent);
+    method public static androidx.compose.foundation.shape.AbsoluteCutCornerShape AbsoluteCutCornerShape(optional @IntRange(from=0L, to=100L) int topLeftPercent, optional @IntRange(from=0L, to=100L) int topRightPercent, optional @IntRange(from=0L, to=100L) int bottomRightPercent, optional @IntRange(from=0L, to=100L) int bottomLeftPercent);
   }
 
   public final class AbsoluteRoundedCornerShape extends androidx.compose.foundation.shape.CornerBasedShape {
@@ -1207,7 +1239,7 @@
     method public static androidx.compose.foundation.shape.AbsoluteRoundedCornerShape AbsoluteRoundedCornerShape(optional float topLeft, optional float topRight, optional float bottomRight, optional float bottomLeft);
     method public static androidx.compose.foundation.shape.AbsoluteRoundedCornerShape AbsoluteRoundedCornerShape(optional float topLeft, optional float topRight, optional float bottomRight, optional float bottomLeft);
     method public static androidx.compose.foundation.shape.AbsoluteRoundedCornerShape AbsoluteRoundedCornerShape(int percent);
-    method public static androidx.compose.foundation.shape.AbsoluteRoundedCornerShape AbsoluteRoundedCornerShape(optional int topLeftPercent, optional int topRightPercent, optional int bottomRightPercent, optional int bottomLeftPercent);
+    method public static androidx.compose.foundation.shape.AbsoluteRoundedCornerShape AbsoluteRoundedCornerShape(optional @IntRange(from=0L, to=100L) int topLeftPercent, optional @IntRange(from=0L, to=100L) int topRightPercent, optional @IntRange(from=0L, to=100L) int bottomRightPercent, optional @IntRange(from=0L, to=100L) int bottomLeftPercent);
   }
 
   public abstract class CornerBasedShape implements androidx.compose.ui.graphics.Shape {
@@ -1233,7 +1265,7 @@
   public final class CornerSizeKt {
     method @androidx.compose.runtime.Stable public static androidx.compose.foundation.shape.CornerSize CornerSize(float size);
     method @androidx.compose.runtime.Stable public static androidx.compose.foundation.shape.CornerSize CornerSize(float size);
-    method @androidx.compose.runtime.Stable public static androidx.compose.foundation.shape.CornerSize CornerSize(int percent);
+    method @androidx.compose.runtime.Stable public static androidx.compose.foundation.shape.CornerSize CornerSize(@IntRange(from=0L, to=100L) int percent);
     method public static androidx.compose.foundation.shape.CornerSize getZeroCornerSize();
     property public static final androidx.compose.foundation.shape.CornerSize ZeroCornerSize;
   }
@@ -1251,7 +1283,7 @@
     method public static androidx.compose.foundation.shape.CutCornerShape CutCornerShape(optional float topStart, optional float topEnd, optional float bottomEnd, optional float bottomStart);
     method public static androidx.compose.foundation.shape.CutCornerShape CutCornerShape(optional float topStart, optional float topEnd, optional float bottomEnd, optional float bottomStart);
     method public static androidx.compose.foundation.shape.CutCornerShape CutCornerShape(int percent);
-    method public static androidx.compose.foundation.shape.CutCornerShape CutCornerShape(optional int topStartPercent, optional int topEndPercent, optional int bottomEndPercent, optional int bottomStartPercent);
+    method public static androidx.compose.foundation.shape.CutCornerShape CutCornerShape(optional @IntRange(from=0L, to=100L) int topStartPercent, optional @IntRange(from=0L, to=100L) int topEndPercent, optional @IntRange(from=0L, to=100L) int bottomEndPercent, optional @IntRange(from=0L, to=100L) int bottomStartPercent);
   }
 
   public final class GenericShape implements androidx.compose.ui.graphics.Shape {
@@ -1272,7 +1304,7 @@
     method public static androidx.compose.foundation.shape.RoundedCornerShape RoundedCornerShape(optional float topStart, optional float topEnd, optional float bottomEnd, optional float bottomStart);
     method public static androidx.compose.foundation.shape.RoundedCornerShape RoundedCornerShape(optional float topStart, optional float topEnd, optional float bottomEnd, optional float bottomStart);
     method public static androidx.compose.foundation.shape.RoundedCornerShape RoundedCornerShape(int percent);
-    method public static androidx.compose.foundation.shape.RoundedCornerShape RoundedCornerShape(optional int topStartPercent, optional int topEndPercent, optional int bottomEndPercent, optional int bottomStartPercent);
+    method public static androidx.compose.foundation.shape.RoundedCornerShape RoundedCornerShape(optional @IntRange(from=0L, to=100L) int topStartPercent, optional @IntRange(from=0L, to=100L) int topEndPercent, optional @IntRange(from=0L, to=100L) int bottomEndPercent, optional @IntRange(from=0L, to=100L) int bottomStartPercent);
     method public static androidx.compose.foundation.shape.RoundedCornerShape getCircleShape();
     property public static final androidx.compose.foundation.shape.RoundedCornerShape CircleShape;
   }
diff --git a/compose/foundation/foundation/api/restricted_current.txt b/compose/foundation/foundation/api/restricted_current.txt
index b676dff..1e5f27a 100644
--- a/compose/foundation/foundation/api/restricted_current.txt
+++ b/compose/foundation/foundation/api/restricted_current.txt
@@ -2,7 +2,7 @@
 package androidx.compose.foundation {
 
   public final class BackgroundKt {
-    method public static androidx.compose.ui.Modifier background(androidx.compose.ui.Modifier, androidx.compose.ui.graphics.Brush brush, optional androidx.compose.ui.graphics.Shape shape, optional float alpha);
+    method public static androidx.compose.ui.Modifier background(androidx.compose.ui.Modifier, androidx.compose.ui.graphics.Brush brush, optional androidx.compose.ui.graphics.Shape shape, optional @FloatRange(from=0.0, to=1.0) float alpha);
     method public static androidx.compose.ui.Modifier background(androidx.compose.ui.Modifier, long color, optional androidx.compose.ui.graphics.Shape shape);
   }
 
@@ -195,7 +195,7 @@
 
   public final class ProgressSemanticsKt {
     method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier progressSemantics(androidx.compose.ui.Modifier);
-    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier progressSemantics(androidx.compose.ui.Modifier, float value, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps);
+    method @androidx.compose.runtime.Stable public static androidx.compose.ui.Modifier progressSemantics(androidx.compose.ui.Modifier, float value, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional @IntRange(from=0L) int steps);
   }
 
   public final class ScrollKt {
@@ -259,7 +259,7 @@
     method public T getCurrentValue();
     method public float getLastVelocity();
     method public float getOffset();
-    method public float getProgress();
+    method @FloatRange(from=0.0, to=1.0) public float getProgress();
     method public T getTargetValue();
     method public boolean isAnimationRunning();
     method public float requireOffset();
@@ -271,7 +271,7 @@
     property public final boolean isAnimationRunning;
     property public final float lastVelocity;
     property public final float offset;
-    property public final float progress;
+    property @FloatRange(from=0.0, to=1.0) public final float progress;
     property public final T targetValue;
     field public static final androidx.compose.foundation.gestures.AnchoredDraggableState.Companion Companion;
   }
@@ -611,9 +611,9 @@
 
   @androidx.compose.foundation.lazy.LazyScopeMarker @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface LazyItemScope {
     method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public androidx.compose.ui.Modifier animateItemPlacement(androidx.compose.ui.Modifier, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec);
-    method public androidx.compose.ui.Modifier fillParentMaxHeight(androidx.compose.ui.Modifier, optional float fraction);
-    method public androidx.compose.ui.Modifier fillParentMaxSize(androidx.compose.ui.Modifier, optional float fraction);
-    method public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier, optional float fraction);
+    method public androidx.compose.ui.Modifier fillParentMaxHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+    method public androidx.compose.ui.Modifier fillParentMaxSize(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+    method public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
   }
 
   public interface LazyListItemInfo {
@@ -662,7 +662,7 @@
 
   @androidx.compose.runtime.Stable public final class LazyListState implements androidx.compose.foundation.gestures.ScrollableState {
     ctor public LazyListState(optional int firstVisibleItemIndex, optional int firstVisibleItemScrollOffset);
-    method public suspend Object? animateScrollToItem(int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public suspend Object? animateScrollToItem(@IntRange(from=0L) int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public float dispatchRawDelta(float delta);
     method public int getFirstVisibleItemIndex();
     method public int getFirstVisibleItemScrollOffset();
@@ -670,7 +670,7 @@
     method public androidx.compose.foundation.lazy.LazyListLayoutInfo getLayoutInfo();
     method public boolean isScrollInProgress();
     method public suspend Object? scroll(androidx.compose.foundation.MutatePriority scrollPriority, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
-    method public suspend Object? scrollToItem(int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public suspend Object? scrollToItem(@IntRange(from=0L) int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
     property public boolean canScrollBackward;
     property public boolean canScrollForward;
     property public final int firstVisibleItemIndex;
@@ -803,7 +803,7 @@
 
   @androidx.compose.runtime.Stable public final class LazyGridState implements androidx.compose.foundation.gestures.ScrollableState {
     ctor public LazyGridState(optional int firstVisibleItemIndex, optional int firstVisibleItemScrollOffset);
-    method public suspend Object? animateScrollToItem(int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public suspend Object? animateScrollToItem(@IntRange(from=0L) int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public float dispatchRawDelta(float delta);
     method public int getFirstVisibleItemIndex();
     method public int getFirstVisibleItemScrollOffset();
@@ -811,7 +811,7 @@
     method public androidx.compose.foundation.lazy.grid.LazyGridLayoutInfo getLayoutInfo();
     method public boolean isScrollInProgress();
     method public suspend Object? scroll(androidx.compose.foundation.MutatePriority scrollPriority, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.ScrollScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
-    method public suspend Object? scrollToItem(int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public suspend Object? scrollToItem(@IntRange(from=0L) int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit>);
     property public boolean canScrollBackward;
     property public boolean canScrollForward;
     property public final int firstVisibleItemIndex;
@@ -1057,6 +1057,13 @@
 
 package androidx.compose.foundation.pager {
 
+  @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public sealed interface PageInfo {
+    method public int getIndex();
+    method public int getOffset();
+    property public abstract int index;
+    property public abstract int offset;
+  }
+
   @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public interface PageSize {
     method public int calculateMainAxisPageSize(androidx.compose.ui.unit.Density, int availableSpace, int pageSpacing);
   }
@@ -1086,6 +1093,29 @@
     method @Deprecated @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void VerticalPager(int pageCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.pager.PagerState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.pager.PageSize pageSize, optional int beyondBoundsPageCount, optional float pageSpacing, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, optional androidx.compose.foundation.gestures.snapping.SnapFlingBehavior flingBehavior, optional boolean userScrollEnabled, optional boolean reverseLayout, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, optional androidx.compose.ui.input.nestedscroll.NestedScrollConnection pageNestedScrollConnection, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.pager.PagerScope,? super java.lang.Integer,kotlin.Unit> pageContent);
   }
 
+  @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public sealed interface PagerLayoutInfo {
+    method public int getAfterContentPadding();
+    method public int getBeforeContentPadding();
+    method public androidx.compose.foundation.gestures.Orientation getOrientation();
+    method public int getPageSize();
+    method public int getPageSpacing();
+    method public boolean getReverseLayout();
+    method public int getViewportEndOffset();
+    method public long getViewportSize();
+    method public int getViewportStartOffset();
+    method public java.util.List<androidx.compose.foundation.pager.PageInfo> getVisiblePagesInfo();
+    property public abstract int afterContentPadding;
+    property public abstract int beforeContentPadding;
+    property public abstract androidx.compose.foundation.gestures.Orientation orientation;
+    property public abstract int pageSize;
+    property public abstract int pageSpacing;
+    property public abstract boolean reverseLayout;
+    property public abstract int viewportEndOffset;
+    property public abstract long viewportSize;
+    property public abstract int viewportStartOffset;
+    property public abstract java.util.List<androidx.compose.foundation.pager.PageInfo> visiblePagesInfo;
+  }
+
   @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi public sealed interface PagerScope {
   }
 
@@ -1109,6 +1139,7 @@
     method public final int getInitialPage();
     method public final float getInitialPageOffsetFraction();
     method public final androidx.compose.foundation.interaction.InteractionSource getInteractionSource();
+    method public final androidx.compose.foundation.pager.PagerLayoutInfo getLayoutInfo();
     method public final float getOffsetFractionForPage(int page);
     method public abstract int getPageCount();
     method public final int getSettledPage();
@@ -1124,6 +1155,7 @@
     property public final float initialPageOffsetFraction;
     property public final androidx.compose.foundation.interaction.InteractionSource interactionSource;
     property public boolean isScrollInProgress;
+    property public final androidx.compose.foundation.pager.PagerLayoutInfo layoutInfo;
     property public abstract int pageCount;
     property public final int settledPage;
     property public final int targetPage;
@@ -1193,7 +1225,7 @@
     method public static androidx.compose.foundation.shape.AbsoluteCutCornerShape AbsoluteCutCornerShape(optional float topLeft, optional float topRight, optional float bottomRight, optional float bottomLeft);
     method public static androidx.compose.foundation.shape.AbsoluteCutCornerShape AbsoluteCutCornerShape(optional float topLeft, optional float topRight, optional float bottomRight, optional float bottomLeft);
     method public static androidx.compose.foundation.shape.AbsoluteCutCornerShape AbsoluteCutCornerShape(int percent);
-    method public static androidx.compose.foundation.shape.AbsoluteCutCornerShape AbsoluteCutCornerShape(optional int topLeftPercent, optional int topRightPercent, optional int bottomRightPercent, optional int bottomLeftPercent);
+    method public static androidx.compose.foundation.shape.AbsoluteCutCornerShape AbsoluteCutCornerShape(optional @IntRange(from=0L, to=100L) int topLeftPercent, optional @IntRange(from=0L, to=100L) int topRightPercent, optional @IntRange(from=0L, to=100L) int bottomRightPercent, optional @IntRange(from=0L, to=100L) int bottomLeftPercent);
   }
 
   public final class AbsoluteRoundedCornerShape extends androidx.compose.foundation.shape.CornerBasedShape {
@@ -1209,7 +1241,7 @@
     method public static androidx.compose.foundation.shape.AbsoluteRoundedCornerShape AbsoluteRoundedCornerShape(optional float topLeft, optional float topRight, optional float bottomRight, optional float bottomLeft);
     method public static androidx.compose.foundation.shape.AbsoluteRoundedCornerShape AbsoluteRoundedCornerShape(optional float topLeft, optional float topRight, optional float bottomRight, optional float bottomLeft);
     method public static androidx.compose.foundation.shape.AbsoluteRoundedCornerShape AbsoluteRoundedCornerShape(int percent);
-    method public static androidx.compose.foundation.shape.AbsoluteRoundedCornerShape AbsoluteRoundedCornerShape(optional int topLeftPercent, optional int topRightPercent, optional int bottomRightPercent, optional int bottomLeftPercent);
+    method public static androidx.compose.foundation.shape.AbsoluteRoundedCornerShape AbsoluteRoundedCornerShape(optional @IntRange(from=0L, to=100L) int topLeftPercent, optional @IntRange(from=0L, to=100L) int topRightPercent, optional @IntRange(from=0L, to=100L) int bottomRightPercent, optional @IntRange(from=0L, to=100L) int bottomLeftPercent);
   }
 
   public abstract class CornerBasedShape implements androidx.compose.ui.graphics.Shape {
@@ -1235,7 +1267,7 @@
   public final class CornerSizeKt {
     method @androidx.compose.runtime.Stable public static androidx.compose.foundation.shape.CornerSize CornerSize(float size);
     method @androidx.compose.runtime.Stable public static androidx.compose.foundation.shape.CornerSize CornerSize(float size);
-    method @androidx.compose.runtime.Stable public static androidx.compose.foundation.shape.CornerSize CornerSize(int percent);
+    method @androidx.compose.runtime.Stable public static androidx.compose.foundation.shape.CornerSize CornerSize(@IntRange(from=0L, to=100L) int percent);
     method public static androidx.compose.foundation.shape.CornerSize getZeroCornerSize();
     property public static final androidx.compose.foundation.shape.CornerSize ZeroCornerSize;
   }
@@ -1253,7 +1285,7 @@
     method public static androidx.compose.foundation.shape.CutCornerShape CutCornerShape(optional float topStart, optional float topEnd, optional float bottomEnd, optional float bottomStart);
     method public static androidx.compose.foundation.shape.CutCornerShape CutCornerShape(optional float topStart, optional float topEnd, optional float bottomEnd, optional float bottomStart);
     method public static androidx.compose.foundation.shape.CutCornerShape CutCornerShape(int percent);
-    method public static androidx.compose.foundation.shape.CutCornerShape CutCornerShape(optional int topStartPercent, optional int topEndPercent, optional int bottomEndPercent, optional int bottomStartPercent);
+    method public static androidx.compose.foundation.shape.CutCornerShape CutCornerShape(optional @IntRange(from=0L, to=100L) int topStartPercent, optional @IntRange(from=0L, to=100L) int topEndPercent, optional @IntRange(from=0L, to=100L) int bottomEndPercent, optional @IntRange(from=0L, to=100L) int bottomStartPercent);
   }
 
   public final class GenericShape implements androidx.compose.ui.graphics.Shape {
@@ -1274,7 +1306,7 @@
     method public static androidx.compose.foundation.shape.RoundedCornerShape RoundedCornerShape(optional float topStart, optional float topEnd, optional float bottomEnd, optional float bottomStart);
     method public static androidx.compose.foundation.shape.RoundedCornerShape RoundedCornerShape(optional float topStart, optional float topEnd, optional float bottomEnd, optional float bottomStart);
     method public static androidx.compose.foundation.shape.RoundedCornerShape RoundedCornerShape(int percent);
-    method public static androidx.compose.foundation.shape.RoundedCornerShape RoundedCornerShape(optional int topStartPercent, optional int topEndPercent, optional int bottomEndPercent, optional int bottomStartPercent);
+    method public static androidx.compose.foundation.shape.RoundedCornerShape RoundedCornerShape(optional @IntRange(from=0L, to=100L) int topStartPercent, optional @IntRange(from=0L, to=100L) int topEndPercent, optional @IntRange(from=0L, to=100L) int bottomEndPercent, optional @IntRange(from=0L, to=100L) int bottomStartPercent);
     method public static androidx.compose.foundation.shape.RoundedCornerShape getCircleShape();
     property public static final androidx.compose.foundation.shape.RoundedCornerShape CircleShape;
   }
diff --git a/compose/foundation/foundation/build.gradle b/compose/foundation/foundation/build.gradle
index 3126b99..60bba5d 100644
--- a/compose/foundation/foundation/build.gradle
+++ b/compose/foundation/foundation/build.gradle
@@ -131,7 +131,6 @@
         if (desktopEnabled) {
             desktopTest {
                 dependsOn(jvmTest)
-                dependsOn(desktopMain)
                 dependencies {
                     implementation(libs.truth)
                     implementation(libs.junit)
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/BasicTextField2Demos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/BasicTextField2Demos.kt
index dc51537..d643b34 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/BasicTextField2Demos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/BasicTextField2Demos.kt
@@ -23,7 +23,9 @@
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.demos.text.TagLine
 import androidx.compose.foundation.demos.text.fontSize8
+import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.imePadding
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.text2.BasicTextField2
@@ -31,6 +33,7 @@
 import androidx.compose.foundation.text2.input.TextFieldState
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.material.Button
+import androidx.compose.material.Checkbox
 import androidx.compose.material.LocalTextStyle
 import androidx.compose.material.Text
 import androidx.compose.runtime.Composable
@@ -39,6 +42,7 @@
 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.graphics.Color
 import androidx.compose.ui.text.TextStyle
@@ -82,6 +86,9 @@
 
         TagLine(tag = "State toggling BasicTextField2")
         StateTogglingBasicTextField2()
+
+        TagLine(tag = "BasicTextField2 Edit Controls")
+        BasicTextField2EditControls()
     }
 }
 
@@ -117,3 +124,31 @@
 
     BasicTextField2(state, demoTextFieldModifiers, textStyle = LocalTextStyle.current)
 }
+
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+fun BasicTextField2EditControls() {
+    var enabled by remember { mutableStateOf(true) }
+    var readOnly by remember { mutableStateOf(false) }
+    val state = remember { TextFieldState("Content goes here") }
+
+    Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
+        Row(verticalAlignment = Alignment.CenterVertically) {
+            Text("Enabled")
+            Checkbox(checked = enabled, onCheckedChange = { enabled = it })
+        }
+
+        Row(verticalAlignment = Alignment.CenterVertically) {
+            Text("Readonly")
+            Checkbox(checked = readOnly, onCheckedChange = { readOnly = it })
+        }
+
+        BasicTextField2(
+            state,
+            demoTextFieldModifiers,
+            textStyle = LocalTextStyle.current,
+            enabled = enabled,
+            readOnly = readOnly
+        )
+    }
+}
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/ScrollDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/ScrollDemos.kt
index 9ff65d7..b144879 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/ScrollDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/ScrollDemos.kt
@@ -19,6 +19,7 @@
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.demos.text.TagLine
 import androidx.compose.foundation.demos.text.fontSize8
+import androidx.compose.foundation.demos.text.loremIpsum
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
@@ -91,12 +92,13 @@
 @Composable
 fun SingleLineHorizontalScrollableTextField() {
     val state = remember {
-        TextFieldState("When content gets long,this field should scroll horizontally")
+        TextFieldState(loremIpsum(wordCount = 100))
     }
     BasicTextField2(
         state = state,
         lineLimits = SingleLine,
-        textStyle = TextStyle(fontSize = 24.sp)
+        textStyle = TextStyle(fontSize = 24.sp),
+        modifier = Modifier.padding(horizontal = 32.dp)
     )
 }
 
@@ -118,12 +120,7 @@
 @Composable
 fun SingleLineVerticalScrollableTextField() {
     val state = remember {
-        TextFieldState(
-            buildString {
-                repeat(10) {
-                    appendLine("When content gets long, this field should scroll vertically")
-                }
-            })
+        TextFieldState("When content gets long, this field should scroll vertically\n".repeat(10))
     }
     BasicTextField2(
         state = state,
@@ -136,13 +133,7 @@
 @Composable
 fun MultiLineVerticalScrollableTextField() {
     val state = remember {
-        TextFieldState(
-            buildString {
-                repeat(10) {
-                    appendLine("When content gets long, this field should scroll vertically")
-                }
-            }
-        )
+        TextFieldState(loremIpsum(wordCount = 200))
     }
     BasicTextField2(
         state = state,
diff --git a/compose/foundation/foundation/lint-baseline.xml b/compose/foundation/foundation/lint-baseline.xml
index 3e9de82..c5a68c5 100644
--- a/compose/foundation/foundation/lint-baseline.xml
+++ b/compose/foundation/foundation/lint-baseline.xml
@@ -1327,7 +1327,7 @@
     <issue
         id="PrimitiveInLambda"
         message="Use a functional interface instead of lambda syntax for lambdas with primitive values in method magnifier has parameter &apos;magnifierCenter&apos; with type Function1&lt;? super Density, Offset>."
-        errorLine1="    magnifierCenter: Density.() -> Offset,"
+        errorLine1="    magnifierCenter: Density.() -> Offset = { Offset.Unspecified },"
         errorLine2="                     ~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/androidMain/kotlin/androidx/compose/foundation/Magnifier.kt"/>
@@ -1336,7 +1336,7 @@
     <issue
         id="PrimitiveInLambda"
         message="Use a functional interface instead of lambda syntax for lambdas with primitive values in method magnifier has parameter &apos;onSizeChanged&apos; with type Function1&lt;? super DpSize, Unit>."
-        errorLine1="    onSizeChanged: ((DpSize) -> Unit)?,"
+        errorLine1="    onSizeChanged: ((DpSize) -> Unit)? = null,"
         errorLine2="                   ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/androidMain/kotlin/androidx/compose/foundation/Magnifier.kt"/>
@@ -1344,27 +1344,135 @@
 
     <issue
         id="PrimitiveInLambda"
-        message="Use a functional interface instead of lambda syntax for lambdas with primitive values in variable &apos;updatedSourceCenter&apos; with type Function1&lt;? super Density, ? extends Offset>."
-        errorLine1="    val updatedSourceCenter by rememberUpdatedState(sourceCenter)"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        message="Use a functional interface instead of lambda syntax for lambdas with primitive values in constructor MagnifierElement has parameter &apos;sourceCenter&apos; with type Function1&lt;? super Density, Offset>."
+        errorLine1="    private val sourceCenter: Density.() -> Offset,"
+        errorLine2="                              ~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/androidMain/kotlin/androidx/compose/foundation/Magnifier.kt"/>
     </issue>
 
     <issue
         id="PrimitiveInLambda"
-        message="Use a functional interface instead of lambda syntax for lambdas with primitive values in variable &apos;updatedMagnifierCenter&apos; with type Function1&lt;? super Density, ? extends Offset>."
-        errorLine1="    val updatedMagnifierCenter by rememberUpdatedState(magnifierCenter)"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        message="Use a functional interface instead of lambda syntax for lambdas with primitive values in constructor MagnifierElement has parameter &apos;magnifierCenter&apos; with type Function1&lt;? super Density, Offset>."
+        errorLine1="    private val magnifierCenter: Density.() -> Offset = { Offset.Unspecified },"
+        errorLine2="                                 ~~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/androidMain/kotlin/androidx/compose/foundation/Magnifier.kt"/>
     </issue>
 
     <issue
         id="PrimitiveInLambda"
-        message="Use a functional interface instead of lambda syntax for lambdas with primitive values in variable &apos;updatedOnSizeChanged&apos; with type Function1&lt;? super DpSize, ? extends Unit>."
-        errorLine1="    val updatedOnSizeChanged by rememberUpdatedState(onSizeChanged)"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        message="Use a functional interface instead of lambda syntax for lambdas with primitive values in constructor MagnifierElement has parameter &apos;onSizeChanged&apos; with type Function1&lt;? super DpSize, Unit>."
+        errorLine1="    private val onSizeChanged: ((DpSize) -> Unit)? = null,"
+        errorLine2="                               ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/foundation/Magnifier.kt"/>
+    </issue>
+
+    <issue
+        id="PrimitiveInLambda"
+        message="Use a functional interface instead of lambda syntax for lambdas with primitive values in constructor MagnifierNode has parameter &apos;sourceCenter&apos; with type Function1&lt;? super Density, Offset>."
+        errorLine1="    var sourceCenter: Density.() -> Offset,"
+        errorLine2="                      ~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/foundation/Magnifier.kt"/>
+    </issue>
+
+    <issue
+        id="PrimitiveInLambda"
+        message="Use a functional interface instead of lambda syntax for lambdas with primitive values in method setSourceCenter has parameter &apos;&lt;set-?>&apos; with type Function1&lt;? super Density, Offset>."
+        errorLine1="    var sourceCenter: Density.() -> Offset,"
+        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/foundation/Magnifier.kt"/>
+    </issue>
+
+    <issue
+        id="PrimitiveInLambda"
+        message="Use a functional interface instead of lambda syntax for lambdas with primitive values in return type Function1&lt;Density, Offset> of &apos;getSourceCenter&apos;."
+        errorLine1="    var sourceCenter: Density.() -> Offset,"
+        errorLine2="                      ~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/foundation/Magnifier.kt"/>
+    </issue>
+
+    <issue
+        id="PrimitiveInLambda"
+        message="Use a functional interface instead of lambda syntax for lambdas with primitive values in constructor MagnifierNode has parameter &apos;magnifierCenter&apos; with type Function1&lt;? super Density, Offset>."
+        errorLine1="    var magnifierCenter: Density.() -> Offset = { Offset.Unspecified },"
+        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/foundation/Magnifier.kt"/>
+    </issue>
+
+    <issue
+        id="PrimitiveInLambda"
+        message="Use a functional interface instead of lambda syntax for lambdas with primitive values in method setMagnifierCenter has parameter &apos;&lt;set-?>&apos; with type Function1&lt;? super Density, Offset>."
+        errorLine1="    var magnifierCenter: Density.() -> Offset = { Offset.Unspecified },"
+        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/foundation/Magnifier.kt"/>
+    </issue>
+
+    <issue
+        id="PrimitiveInLambda"
+        message="Use a functional interface instead of lambda syntax for lambdas with primitive values in return type Function1&lt;Density, Offset> of &apos;getMagnifierCenter&apos;."
+        errorLine1="    var magnifierCenter: Density.() -> Offset = { Offset.Unspecified },"
+        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/foundation/Magnifier.kt"/>
+    </issue>
+
+    <issue
+        id="PrimitiveInLambda"
+        message="Use a functional interface instead of lambda syntax for lambdas with primitive values in constructor MagnifierNode has parameter &apos;onSizeChanged&apos; with type Function1&lt;? super DpSize, Unit>."
+        errorLine1="    var onSizeChanged: ((DpSize) -> Unit)? = null,"
+        errorLine2="                       ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/foundation/Magnifier.kt"/>
+    </issue>
+
+    <issue
+        id="PrimitiveInLambda"
+        message="Use a functional interface instead of lambda syntax for lambdas with primitive values in method setOnSizeChanged has parameter &apos;&lt;set-?>&apos; with type Function1&lt;? super DpSize, Unit>."
+        errorLine1="    var onSizeChanged: ((DpSize) -> Unit)? = null,"
+        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/foundation/Magnifier.kt"/>
+    </issue>
+
+    <issue
+        id="PrimitiveInLambda"
+        message="Use a functional interface instead of lambda syntax for lambdas with primitive values in return type Function1&lt;DpSize, Unit> of &apos;getOnSizeChanged&apos;."
+        errorLine1="    var onSizeChanged: ((DpSize) -> Unit)? = null,"
+        errorLine2="                       ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/foundation/Magnifier.kt"/>
+    </issue>
+
+    <issue
+        id="PrimitiveInLambda"
+        message="Use a functional interface instead of lambda syntax for lambdas with primitive values in method update has parameter &apos;sourceCenter&apos; with type Function1&lt;? super Density, Offset>."
+        errorLine1="        sourceCenter: Density.() -> Offset,"
+        errorLine2="                      ~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/foundation/Magnifier.kt"/>
+    </issue>
+
+    <issue
+        id="PrimitiveInLambda"
+        message="Use a functional interface instead of lambda syntax for lambdas with primitive values in method update has parameter &apos;magnifierCenter&apos; with type Function1&lt;? super Density, Offset>."
+        errorLine1="        magnifierCenter: Density.() -> Offset,"
+        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/androidMain/kotlin/androidx/compose/foundation/Magnifier.kt"/>
+    </issue>
+
+    <issue
+        id="PrimitiveInLambda"
+        message="Use a functional interface instead of lambda syntax for lambdas with primitive values in method update has parameter &apos;onSizeChanged&apos; with type Function1&lt;? super DpSize, Unit>."
+        errorLine1="        onSizeChanged: ((DpSize) -> Unit)?,"
+        errorLine2="                       ~~~~~~~~~~~~~~~~~~~">
         <location
             file="src/androidMain/kotlin/androidx/compose/foundation/Magnifier.kt"/>
     </issue>
@@ -2010,6 +2118,42 @@
 
     <issue
         id="PrimitiveInLambda"
+        message="Use a functional interface instead of lambda syntax for lambdas with primitive values in constructor TransformableElement has parameter &apos;canPan&apos; with type Function1&lt;? super Offset, Boolean>."
+        errorLine1="    private val canPan: (Offset) -> Boolean,"
+        errorLine2="                        ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/commonMain/kotlin/androidx/compose/foundation/gestures/Transformable.kt"/>
+    </issue>
+
+    <issue
+        id="PrimitiveInLambda"
+        message="Use a functional interface instead of lambda syntax for lambdas with primitive values in constructor TransformableNode has parameter &apos;canPan&apos; with type Function1&lt;? super Offset, Boolean>."
+        errorLine1="    private var canPan: (Offset) -> Boolean,"
+        errorLine2="                        ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/commonMain/kotlin/androidx/compose/foundation/gestures/Transformable.kt"/>
+    </issue>
+
+    <issue
+        id="PrimitiveInLambda"
+        message="Use a functional interface instead of lambda syntax for lambdas with primitive values in method update has parameter &apos;canPan&apos; with type Function1&lt;? super Offset, Boolean>."
+        errorLine1="        canPan: (Offset) -> Boolean,"
+        errorLine2="                ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/commonMain/kotlin/androidx/compose/foundation/gestures/Transformable.kt"/>
+    </issue>
+
+    <issue
+        id="PrimitiveInLambda"
+        message="Use a functional interface instead of lambda syntax for lambdas with primitive values in method detectZoom has parameter &apos;canPan&apos; with type Function1&lt;? super Offset, Boolean>."
+        errorLine1="    canPan: (Offset) -> Boolean"
+        errorLine2="            ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/commonMain/kotlin/androidx/compose/foundation/gestures/Transformable.kt"/>
+    </issue>
+
+    <issue
+        id="PrimitiveInLambda"
         message="Use a functional interface instead of lambda syntax for lambdas with primitive values in method TransformableState has parameter &apos;onTransformation&apos; with type Function3&lt;? super Float, ? super Offset, ? super Float, Unit>."
         errorLine1="    onTransformation: (zoomChange: Float, panChange: Offset, rotationChange: Float) -> Unit"
         errorLine2="                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/PagerSamples.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/PagerSamples.kt
index 5d26bb0..979c731 100644
--- a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/PagerSamples.kt
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/PagerSamples.kt
@@ -38,9 +38,11 @@
 import androidx.compose.material.Text
 import androidx.compose.material.TopAppBar
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.snapshotFlow
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
@@ -338,3 +340,16 @@
         }
     }
 }
+
+@OptIn(ExperimentalFoundationApi::class)
+@Sampled
+@Composable
+fun UsingPagerLayoutInfoForSideEffectSample() {
+    val pagerState = rememberPagerState() { 10 }
+    LaunchedEffect(pagerState) {
+        snapshotFlow { pagerState.layoutInfo.visiblePagesInfo.firstOrNull() }
+            .collect {
+                // use the new first visible page info
+            }
+    }
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/DraggableTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/DraggableTest.kt
index 66e60d5e..684016b 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/DraggableTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/DraggableTest.kt
@@ -49,7 +49,10 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
+import kotlin.test.assertTrue
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.runBlocking
 import org.junit.After
@@ -920,6 +923,41 @@
     }
 
     @Test
+    fun onDragStopped_inputChanged_shouldNotCancelScope() {
+        val enabled = mutableStateOf(true)
+        lateinit var runningJob: Job
+        rule.setContent {
+            Box(
+                modifier = Modifier
+                    .testTag(draggableBoxTag)
+                    .size(100.dp)
+                    .draggable(
+                        enabled = enabled.value,
+                        state = rememberDraggableState { },
+                        orientation = Orientation.Vertical,
+                        onDragStopped = { _ ->
+                            runningJob = launch { delay(10_000L) } // long running operation
+                        }
+                    )
+            )
+        }
+
+        rule.onNodeWithTag(draggableBoxTag).performTouchInput {
+            down(center)
+            moveBy(Offset(100f, 100f))
+            up()
+        }
+
+        rule.runOnIdle {
+            enabled.value = false // cancels pointer input scope
+        }
+
+       rule.runOnIdle {
+           assertTrue { runningJob.isActive } // check if scope is still active
+       }
+    }
+
+    @Test
     fun testInspectableValue() {
         rule.setContent {
             val modifier = Modifier.draggable(
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/MagnifierTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/MagnifierTest.kt
index cf58f35..89438cc 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/MagnifierTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/MagnifierTest.kt
@@ -719,6 +719,36 @@
 
     @SdkSuppress(minSdkVersion = 28)
     @Test
+    fun platformMagnifierModifier_firesOnSizeChanged_initially_whenSourceCenterUnspecified() {
+        val magnifierSize = IntSize(10, 11)
+        val sizeEvents = mutableListOf<DpSize>()
+        val platformMagnifier = CountingPlatformMagnifier().apply {
+            size = magnifierSize
+        }
+        rule.setContent {
+            Box(
+                Modifier.magnifier(
+                    sourceCenter = { Offset.Unspecified },
+                    magnifierCenter = { Offset.Unspecified },
+                    zoom = Float.NaN,
+                    style = MagnifierStyle.Default,
+                    onSizeChanged = { sizeEvents += it },
+                    platformMagnifierFactory = PlatformMagnifierFactory(platformMagnifier)
+                )
+            )
+        }
+
+        rule.runOnIdle {
+            assertThat(sizeEvents).containsExactly(
+                with(rule.density) {
+                    magnifierSize.toSize().toDpSize()
+                }
+            )
+        }
+    }
+
+    @SdkSuppress(minSdkVersion = 28)
+    @Test
     fun platformMagnifierModifier_firesOnSizeChanged_whenNewSizeRequested() {
         val size1 = IntSize(10, 11)
         val size2 = size1 * 2
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/OverscrollTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/OverscrollTest.kt
index f0f7f96..826dfbbc 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/OverscrollTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/OverscrollTest.kt
@@ -24,6 +24,7 @@
 import androidx.compose.foundation.gestures.ScrollableState
 import androidx.compose.foundation.gestures.scrollable
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
@@ -61,6 +62,7 @@
 import com.google.common.truth.Truth.assertWithMessage
 import kotlin.math.abs
 import kotlinx.coroutines.runBlocking
+import org.junit.Assert.assertEquals
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -786,6 +788,25 @@
         }
     }
 
+    @MediumTest
+    @Test
+    fun testOverscrollModifierDrawsOnce() {
+        var drawCount = 0
+        rule.setContent {
+            Spacer(
+                modifier = Modifier.testTag(boxTag)
+                    .size(100.dp)
+                    .overscroll(ScrollableDefaults.overscrollEffect())
+                    .drawBehind {
+                        drawCount++
+                    }
+            )
+        }
+        rule.runOnIdle {
+            assertEquals(1, drawCount)
+        }
+    }
+
     @OptIn(ExperimentalTestApi::class)
     @ExperimentalFoundationApi
     @MediumTest
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyScrollTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyScrollTest.kt
index 02e397e..b3aafccd 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyScrollTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyScrollTest.kt
@@ -32,7 +32,12 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.test.SemanticsMatcher
+import androidx.compose.ui.test.assert
 import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.unit.Dp
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
@@ -56,6 +61,8 @@
     @get:Rule
     val rule = createComposeRule()
 
+    private val lazyListTag = "LazyList"
+
     private val vertical: Boolean
         get() = orientation == Orientation.Vertical
 
@@ -216,6 +223,56 @@
     }
 
     @Test
+    fun animateScrollBySemantics() = testScroll {
+        val scrollAxisKey = if (vertical) {
+            SemanticsProperties.VerticalScrollAxisRange
+        } else {
+            SemanticsProperties.HorizontalScrollAxisRange
+        }
+
+        withContext(Dispatchers.Main + AutoTestFrameClock()) {
+            state.animateScrollBy(1f)
+        }
+
+        var firstScrollAmount = 0
+        rule.onNodeWithTag(lazyListTag)
+            .assert(SemanticsMatcher("Scroll amount is nonzero") {
+                firstScrollAmount = it.config.get(scrollAxisKey).value().toInt()
+                firstScrollAmount != 0
+            })
+            .assert(SemanticsMatcher("Max scroll value is higher than scroll amount") {
+                with(it.config.get(scrollAxisKey)) {
+                    value().toInt() < maxValue().toInt()
+                }
+            })
+
+        withContext(Dispatchers.Main + AutoTestFrameClock()) {
+            state.animateScrollBy(1f)
+        }
+
+        rule.onNodeWithTag(lazyListTag)
+            .assert(SemanticsMatcher("Second scroll amount is different from the first") {
+                it.config.get(scrollAxisKey).value().toInt() != firstScrollAmount
+            })
+            .assert(SemanticsMatcher("Max scroll value is higher than scroll amount") {
+                with(it.config.get(scrollAxisKey)) {
+                    value().toInt() < maxValue().toInt()
+                }
+            })
+
+        withContext(Dispatchers.Main + AutoTestFrameClock()) {
+            state.animateScrollBy(10_000f)
+        }
+
+        rule.onNodeWithTag(lazyListTag)
+            .assert(SemanticsMatcher("Max scroll value is equal to scroll amount") {
+                with(it.config.get(scrollAxisKey)) {
+                    value().toInt() == maxValue().toInt()
+                }
+            })
+    }
+
+    @Test
     fun animatePerFrameForwardToVisibleItem() = testScroll {
         assertSpringAnimation(toIndex = 2)
     }
@@ -379,7 +436,7 @@
     private fun TestContent(spacingDp: Dp) {
         if (vertical) {
             LazyColumn(
-                Modifier.height(containerSizeDp),
+                Modifier.height(containerSizeDp).testTag(lazyListTag),
                 state,
                 verticalArrangement = Arrangement.spacedBy(spacingDp)
             ) {
@@ -389,7 +446,8 @@
             }
         } else {
             LazyRow(
-                Modifier.width(containerSizeDp), state,
+                Modifier.width(containerSizeDp).testTag(lazyListTag),
+                state,
                 horizontalArrangement = Arrangement.spacedBy(spacingDp)
             ) {
                 items(itemsCount) {
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerAccessibilityTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerAccessibilityTest.kt
index 752614c..ac34782 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerAccessibilityTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerAccessibilityTest.kt
@@ -31,6 +31,7 @@
 import androidx.core.view.ViewCompat
 import androidx.test.filters.LargeTest
 import com.google.common.truth.Truth.assertThat
+import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
@@ -63,6 +64,7 @@
         rule.runOnIdle { assertThat(pagerState.currentPageOffsetFraction).isEqualTo(0.0f) }
     }
 
+    @Ignore
     @Test
     fun accessibilityPaging_animateScrollToPage() {
         createPager(initialPage = 5, pageCount = { DefaultPageCount })
@@ -106,6 +108,7 @@
         rule.runOnIdle { assertThat(pagerState.currentPageOffsetFraction).isEqualTo(0.0f) }
     }
 
+    @Ignore
     @Test
     fun userScrollEnabledIsOff_shouldNotAllowPageAccessibilityActions() {
         // Arrange
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerLayoutInfoTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerLayoutInfoTest.kt
index b9975e5..acbcc3f 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerLayoutInfoTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerLayoutInfoTest.kt
@@ -175,26 +175,6 @@
     }
 
     @Test
-    fun totalCountIsCorrect() {
-        var count by mutableStateOf(10)
-        createPager(
-            pageCount = { count },
-            pageSize = { PageSize.Fixed(10.dp) }
-        ) {
-            Box(Modifier.requiredSize(10.dp))
-        }
-
-        rule.runOnIdle {
-            assertThat(pagerState.layoutInfo.pagesCount).isEqualTo(10)
-            count = 20
-        }
-
-        rule.runOnIdle {
-            assertThat(pagerState.layoutInfo.pagesCount).isEqualTo(20)
-        }
-    }
-
-    @Test
     fun viewportOffsetsAndSizeAreCorrect() {
         val sizePx = 45
         val sizeDp = with(rule.density) { sizePx.toDp() }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerTest.kt
index d1a0b13..b7a50b8 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/pager/PagerTest.kt
@@ -17,7 +17,9 @@
 package androidx.compose.foundation.pager
 
 import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.composed
@@ -26,6 +28,7 @@
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.test.swipeLeft
 import androidx.test.filters.LargeTest
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.launch
@@ -250,6 +253,46 @@
         }
     }
 
+    @Test
+    fun keyLambdaShouldUpdateWhenDatasetChanges() {
+        lateinit var pagerState: PagerState
+        val listA = mutableStateOf(listOf(1))
+
+        @Composable
+        fun MyComposable(data: List<Int>) {
+            pagerState = rememberPagerState { data.size }
+            HorizontalPager(
+                modifier = Modifier
+                    .fillMaxSize()
+                    .testTag("pager"),
+                state = pagerState,
+                key = { data[it] }
+            ) {
+                Spacer(
+                    Modifier
+                        .fillMaxSize())
+            }
+        }
+
+        rule.setContent {
+            MyComposable(listA.value)
+        }
+
+        rule.runOnIdle {
+            listA.value = listOf(1, 2)
+        }
+
+        assertThat(listA.value.size).isEqualTo(2)
+
+        rule.onNodeWithTag("pager").performTouchInput {
+            swipeLeft()
+        }
+
+        rule.runOnIdle {
+            assertThat(pagerState.currentPage).isEqualTo(1)
+        }
+    }
+
     companion object {
         @JvmStatic
         @Parameterized.Parameters(name = "{0}")
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/AbstractSelectionMagnifierTests.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/AbstractSelectionMagnifierTests.kt
index 3160b86..7caa305 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/AbstractSelectionMagnifierTests.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/AbstractSelectionMagnifierTests.kt
@@ -16,7 +16,6 @@
 
 package androidx.compose.foundation.text.selection
 
-import androidx.compose.foundation.MagnifierPositionInRoot
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.foundation.text.BasicText
@@ -25,12 +24,10 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.isUnspecified
 import androidx.compose.ui.layout.onSizeChanged
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.semantics.getOrNull
 import androidx.compose.ui.test.SemanticsMatcher
-import androidx.compose.ui.test.SemanticsMatcher.Companion.keyIsDefined
 import androidx.compose.ui.test.TouchInjectionScope
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.longClick
@@ -100,7 +97,7 @@
         rule.onNodeWithTag(tag).performTouchInput { longClick() }
 
         // No magnifier yet.
-        assertNoMagnifierExists()
+        assertNoMagnifierExists(rule)
     }
 
     @Test
@@ -112,7 +109,7 @@
         // TODO(b/209698586) Select programmatically once that's fixed.
         rule.onNodeWithTag(tag).performTouchInput { longClick() }
         // No magnifier yet.
-        assertNoMagnifierExists()
+        assertNoMagnifierExists(rule)
     }
 
     @Test
@@ -288,13 +285,39 @@
         rule.onNode(isSelectionHandle(handle))
             .performTouchInput { down(center) }
 
-        assertThat(getMagnifierCenterOffset()).isNotEqualTo(Offset.Zero)
+        assertMagnifierExists(rule)
 
         // Stop touching the handle to hide the magnifier.
         rule.onNode(isSelectionHandle(handle))
             .performTouchInput { up() }
 
-        assertNoMagnifierExists()
+        assertNoMagnifierExists(rule)
+    }
+
+    protected fun checkMagnifierAppears_whenCursorHandleDragged() {
+        rule.setContent {
+            Content("aaaa aaaa aaaa", Modifier.testTag(tag))
+        }
+
+        showHandle(Handle.Cursor)
+
+        // Touch the handle
+        rule.onNode(isSelectionHandle(Handle.Cursor))
+            .performTouchInput { down(center) }
+
+        assertNoMagnifierExists(rule)
+
+        // move the handle to show the magnifier
+        rule.onNode(isSelectionHandle(Handle.Cursor))
+            .performTouchInput { movePastSlopBy(Offset(x = 1f, y = 0f)) }
+
+        assertMagnifierExists(rule)
+
+        // Stop touching the handle to hide the magnifier.
+        rule.onNode(isSelectionHandle(Handle.Cursor))
+            .performTouchInput { up() }
+
+        assertNoMagnifierExists(rule)
     }
 
     protected fun checkMagnifierShowsDuringInitialLongPressDrag(
@@ -326,8 +349,7 @@
             }
 
         // Magnifier should show after long-press starts.
-        val magnifierInitialPosition = getMagnifierCenterOffset()
-        assertThat(magnifierInitialPosition).isNotEqualTo(Offset.Zero)
+        val magnifierInitialPosition = getMagnifierCenterOffset(rule, requireSpecified = true)
 
         // Drag horizontally - the magnifier should follow.
         rule.onNodeWithTag(tag)
@@ -337,7 +359,7 @@
                 moveBy(dragDistance * dragDirection)
             }
 
-        assertThat(getMagnifierCenterOffset())
+        assertThat(getMagnifierCenterOffset(rule))
             .isEqualTo(magnifierInitialPosition + (dragDistance * dragDirection))
     }
 
@@ -368,13 +390,13 @@
             down(center)
             movePastSlopBy(dragDistance)
         }
-        val magnifierInitialPosition = getMagnifierCenterOffset()
+        val magnifierInitialPosition = getMagnifierCenterOffset(rule, requireSpecified = true)
 
         // Drag the handle horizontally - the magnifier should follow.
         rule.onNode(isSelectionHandle(handle))
             .performTouchInput { moveBy(dragDistance) }
 
-        assertThat(getMagnifierCenterOffset())
+        assertThat(getMagnifierCenterOffset(rule))
             .isEqualTo(magnifierInitialPosition + dragDistance)
     }
 
@@ -414,7 +436,8 @@
                 movePastSlopBy(moveOffset)
             }
         }
-        val magnifierInitialPosition = getMagnifierCenterOffset()
+
+        val magnifierInitialPosition = getMagnifierCenterOffset(rule, requireSpecified = true)
 
         // Drag just a little past the end of the line.
         rule.onNode(isSelectionHandle(handle))
@@ -428,7 +451,7 @@
             }
 
         // The magnifier shouldn't have moved.
-        assertThat(getMagnifierCenterOffset()).isEqualTo(magnifierInitialPosition)
+        assertThat(getMagnifierCenterOffset(rule)).isEqualTo(magnifierInitialPosition)
     }
 
     protected fun checkMagnifierHiddenWhenDraggedTooFar(
@@ -468,7 +491,7 @@
             }
 
         // The magnifier should be gone.
-        assertNoMagnifierExists()
+        assertNoMagnifierExists(rule)
     }
 
     protected fun checkMagnifierFollowsHandleVerticallyBetweenLines(handle: Handle) {
@@ -491,13 +514,13 @@
         // Touch the handle to show the magnifier.
         rule.onNode(isSelectionHandle(handle))
             .performTouchInput { down(center) }
-        val magnifierInitialPosition = getMagnifierCenterOffset()
+        val magnifierInitialPosition = getMagnifierCenterOffset(rule, requireSpecified = true)
 
         // Drag the handle down - the magnifier should follow.
         rule.onNode(isSelectionHandle(handle))
             .performTouchInput { movePastSlopBy(dragDistance) }
 
-        val (x, y) = getMagnifierCenterOffset()
+        val (x, y) = getMagnifierCenterOffset(rule)
         assertThat(x).isEqualTo(magnifierInitialPosition.x)
         assertThat(y)
             .isWithin(1f)
@@ -530,7 +553,7 @@
         rule.onNode(isSelectionHandle(handle))
             .performTouchInput { movePastSlopBy(dragDistance) }
 
-        assertNoMagnifierExists()
+        assertNoMagnifierExists(rule)
     }
 
     protected fun checkMagnifierDoesNotFollowHandleVerticallyWithinLine(handle: Handle) {
@@ -551,7 +574,8 @@
         // Touch the handle to show the magnifier.
         rule.onNode(isSelectionHandle(handle))
             .performTouchInput { down(center) }
-        val magnifierInitialPosition = getMagnifierCenterOffset()
+
+        val magnifierInitialPosition = getMagnifierCenterOffset(rule, requireSpecified = true)
 
         // Drag the handle up - the magnifier should not follow.
         // Note that dragging it down *should* cause it to move to the line below, so only drag up.
@@ -560,7 +584,7 @@
                 movePastSlopBy(-dragDistance)
             }
 
-        assertThat(getMagnifierCenterOffset())
+        assertThat(getMagnifierCenterOffset(rule))
             .isEqualTo(magnifierInitialPosition)
     }
 
@@ -590,26 +614,4 @@
         )
         moveBy(delta + slop)
     }
-
-    private fun getMagnifierCenterOffset(): Offset =
-        rule.onNode(keyIsDefined(MagnifierPositionInRoot))
-            .fetchSemanticsNode()
-            .config[MagnifierPositionInRoot]
-            .let(rule::runOnIdle)
-
-    /**
-     * Asserts that there is no magnifier being displayed. This may be because no
-     * `Modifier.magnifier` modifiers are currently set on any nodes, or because all the magnifiers
-     * that exist have an unspecified position.
-     */
-    private fun assertNoMagnifierExists() {
-        // The magnifier semantics will be present whenever the modifier is, even if the modifier
-        // isn't actually showing a magnifier because the position is unspecified. So instead of
-        // just checking that no semantics property exists, we need to check that the value of each
-        // property won't show a magnifier.
-        val magnifierNodes = rule.onAllNodes(keyIsDefined(MagnifierPositionInRoot))
-            .fetchSemanticsNodes(atLeastOneRootRequired = false)
-            .map { it.config[MagnifierPositionInRoot].invoke() }
-        assertThat(magnifierNodes.all { it.isUnspecified }).isTrue()
-    }
 }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/MagnifierTestUtils.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/MagnifierTestUtils.kt
new file mode 100644
index 0000000..e926727
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/MagnifierTestUtils.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.text.selection
+
+import androidx.compose.foundation.MagnifierPositionInRoot
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.isSpecified
+import androidx.compose.ui.geometry.isUnspecified
+import androidx.compose.ui.test.SemanticsMatcher
+import androidx.compose.ui.test.junit4.ComposeTestRule
+import com.google.common.truth.Truth.assertWithMessage
+import kotlin.test.fail
+
+internal fun getMagnifierCenterOffset(
+    rule: ComposeTestRule,
+    requireSpecified: Boolean = false
+): Offset {
+    val positions = getMagnifierPositions(rule)
+    return if (requireSpecified) {
+        val specifiedPositions = positions.filter { it.isSpecified }
+        if (specifiedPositions.size != 1) {
+            fail(
+                "Expected one specified magnifier position, but found ${specifiedPositions.size}${
+                    if (specifiedPositions.isEmpty()) "." else ": $specifiedPositions"
+                }"
+            )
+        }
+        specifiedPositions.single()
+    } else {
+        positions.firstOrNull() ?: fail("No magnifier position found")
+    }
+}
+
+internal fun assertMagnifierExists(rule: ComposeTestRule) {
+    assertWithMessage("Expected magnifier to exist and have specified coordinates.")
+        .that(getMagnifierPositions(rule).any { it.isSpecified })
+        .isTrue()
+}
+
+/**
+ * Asserts that there is no magnifier being displayed. This may be because no
+ * `Modifier.magnifier` modifiers are currently set on any nodes, or because all the magnifiers
+ * that exist have an unspecified position.
+ */
+internal fun assertNoMagnifierExists(rule: ComposeTestRule) {
+    // The magnifier semantics will be present whenever the modifier is, even if the modifier
+    // isn't actually showing a magnifier because the position is unspecified. So instead of
+    // just checking that no semantics property exists, we need to check that the value of each
+    // property won't show a magnifier.
+    assertWithMessage("Expected magnifier to not exist or exist with unspecified coordinates.")
+        .that(getMagnifierPositions(rule).all { it.isUnspecified })
+        .isTrue()
+}
+
+internal fun getMagnifierPositions(rule: ComposeTestRule) =
+    rule.onAllNodes(SemanticsMatcher.keyIsDefined(MagnifierPositionInRoot))
+        .fetchSemanticsNodes(atLeastOneRootRequired = false)
+        .map { it.config[MagnifierPositionInRoot] }
+        .let { positionFunctions ->
+            rule.runOnIdle {
+                positionFunctions.map { it.invoke() }
+            }
+        }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/TextFieldMagnifierTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/TextFieldMagnifierTest.kt
index 0cc0ff8..ee1c079 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/TextFieldMagnifierTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/TextFieldMagnifierTest.kt
@@ -70,8 +70,8 @@
     }
 
     @Test
-    fun magnifier_appears_whileStartCursorTouched() {
-        checkMagnifierAppears_whileHandleTouched(Handle.Cursor)
+    fun magnifier_appears_whenCursorStartDrag() {
+        checkMagnifierAppears_whenCursorHandleDragged()
     }
 
     @Test
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/TextFieldVisualTransformationMagnifierTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/TextFieldVisualTransformationMagnifierTest.kt
index 0b9362c..c0105175 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/TextFieldVisualTransformationMagnifierTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/TextFieldVisualTransformationMagnifierTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.compose.foundation.text.selection
 
-import androidx.compose.foundation.MagnifierPositionInRoot
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.foundation.text.BasicTextField
@@ -90,11 +89,16 @@
         val handle = config.handle
         showHandle(handle)
 
+        assertNoMagnifierExists(rule)
+
         // Touch the handle to show the magnifier.
         rule.onNode(isSelectionHandle(handle))
-            .performTouchInput { down(center) }
+            .performTouchInput {
+                down(center)
+                movePastSlopBy(Offset(1f, 0f))
+            }
 
-        assertThat(getMagnifierCenterOffset()).isNotEqualTo(Offset.Zero)
+        assertMagnifierExists(rule)
     }
 
     @Ignore("b/266233836")
@@ -124,13 +128,13 @@
         // Touch the handle to show the magnifier.
         rule.onNode(isSelectionHandle(handle))
             .performTouchInput { down(center) }
-        val magnifierInitialPosition = getMagnifierCenterOffset()
+        val magnifierInitialPosition = getMagnifierCenterOffset(rule, requireSpecified = true)
 
         // Drag the handle horizontally - the magnifier should follow.
         rule.onNode(isSelectionHandle(handle))
             .performTouchInput { movePastSlopBy(dragDistance) }
 
-        assertThat(getMagnifierCenterOffset())
+        assertThat(getMagnifierCenterOffset(rule))
             .isEqualTo(magnifierInitialPosition + dragDistance)
     }
 
@@ -162,12 +166,6 @@
         moveBy(delta + slop)
     }
 
-    private fun getMagnifierCenterOffset(): Offset =
-        rule.onNode(SemanticsMatcher.keyIsDefined(MagnifierPositionInRoot))
-            .fetchSemanticsNode()
-            .config[MagnifierPositionInRoot]
-            .let(rule::runOnIdle)
-
     companion object {
         @JvmStatic
         @Parameterized.Parameters(name = "{0}")
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/gestures/TextFieldSelectionHandlesGesturesTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/gestures/TextFieldSelectionHandlesGesturesTest.kt
new file mode 100644
index 0000000..a61a929
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/gestures/TextFieldSelectionHandlesGesturesTest.kt
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.text.selection.gestures
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.text.Handle
+import androidx.compose.foundation.text.selection.fetchTextLayoutResult
+import androidx.compose.foundation.text.selection.gestures.util.TextFieldSelectionAsserter
+import androidx.compose.foundation.text.selection.gestures.util.applyAndAssert
+import androidx.compose.foundation.text.selection.gestures.util.to
+import androidx.compose.foundation.text.selection.isSelectionHandle
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.SemanticsNodeInteraction
+import androidx.compose.ui.test.longClick
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.input.TextFieldValue
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+internal class TextFieldSelectionHandlesGesturesTest : AbstractSelectionGesturesTest() {
+
+    override val pointerAreaTag = "testTag"
+    private val textContent = "line1\nline2 text1 text2\nline3"
+
+    private val textFieldValue = mutableStateOf(TextFieldValue(textContent))
+
+    private lateinit var asserter: TextFieldSelectionAsserter
+
+    @Composable
+    override fun Content() {
+        BasicTextField(
+            value = textFieldValue.value,
+            onValueChange = { textFieldValue.value = it },
+            textStyle = TextStyle(fontFamily = fontFamily, fontSize = fontSize),
+            modifier = Modifier
+                .fillMaxWidth()
+                .testTag(pointerAreaTag),
+        )
+    }
+
+    @Before
+    fun setupAsserter() {
+        performTouchGesture {
+            longClick(characterPosition(13))
+        }
+
+        asserter = TextFieldSelectionAsserter(
+            textContent = textContent,
+            rule = rule,
+            textToolbar = textToolbar,
+            hapticFeedback = hapticFeedback,
+            getActual = { textFieldValue.value }
+        ).apply {
+            selection = 12 to 17
+            selectionHandlesShown = true
+            textToolbarShown = true
+            hapticsCount++
+        }
+    }
+
+    @Test
+    fun whenOnlySetup_middleWordIsSelected() {
+        asserter.assert()
+    }
+
+    @Test
+    fun whenTouchHandle_magnifierReplacesToolbar() {
+        withHandlePressed(Handle.SelectionEnd) {
+            asserter.applyAndAssert {
+                magnifierShown = true
+                textToolbarShown = false
+            }
+        }
+
+        asserter.applyAndAssert {
+            magnifierShown = false
+            textToolbarShown = true
+        }
+
+        withHandlePressed(Handle.SelectionStart) {
+            asserter.applyAndAssert {
+                magnifierShown = true
+                textToolbarShown = false
+            }
+        }
+
+        asserter.applyAndAssert {
+            magnifierShown = false
+            textToolbarShown = true
+        }
+    }
+
+    private fun withHandlePressed(
+        handle: Handle,
+        block: SemanticsNodeInteraction.() -> Unit
+    ) = rule.onNode(isSelectionHandle(handle)).run {
+        performTouchInput { down(center) }
+        block()
+        performTouchInput { up() }
+    }
+
+    private fun characterPosition(offset: Int): Offset = characterBox(offset).center
+
+    private fun characterBox(offset: Int): Rect {
+        val nodePosition = rule.onNodeWithTag(pointerAreaTag).fetchSemanticsNode().positionInRoot
+        val textLayoutResult = rule.onNodeWithTag(pointerAreaTag).fetchTextLayoutResult()
+        return textLayoutResult.getBoundingBox(offset).translate(nodePosition)
+    }
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicSecureTextFieldTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicSecureTextFieldTest.kt
index 1ea8cac..8943967 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicSecureTextFieldTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicSecureTextFieldTest.kt
@@ -21,20 +21,29 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.text.selection.FakeTextToolbar
 import androidx.compose.foundation.text.selection.fetchTextLayoutResult
+import androidx.compose.foundation.text2.input.TextFieldState
 import androidx.compose.foundation.text2.input.TextObfuscationMode
 import androidx.compose.foundation.text2.input.rememberTextFieldState
+import androidx.compose.foundation.text2.selection.FakeClipboardManager
+import androidx.compose.runtime.CompositionLocalProvider
 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
+import androidx.compose.ui.platform.LocalClipboardManager
+import androidx.compose.ui.platform.LocalTextToolbar
 import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.SemanticsActions
 import androidx.compose.ui.semantics.SemanticsProperties
 import androidx.compose.ui.test.ExperimentalTestApi
 import androidx.compose.ui.test.SemanticsMatcher
 import androidx.compose.ui.test.assert
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performSemanticsAction
 import androidx.compose.ui.test.performTextInput
 import androidx.compose.ui.test.performTextInputSelection
 import androidx.compose.ui.test.performTextReplacement
@@ -64,12 +73,20 @@
     fun passwordSemanticsAreSet() {
         rule.setContent {
             BasicSecureTextField(
-                state = rememberTextFieldState(),
+                state = remember {
+                    TextFieldState("Hello", initialSelectionInChars = TextRange(0, 1))
+                },
                 modifier = Modifier.testTag(Tag)
             )
         }
 
+        rule.onNodeWithTag(Tag).requestFocus()
+        rule.waitForIdle()
         rule.onNodeWithTag(Tag).assert(SemanticsMatcher.keyIsDefined(SemanticsProperties.Password))
+        rule.onNodeWithTag(Tag).assert(SemanticsMatcher.keyIsDefined(SemanticsActions.PasteText))
+        // temporarily define copy and cut actions on BasicSecureTextField but make them no-op
+        rule.onNodeWithTag(Tag).assert(SemanticsMatcher.keyIsDefined(SemanticsActions.CopyText))
+        rule.onNodeWithTag(Tag).assert(SemanticsMatcher.keyIsDefined(SemanticsActions.CutText))
     }
 
     @Test
@@ -140,10 +157,11 @@
                     state = rememberTextFieldState(),
                     modifier = Modifier.testTag(Tag)
                 )
-                Box(modifier = Modifier
-                    .size(1.dp)
-                    .testTag("otherFocusable")
-                    .focusable()
+                Box(
+                    modifier = Modifier
+                        .size(1.dp)
+                        .testTag("otherFocusable")
+                        .focusable()
                 )
             }
         }
@@ -269,4 +287,83 @@
                 .isEqualTo("\u2022\u2022\u2022")
         }
     }
+
+    @OptIn(ExperimentalTestApi::class)
+    @Test
+    fun semantics_copy() {
+        val state = TextFieldState("Hello World!")
+        val clipboardManager = FakeClipboardManager("initial")
+        rule.setContent {
+            CompositionLocalProvider(LocalClipboardManager provides clipboardManager) {
+                BasicSecureTextField(
+                    state = state,
+                    modifier = Modifier.testTag(Tag)
+                )
+            }
+        }
+
+        rule.onNodeWithTag(Tag).performTextInputSelection(TextRange(0, 5))
+        rule.onNodeWithTag(Tag).performSemanticsAction(SemanticsActions.CopyText)
+
+        rule.runOnIdle {
+            assertThat(clipboardManager.getText()?.toString()).isEqualTo("initial")
+        }
+    }
+
+    @OptIn(ExperimentalTestApi::class)
+    @Test
+    fun semantics_cut() {
+        val state = TextFieldState("Hello World!")
+        val clipboardManager = FakeClipboardManager("initial")
+        rule.setContent {
+            CompositionLocalProvider(LocalClipboardManager provides clipboardManager) {
+                BasicSecureTextField(
+                    state = state,
+                    modifier = Modifier.testTag(Tag)
+                )
+            }
+        }
+
+        rule.onNodeWithTag(Tag).performTextInputSelection(TextRange(0, 5))
+        rule.onNodeWithTag(Tag).performSemanticsAction(SemanticsActions.CutText)
+
+        rule.runOnIdle {
+            assertThat(clipboardManager.getText()?.toString()).isEqualTo("initial")
+            assertThat(state.text.toString()).isEqualTo("Hello World!")
+        }
+    }
+
+    @OptIn(ExperimentalTestApi::class)
+    @Test
+    fun toolbarDoesNotShowCopyOrCut() {
+        var copyOptionAvailable = false
+        var cutOptionAvailable = false
+        var showMenuRequested = false
+        val textToolbar = FakeTextToolbar(
+            onShowMenu = { _, onCopyRequested, _, onCutRequested, _ ->
+                showMenuRequested = true
+                copyOptionAvailable = onCopyRequested != null
+                cutOptionAvailable = onCutRequested != null
+            },
+            onHideMenu = {}
+        )
+        val state = TextFieldState("Hello")
+        rule.setContent {
+            CompositionLocalProvider(LocalTextToolbar provides textToolbar) {
+                BasicSecureTextField(
+                    state = state,
+                    modifier = Modifier.testTag(Tag)
+                )
+            }
+        }
+
+        rule.onNodeWithTag(Tag).requestFocus()
+        rule.onNodeWithTag(Tag).performTextInputSelection(TextRange(0, 5))
+
+        rule.runOnIdle {
+            assertThat(showMenuRequested).isTrue()
+            assertThat(copyOptionAvailable).isFalse()
+            assertThat(cutOptionAvailable).isFalse()
+        }
+    }
 }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicTextField2SemanticsTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicTextField2SemanticsTest.kt
index 2761b7e..be39aa2 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicTextField2SemanticsTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicTextField2SemanticsTest.kt
@@ -3,15 +3,21 @@
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.text.BasicText
+import androidx.compose.foundation.text.Handle
 import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.foundation.text.selection.fetchTextLayoutResult
+import androidx.compose.foundation.text.selection.isSelectionHandle
 import androidx.compose.foundation.text2.input.TextFieldCharSequence
 import androidx.compose.foundation.text2.input.TextFieldState
+import androidx.compose.foundation.text2.input.placeCursorAtEnd
+import androidx.compose.foundation.text2.selection.FakeClipboardManager
+import androidx.compose.runtime.CompositionLocalProvider
 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
+import androidx.compose.ui.platform.LocalClipboardManager
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.semantics.SemanticsActions
 import androidx.compose.ui.semantics.SemanticsProperties
@@ -21,6 +27,7 @@
 import androidx.compose.ui.test.SemanticsNodeInteraction
 import androidx.compose.ui.test.assert
 import androidx.compose.ui.test.assertHasClickAction
+import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.assertTextEquals
 import androidx.compose.ui.test.hasImeAction
 import androidx.compose.ui.test.hasSetTextAction
@@ -33,6 +40,7 @@
 import androidx.compose.ui.test.performSemanticsAction
 import androidx.compose.ui.test.performTextInput
 import androidx.compose.ui.test.performTextInputSelection
+import androidx.compose.ui.test.performTextReplacement
 import androidx.compose.ui.text.TextLayoutResult
 import androidx.compose.ui.text.TextRange
 import androidx.compose.ui.text.input.ImeAction
@@ -81,9 +89,9 @@
                 )
             )
             .assert(SemanticsMatcher.keyIsDefined(SemanticsActions.SetText))
+            .assert(SemanticsMatcher.keyIsDefined(SemanticsActions.PasteText))
             .assert(SemanticsMatcher.keyNotDefined(SemanticsProperties.Password))
-            // TODO(halilibo): enable after selection work is completed.
-            // .assert(SemanticsMatcher.keyIsDefined(SemanticsActions.SetSelection))
+            .assert(SemanticsMatcher.keyIsDefined(SemanticsActions.SetSelection))
             .assert(SemanticsMatcher.keyIsDefined(SemanticsActions.GetTextLayoutResult))
 
         val textLayoutResults = mutableListOf<TextLayoutResult>()
@@ -115,6 +123,95 @@
     }
 
     @Test
+    fun semantics_setTextAction() {
+        val state = TextFieldState()
+        rule.setContent {
+            BasicTextField2(
+                state = state,
+                modifier = Modifier.testTag(Tag)
+            )
+        }
+
+        rule.onNodeWithTag(Tag)
+            .assert(isNotFocused())
+            .performTextReplacement("Hello")
+        rule.onNodeWithTag(Tag)
+            .assert(isFocused())
+            .assertTextEquals("Hello")
+
+        assertThat(state.text.toString()).isEqualTo("Hello")
+    }
+
+    @Test
+    fun semantics_setTextAction_appliesFilter() {
+        val state = TextFieldState()
+        rule.setContent {
+            BasicTextField2(
+                state = state,
+                modifier = Modifier.testTag(Tag),
+                filter = { _, changes ->
+                    if (changes.length > 1) {
+                        val newText = changes.asSequence().joinToString("-")
+                        changes.replace(0, changes.length, newText)
+                    }
+                }
+            )
+        }
+
+        rule.onNodeWithTag(Tag)
+            .assert(isNotFocused())
+            .performTextReplacement("Hello")
+        rule.onNodeWithTag(Tag)
+            .assert(isFocused())
+            .assertTextEquals("H-e-l-l-o")
+
+        assertThat(state.text.toString()).isEqualTo("H-e-l-l-o")
+    }
+
+    @Test
+    fun semantics_performTextInputAction() {
+        val state = TextFieldState("Hello", initialSelectionInChars = TextRange(1))
+        rule.setContent {
+            BasicTextField2(
+                state = state,
+                modifier = Modifier.testTag(Tag)
+            )
+        }
+
+        rule.onNodeWithTag(Tag)
+            .assert(isNotFocused())
+            .performTextInput("a")
+        rule.onNodeWithTag(Tag)
+            .assert(isFocused())
+            .assertTextEquals("Haello")
+
+        assertThat(state.text.toString()).isEqualTo("Haello")
+    }
+
+    @Test
+    fun semantics_performTextInputAction_appliesFilter() {
+        val state = TextFieldState("Hello", initialSelectionInChars = TextRange(1))
+        rule.setContent {
+            BasicTextField2(
+                state = state,
+                modifier = Modifier.testTag(Tag),
+                filter = { _, changes ->
+                    changes.replace(0, changes.length, changes.replace(Regex("a"), ""))
+                }
+            )
+        }
+
+        rule.onNodeWithTag(Tag)
+            .assert(isNotFocused())
+            .performTextInput("abc")
+        rule.onNodeWithTag(Tag)
+            .assert(isFocused())
+            .assertTextEquals("Hbcello")
+
+        assertThat(state.text.toString()).isEqualTo("Hbcello")
+    }
+
+    @Test
     fun semantics_clickAction() {
         rule.setContent {
             val state = remember { TextFieldState() }
@@ -225,10 +322,34 @@
             )
         }
 
+        rule.onNodeWithTag(Tag).performTextInputSelection(TextRange(2, 3))
+
+        rule.onNode(isSelectionHandle(Handle.SelectionStart)).assertIsDisplayed()
+        rule.onNode(isSelectionHandle(Handle.SelectionEnd)).assertIsDisplayed()
+
+        rule.runOnIdle {
+            assertThat(state.text.selectionInChars).isEqualTo(TextRange(2, 3))
+        }
+    }
+
+    @OptIn(ExperimentalTestApi::class)
+    @Test
+    fun inputSelection_changesSelectionState_appliesFilter() {
+        val state = TextFieldState("hello", initialSelectionInChars = TextRange(5))
+        rule.setContent {
+            BasicTextField2(
+                state = state,
+                modifier = Modifier.testTag(Tag),
+                filter = { _, changes ->
+                    changes.revertAllChanges()
+                }
+            )
+        }
+
         rule.onNodeWithTag(Tag).performTextInputSelection(TextRange(2))
 
         rule.runOnIdle {
-            assertThat(state.text.selectionInChars).isEqualTo(TextRange(2))
+            assertThat(state.text.selectionInChars).isEqualTo(TextRange(5))
         }
     }
 
@@ -290,6 +411,261 @@
         }
     }
 
+    @Test
+    fun semantics_paste_notAvailable_whenDisabledOrReadOnly() {
+        val state = TextFieldState("World!", initialSelectionInChars = TextRange(0))
+        var enabled by mutableStateOf(false)
+        var readOnly by mutableStateOf(false)
+        rule.setContent {
+            BasicTextField2(
+                state = state,
+                modifier = Modifier.testTag(Tag),
+                enabled = enabled,
+                readOnly = readOnly
+            )
+        }
+
+        rule.onNodeWithTag(Tag).assert(SemanticsMatcher.keyNotDefined(SemanticsActions.PasteText))
+
+        enabled = true
+        readOnly = true
+
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(Tag).assert(SemanticsMatcher.keyNotDefined(SemanticsActions.PasteText))
+
+        enabled = true
+        readOnly = false
+
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(Tag).assert(SemanticsMatcher.keyIsDefined(SemanticsActions.PasteText))
+    }
+
+    @OptIn(ExperimentalTestApi::class)
+    @Test
+    fun semantics_paste() {
+        val state = TextFieldState("Here World!")
+        val clipboardManager = FakeClipboardManager("Hello")
+        rule.setContent {
+            CompositionLocalProvider(LocalClipboardManager provides clipboardManager) {
+                BasicTextField2(
+                    state = state,
+                    modifier = Modifier.testTag(Tag)
+                )
+            }
+        }
+
+        rule.onNodeWithTag(Tag).performTextInputSelection(TextRange(0, 4))
+        rule.onNodeWithTag(Tag).performSemanticsAction(SemanticsActions.PasteText)
+
+        rule.runOnIdle {
+            assertThat(state.text.selectionInChars).isEqualTo(TextRange(5))
+            assertThat(state.text.toString()).isEqualTo("Hello World!")
+        }
+    }
+
+    @OptIn(ExperimentalTestApi::class)
+    @Test
+    fun semantics_paste_appliesFilter() {
+        val state = TextFieldState("Here World!")
+        val clipboardManager = FakeClipboardManager("Hello")
+        rule.setContent {
+            CompositionLocalProvider(LocalClipboardManager provides clipboardManager) {
+                BasicTextField2(
+                    state = state,
+                    modifier = Modifier.testTag(Tag),
+                    filter = { _, changes ->
+                        // remove all 'l' characters
+                        if (changes.changes.changeCount != 0) {
+                            changes.replace(0, changes.length, changes.replace(Regex("l"), ""))
+                            changes.placeCursorAtEnd()
+                        }
+                    }
+                )
+            }
+        }
+
+        rule.onNodeWithTag(Tag).performTextInputSelection(TextRange(0, 4))
+        rule.onNodeWithTag(Tag).performSemanticsAction(SemanticsActions.PasteText)
+
+        rule.runOnIdle {
+            assertThat(state.text.selectionInChars).isEqualTo(TextRange(9))
+            assertThat(state.text.toString()).isEqualTo("Heo Word!")
+        }
+    }
+
+    @OptIn(ExperimentalTestApi::class)
+    @Test
+    fun semantics_copy() {
+        val state = TextFieldState("Hello World!")
+        val clipboardManager = FakeClipboardManager()
+        rule.setContent {
+            CompositionLocalProvider(LocalClipboardManager provides clipboardManager) {
+                BasicTextField2(
+                    state = state,
+                    modifier = Modifier.testTag(Tag)
+                )
+            }
+        }
+
+        rule.onNodeWithTag(Tag).performTextInputSelection(TextRange(0, 5))
+        rule.onNodeWithTag(Tag).performSemanticsAction(SemanticsActions.CopyText)
+
+        rule.runOnIdle {
+            assertThat(clipboardManager.getText()?.toString()).isEqualTo("Hello")
+        }
+    }
+
+//    @Test
+//    fun semantics_copy_disabled_whenDisallowCopy() {
+//        val state = TextFieldState("Hello World!", initialSelectionInChars = TextRange(0, 5))
+//        rule.setContent {
+//            BasicTextField2(
+//                state = state,
+//                modifier = Modifier.testTag(Tag),
+//                allowCopy = false
+//            )
+//        }
+//
+//        rule.onNodeWithTag(Tag).assert(SemanticsMatcher.keyNotDefined(SemanticsActions.CopyText))
+//    }
+
+    @Test
+    fun semantics_copy_disabled_whenSelectionCollapsed() {
+        val state = TextFieldState("Hello World!")
+        rule.setContent {
+            BasicTextField2(
+                state = state,
+                modifier = Modifier.testTag(Tag)
+            )
+        }
+
+        rule.onNodeWithTag(Tag).assert(SemanticsMatcher.keyNotDefined(SemanticsActions.CopyText))
+    }
+
+    @OptIn(ExperimentalTestApi::class)
+    @Test
+    fun semantics_copy_appliesFilter() {
+        val state = TextFieldState("Hello World!", initialSelectionInChars = TextRange(0, 5))
+        val clipboardManager = FakeClipboardManager()
+        rule.setContent {
+            CompositionLocalProvider(LocalClipboardManager provides clipboardManager) {
+                BasicTextField2(
+                    state = state,
+                    modifier = Modifier.testTag(Tag),
+                    filter = { original, changes ->
+                        // reject copy action collapsing the selection
+                        if (changes.selectionInChars != original.selectionInChars) {
+                            changes.revertAllChanges()
+                        }
+                    }
+                )
+            }
+        }
+
+        rule.onNodeWithTag(Tag).performSemanticsAction(SemanticsActions.CopyText)
+
+        rule.runOnIdle {
+            assertThat(state.text.selectionInChars).isEqualTo(TextRange(0, 5))
+        }
+    }
+
+    @OptIn(ExperimentalTestApi::class)
+    @Test
+    fun semantics_cut() {
+        val state = TextFieldState("Hello World!", initialSelectionInChars = TextRange(0, 5))
+        val clipboardManager = FakeClipboardManager()
+        rule.setContent {
+            CompositionLocalProvider(LocalClipboardManager provides clipboardManager) {
+                BasicTextField2(
+                    state = state,
+                    modifier = Modifier.testTag(Tag)
+                )
+            }
+        }
+
+        rule.onNodeWithTag(Tag).performSemanticsAction(SemanticsActions.CutText)
+
+        rule.runOnIdle {
+            assertThat(state.text.toString()).isEqualTo(" World!")
+            assertThat(state.text.selectionInChars).isEqualTo(TextRange(0))
+            assertThat(clipboardManager.getText()?.toString()).isEqualTo("Hello")
+        }
+    }
+
+//    @OptIn(ExperimentalTestApi::class)
+//    @Test
+//    fun semantics_cut_disabled_whenDisallowCopy() {
+//        val state = TextFieldState("Hello World!", initialSelectionInChars = TextRange(0, 5))
+//        rule.setContent {
+//            BasicTextField2(
+//                state = state,
+//                modifier = Modifier.testTag(Tag),
+//                allowCopy = false
+//            )
+//        }
+//
+//        rule.onNodeWithTag(Tag).assert(SemanticsMatcher.keyNotDefined(SemanticsActions.CutText))
+//    }
+
+    @OptIn(ExperimentalTestApi::class)
+    @Test
+    fun semantics_cut_appliesFilter() {
+        val state = TextFieldState("Hello World!", initialSelectionInChars = TextRange(0, 5))
+        val clipboardManager = FakeClipboardManager()
+        rule.setContent {
+            CompositionLocalProvider(LocalClipboardManager provides clipboardManager) {
+                BasicTextField2(
+                    state = state,
+                    modifier = Modifier.testTag(Tag),
+                    filter = { _, changes ->
+                        changes.revertAllChanges()
+                    }
+                )
+            }
+        }
+
+        rule.onNodeWithTag(Tag).performSemanticsAction(SemanticsActions.CutText)
+
+        rule.runOnIdle {
+            assertThat(state.text.toString()).isEqualTo("Hello World!")
+            assertThat(state.text.selectionInChars).isEqualTo(TextRange(0, 5))
+            assertThat(clipboardManager.getText()?.toString()).isEqualTo("Hello")
+        }
+    }
+
+    @Test
+    fun semantics_cut_notAvailable_whenDisabledOrReadOnly() {
+        val state = TextFieldState("World!", initialSelectionInChars = TextRange(0, 1))
+        var enabled by mutableStateOf(false)
+        var readOnly by mutableStateOf(false)
+        rule.setContent {
+            BasicTextField2(
+                state = state,
+                modifier = Modifier.testTag(Tag),
+                enabled = enabled,
+                readOnly = readOnly
+            )
+        }
+
+        rule.onNodeWithTag(Tag).assert(SemanticsMatcher.keyNotDefined(SemanticsActions.CutText))
+
+        enabled = true
+        readOnly = true
+
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(Tag).assert(SemanticsMatcher.keyNotDefined(SemanticsActions.CutText))
+
+        enabled = true
+        readOnly = false
+
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(Tag).assert(SemanticsMatcher.keyIsDefined(SemanticsActions.CutText))
+    }
+
     private fun SemanticsNodeInteraction.assertSelection(expected: TextRange) {
         val selection = fetchSemanticsNode().config
             .getOrNull(SemanticsProperties.TextSelectionRange)
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicTextField2Test.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicTextField2Test.kt
index 8ccc699..ac5bc56 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicTextField2Test.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/BasicTextField2Test.kt
@@ -907,6 +907,9 @@
         // hide it again.
         keyboardHelper.hideKeyboardIfShown()
         rule.onNodeWithTag(Tag).assertIsFocused()
+
+        rule.mainClock.advanceTimeBy(1000) // to not cause double click
+
         rule.onNodeWithTag(Tag).performClick()
 
         // expect keyboard to show up again.
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldCursorTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldCursorTest.kt
index d668835..0e39776 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldCursorTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/TextFieldCursorTest.kt
@@ -647,6 +647,64 @@
             )
     }
 
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    fun readOnly_cursorIsNotDrawn() {
+        state = TextFieldState("test", initialSelectionInChars = TextRange(4))
+        rule.setTestContent {
+            BasicTextField2(
+                state = state,
+                textStyle = textStyle,
+                modifier = textFieldModifier,
+                readOnly = true,
+                cursorBrush = SolidColor(cursorColor),
+                onTextLayout = onTextLayout
+            )
+        }
+
+        focusAndWait()
+
+        rule.mainClock.advanceTimeBy(100)
+        rule.mainClock.advanceTimeByFrame()
+
+        rule.onNode(hasSetTextAction())
+            .captureToImage()
+            .assertDoesNotContainColor(cursorColor)
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    fun toggling_readOnly_drawsCursorAgain() {
+        var readOnly by mutableStateOf(true)
+        state = TextFieldState("test", initialSelectionInChars = TextRange(4))
+        rule.setTestContent {
+            BasicTextField2(
+                state = state,
+                textStyle = textStyle,
+                modifier = textFieldModifier,
+                readOnly = readOnly,
+                cursorBrush = SolidColor(cursorColor),
+                onTextLayout = onTextLayout
+            )
+        }
+
+        focusAndWait()
+
+        rule.mainClock.advanceTimeBy(100)
+        rule.mainClock.advanceTimeByFrame()
+
+        rule.onNode(hasSetTextAction())
+            .captureToImage()
+            .assertDoesNotContainColor(cursorColor)
+
+        readOnly = false
+        rule.mainClock.advanceTimeByFrame()
+
+        rule.onNode(hasSetTextAction())
+            .captureToImage()
+            .assertCursor(cursorTopCenterInLtr)
+    }
+
     private fun focusAndWait() {
         rule.onNode(hasSetTextAction()).requestFocus()
         rule.mainClock.advanceTimeUntil { isFocused }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/selection/TapAndDoubleTapTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/selection/TapAndDoubleTapTest.kt
new file mode 100644
index 0000000..3e8116b
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/selection/TapAndDoubleTapTest.kt
@@ -0,0 +1,452 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.text2.selection
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.testutils.TestViewConfiguration
+import androidx.compose.ui.AbsoluteAlignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.pointer.PointerEventPass
+import androidx.compose.ui.input.pointer.PointerInputChange
+import androidx.compose.ui.input.pointer.PointerInputScope
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalViewConfiguration
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.TouchInjectionScope
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.DpSize
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+private const val TargetTag = "TargetLayout"
+
+/**
+ * These tests are mostly copied from TapGestureDetectorTest.
+ */
+@RunWith(JUnit4::class)
+class TapAndDoubleTapTest {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    private var tapped = false
+    private var doubleTapped = false
+
+    /** The time before a long press gesture attempts to win. */
+    private val LongPressTimeoutMillis: Long = 500L
+
+    /**
+     * The maximum time from the start of the first tap to the start of the second
+     * tap in a double-tap gesture.
+     */
+    private val DoubleTapTimeoutMillis: Long = 300L
+
+    private val util = layoutWithGestureDetector {
+        detectTapAndDoubleTap(
+            onTap = {
+                tapped = true
+            }
+        )
+    }
+
+    private val utilWithDoubleTap = layoutWithGestureDetector {
+        detectTapAndDoubleTap(
+            onTap = {
+                tapped = true
+            },
+            onDoubleTap = {
+                doubleTapped = true
+            }
+        )
+    }
+
+    private val nothingHandler: PointerInputChange.() -> Unit = {}
+
+    private var initialPass: PointerInputChange.() -> Unit = nothingHandler
+    private var finalPass: PointerInputChange.() -> Unit = nothingHandler
+
+    @Before
+    fun setup() {
+        tapped = false
+        doubleTapped = false
+    }
+
+    private fun layoutWithGestureDetector(
+        gestureDetector: suspend PointerInputScope.() -> Unit,
+    ): @Composable () -> Unit = {
+        CompositionLocalProvider(
+            LocalDensity provides Density(1f),
+            LocalViewConfiguration provides TestViewConfiguration(
+                minimumTouchTargetSize = DpSize.Zero
+            )
+        ) {
+            with(LocalDensity.current) {
+                Box(
+                    Modifier
+                        .fillMaxSize()
+                        // Some tests execute a lambda before the initial and final passes
+                        // so they are called here, higher up the chain, so that the
+                        // calls happen prior to the gestureDetector below. The lambdas
+                        // do things like consume events on the initial pass or validate
+                        // consumption on the final pass.
+                        .pointerInput(Unit) {
+                            awaitPointerEventScope {
+                                while (true) {
+                                    val event = awaitPointerEvent(PointerEventPass.Initial)
+                                    event.changes.forEach {
+                                        initialPass(it)
+                                    }
+                                    awaitPointerEvent(PointerEventPass.Final)
+                                    event.changes.forEach {
+                                        finalPass(it)
+                                    }
+                                }
+                            }
+                        }
+                        .wrapContentSize(AbsoluteAlignment.TopLeft)
+                        .size(10.toDp())
+                        .pointerInput(gestureDetector, gestureDetector)
+                        .testTag(TargetTag)
+                )
+            }
+        }
+    }
+
+    private fun performTouch(
+        initialPass: PointerInputChange.() -> Unit = nothingHandler,
+        finalPass: PointerInputChange.() -> Unit = nothingHandler,
+        block: TouchInjectionScope.() -> Unit
+    ) {
+        this.initialPass = initialPass
+        this.finalPass = finalPass
+        rule.onNodeWithTag(TargetTag).performTouchInput(block)
+        rule.waitForIdle()
+        this.initialPass = nothingHandler
+        this.finalPass = nothingHandler
+    }
+
+    @Test
+    fun normalTap() {
+        rule.setContent(util)
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            down(0, Offset(5f, 5f))
+        }
+
+        assertFalse(tapped)
+
+        rule.mainClock.advanceTimeBy(50)
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            up(0)
+        }
+
+        assertTrue(tapped)
+    }
+
+    @Test
+    fun normalTapWithDoubleTapDefined_butNotExecuted() {
+        rule.setContent(utilWithDoubleTap)
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            down(0, Offset(5f, 5f))
+        }
+
+        rule.mainClock.advanceTimeBy(50)
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            up(0)
+        }
+
+        // we don't wait for double tap, tap should be called immediately.
+        assertTrue(tapped)
+        assertFalse(doubleTapped)
+
+        rule.mainClock.advanceTimeBy(DoubleTapTimeoutMillis + 10)
+
+        assertTrue(tapped)
+        assertFalse(doubleTapped)
+    }
+
+    @Test
+    fun normalDoubleTap() {
+        rule.setContent(utilWithDoubleTap)
+
+        performTouch { down(0, Offset(5f, 5f)) }
+        performTouch(finalPass = { assertTrue(isConsumed) }) { up(0) }
+
+        assertTrue(tapped)
+        assertFalse(doubleTapped)
+
+        rule.mainClock.advanceTimeBy(50)
+
+        performTouch { down(0, Offset(5f, 5f)) }
+        performTouch(finalPass = { assertTrue(isConsumed) }) { up(0) }
+
+        assertTrue(tapped)
+        assertTrue(doubleTapped)
+    }
+
+    @Test
+    fun normalLongPress() {
+        rule.setContent(utilWithDoubleTap)
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            down(0, Offset(5f, 5f))
+        }
+
+        assertFalse(tapped)
+        assertFalse(doubleTapped)
+
+        rule.mainClock.advanceTimeBy(LongPressTimeoutMillis + 10)
+
+        assertFalse(tapped)
+        assertFalse(doubleTapped)
+
+        rule.mainClock.advanceTimeBy(500)
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            up(0)
+        }
+
+        assertFalse(tapped)
+        assertFalse(doubleTapped)
+    }
+
+    /**
+     * Pressing in the region, sliding out and then lifting should result in
+     * the callback not being invoked
+     */
+    @Test
+    fun tapMiss() {
+        rule.setContent(util)
+
+        performTouch {
+            down(0, Offset(5f, 5f))
+            moveTo(0, Offset(15f, 15f))
+        }
+
+        performTouch(finalPass = { assertFalse(isConsumed) }) {
+            up(0)
+        }
+
+        assertFalse(tapped)
+    }
+
+    /**
+     * Pressing in the region, sliding out and then lifting should result in
+     * the callback not being invoked
+     */
+    @Test
+    fun longPressMiss() {
+        rule.setContent(utilWithDoubleTap)
+
+        performTouch {
+            down(0, Offset(5f, 5f))
+            moveTo(0, Offset(15f, 15f))
+        }
+
+        rule.mainClock.advanceTimeBy(LongPressTimeoutMillis + 10)
+
+        performTouch(finalPass = { assertFalse(isConsumed) }) {
+            up(0)
+        }
+
+        assertFalse(tapped)
+        assertFalse(doubleTapped)
+    }
+
+    /**
+     * Pressing in the region, sliding out and then lifting should result in
+     * the callback not being invoked for double-tap
+     */
+    @Test
+    fun doubleTapMiss() {
+        rule.setContent(utilWithDoubleTap)
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            down(0, Offset(5f, 5f))
+            up(0)
+        }
+
+        assertTrue(tapped)
+
+        rule.mainClock.advanceTimeBy(50)
+
+        performTouch {
+            down(1, Offset(5f, 5f))
+            moveTo(1, Offset(15f, 15f))
+        }
+
+        performTouch(finalPass = { assertFalse(isConsumed) }) {
+            up(1)
+        }
+
+        assertTrue(tapped)
+        assertFalse(doubleTapped)
+    }
+
+    /**
+     * After a first tap, a second tap should also be detected.
+     */
+    @Test
+    fun secondTap() {
+        rule.setContent(util)
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            down(0, Offset(5f, 5f))
+            up(0)
+        }
+
+        assertTrue(tapped)
+
+        tapped = false
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            down(1, Offset(4f, 4f))
+            up(1)
+        }
+
+        assertTrue(tapped)
+    }
+
+    /**
+     * Clicking in the region with the up already consumed should result in the callback not
+     * being invoked.
+     */
+    @Test
+    fun consumedUpTap() {
+        rule.setContent(util)
+
+        performTouch {
+            down(0, Offset(5f, 5f))
+        }
+
+        assertFalse(tapped)
+
+        performTouch(initialPass = { if (pressed != previousPressed) consume() }) {
+            up(0)
+        }
+
+        assertFalse(tapped)
+    }
+
+    /**
+     * Clicking in the region with the motion consumed should result in the callback not
+     * being invoked.
+     */
+    @Test
+    fun consumedMotionTap() {
+        rule.setContent(util)
+
+        performTouch {
+            down(0, Offset(5f, 5f))
+        }
+
+        performTouch(initialPass = { consume() }) {
+            moveTo(0, Offset(6f, 2f))
+        }
+
+        rule.mainClock.advanceTimeBy(50)
+
+        performTouch {
+            up(0)
+        }
+
+        assertFalse(tapped)
+    }
+
+    /**
+     * Ensure that two-finger taps work.
+     */
+    @Test
+    fun twoFingerTap() {
+        rule.setContent(util)
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            down(0, Offset(1f, 1f))
+        }
+
+        assertFalse(tapped)
+
+        performTouch(finalPass = { assertFalse(isConsumed) }) {
+            down(1, Offset(9f, 5f))
+        }
+
+        assertFalse(tapped)
+
+        performTouch(finalPass = { assertFalse(isConsumed) }) {
+            up(0)
+        }
+
+        assertFalse(tapped)
+
+        performTouch(finalPass = { assertTrue(isConsumed) }) {
+            up(1)
+        }
+
+        assertTrue(tapped)
+    }
+
+    /**
+     * A position change consumption on any finger should cause tap to cancel.
+     */
+    @Test
+    fun twoFingerTapCancel() {
+        rule.setContent(util)
+
+        performTouch {
+            down(0, Offset(1f, 1f))
+        }
+        assertFalse(tapped)
+
+        performTouch {
+            down(1, Offset(9f, 5f))
+        }
+
+        performTouch(initialPass = { consume() }) {
+            moveTo(0, Offset(5f, 5f))
+        }
+        performTouch(finalPass = { assertFalse(isConsumed) }) {
+            up(0)
+        }
+
+        assertFalse(tapped)
+
+        rule.mainClock.advanceTimeBy(50)
+        performTouch(finalPass = { assertFalse(isConsumed) }) {
+            up(1)
+        }
+
+        assertFalse(tapped)
+    }
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/selection/TextFieldCursorHandleTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/selection/TextFieldCursorHandleTest.kt
index 006ea1d..e9c6323 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/selection/TextFieldCursorHandleTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/selection/TextFieldCursorHandleTest.kt
@@ -570,9 +570,10 @@
         rule.onNode(isSelectionHandle(Handle.Cursor)).assertIsDisplayed()
 
         swipeToLeft(fontSizePx)
-        rule.waitForIdle()
 
-        assertThat(state.text.selectionInChars).isEqualTo(TextRange(2))
+        rule.runOnIdle {
+            assertThat(state.text.selectionInChars).isEqualTo(TextRange(2))
+        }
     }
 
     @Test
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/selection/TextFieldLongClickTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/selection/TextFieldLongClickTest.kt
index 64cb81d..767804ea 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/selection/TextFieldLongClickTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/selection/TextFieldLongClickTest.kt
@@ -42,7 +42,6 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import androidx.test.filters.LargeTest
-import androidx.test.filters.SdkSuppress
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
@@ -123,9 +122,8 @@
         assertThat(state.text.selectionInChars).isEqualTo(TextRange(4, 7))
     }
 
-    @SdkSuppress(minSdkVersion = 23)
     @Test
-    fun longClickOnWhitespace_selectsNextWord() {
+    fun longClickOnWhitespace_doesNotSelectWhitespace() {
         val state = TextFieldState("abc def ghi")
         rule.setContent {
             BasicTextField2(
@@ -141,7 +139,8 @@
 
         rule.onNode(isSelectionHandle(Handle.SelectionStart)).assertIsDisplayed()
         rule.onNode(isSelectionHandle(Handle.SelectionEnd)).assertIsDisplayed()
-        assertThat(state.text.selectionInChars).isEqualTo(TextRange(8, 11))
+        assertThat(state.text.selectionInChars).isNotEqualTo(TextRange(7, 8))
+        assertThat(state.text.selectionInChars.collapsed).isFalse()
     }
 
     @Test
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/selection/TextFieldSelectionHandlesTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/selection/TextFieldSelectionHandlesTest.kt
index 3a904b7..0444338 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/selection/TextFieldSelectionHandlesTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/selection/TextFieldSelectionHandlesTest.kt
@@ -18,7 +18,9 @@
 
 import android.os.Build
 import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.ScrollState
 import androidx.compose.foundation.focusable
+import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.horizontalScroll
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
@@ -37,12 +39,15 @@
 import androidx.compose.foundation.text2.input.TextFieldLineLimits
 import androidx.compose.foundation.text2.input.TextFieldState
 import androidx.compose.foundation.verticalScroll
+import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.semantics.SemanticsActions
 import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.doubleClick
 import androidx.compose.ui.test.hasSetTextAction
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
@@ -52,6 +57,7 @@
 import androidx.compose.ui.test.swipeLeft
 import androidx.compose.ui.test.swipeRight
 import androidx.compose.ui.test.swipeUp
+import androidx.compose.ui.text.TextLayoutResult
 import androidx.compose.ui.text.TextRange
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.unit.dp
@@ -59,6 +65,8 @@
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
 import org.junit.Rule
 import org.junit.Test
 
@@ -74,6 +82,7 @@
     private val TAG = "BasicTextField2"
 
     private val fontSize = 10.sp
+    private val fontSizePx = with(rule.density) { fontSize.toPx() }
 
     @Test
     fun selectionHandles_doNotShow_whenFieldNotFocused() {
@@ -357,6 +366,337 @@
         }
     }
 
+    @Test
+    fun dragStartSelectionHandle_toExtendSelection() {
+        state = TextFieldState("abc def ghj", initialSelectionInChars = TextRange(4, 7))
+        rule.setContent {
+            BasicTextField2(
+                state,
+                textStyle = TextStyle(fontSize = fontSize, fontFamily = TEST_FONT_FAMILY),
+                modifier = Modifier
+                    .testTag(TAG)
+                    .width(200.dp)
+            )
+        }
+
+        focusAndWait()
+
+        swipeToLeft(Handle.SelectionStart, fontSizePx * 4)
+        rule.runOnIdle {
+            assertThat(state.text.selectionInChars).isEqualTo(TextRange(0, 7))
+        }
+    }
+
+    @Test
+    fun dragEndSelectionHandle_toExtendSelection() {
+        state = TextFieldState("abc def ghj", initialSelectionInChars = TextRange(4, 7))
+        rule.setContent {
+            BasicTextField2(
+                state,
+                textStyle = TextStyle(fontSize = fontSize, fontFamily = TEST_FONT_FAMILY),
+                modifier = Modifier
+                    .testTag(TAG)
+                    .width(200.dp)
+            )
+        }
+
+        focusAndWait()
+
+        swipeToRight(Handle.SelectionEnd, fontSizePx * 4)
+        rule.runOnIdle {
+            assertThat(state.text.selectionInChars).isEqualTo(TextRange(4, 11))
+        }
+    }
+
+    @Test
+    fun doubleClickOnWord_toSelectWord() {
+        state = TextFieldState("abc def ghj")
+        rule.setContent {
+            BasicTextField2(
+                state,
+                textStyle = TextStyle(fontSize = fontSize, fontFamily = TEST_FONT_FAMILY),
+                modifier = Modifier
+                    .testTag(TAG)
+                    .width(200.dp)
+            )
+        }
+
+        focusAndWait()
+
+        rule.onNodeWithTag(TAG).performTouchInput {
+            doubleClick(Offset(fontSizePx * 5, fontSizePx / 2)) // middle word
+        }
+        rule.runOnIdle {
+            assertThat(state.text.selectionInChars).isEqualTo(TextRange(4, 7))
+        }
+    }
+
+    @SdkSuppress(minSdkVersion = 23)
+    @Test
+    fun doubleClickOnWhitespace_toSelectNextWord() {
+        state = TextFieldState("abc def ghj")
+        rule.setContent {
+            BasicTextField2(
+                state,
+                textStyle = TextStyle(fontSize = fontSize, fontFamily = TEST_FONT_FAMILY),
+                modifier = Modifier
+                    .testTag(TAG)
+                    .width(200.dp)
+            )
+        }
+
+        focusAndWait()
+
+        rule.onNodeWithTag(TAG).performTouchInput {
+            // space between first and second words
+            doubleClick(Offset(fontSizePx * 3.5f, fontSizePx / 2))
+        }
+        rule.runOnIdle {
+            assertThat(state.text.selectionInChars).isEqualTo(TextRange(4, 7))
+        }
+    }
+
+    @Test
+    fun dragStartSelectionHandle_outOfBounds_horizontally() {
+        state = TextFieldState("abc def ".repeat(10), initialSelectionInChars = TextRange(77, 80))
+        val scrollState = ScrollState(0)
+        lateinit var scope: CoroutineScope
+        rule.setContent {
+            scope = rememberCoroutineScope()
+            BasicTextField2(
+                state,
+                textStyle = TextStyle(fontSize = fontSize, fontFamily = TEST_FONT_FAMILY),
+                lineLimits = TextFieldLineLimits.SingleLine,
+                scrollState = scrollState,
+                modifier = Modifier
+                    .testTag(TAG)
+                    .width(100.dp)
+            )
+        }
+
+        rule.waitForIdle()
+        scope.launch { scrollState.scrollTo(scrollState.maxValue) } // scroll to the most right
+        focusAndWait() // selection handles show up
+
+        repeat(80) {
+            swipeToLeft(Handle.SelectionStart, fontSizePx)
+        }
+        rule.runOnIdle {
+            assertThat(state.text.selectionInChars).isEqualTo(TextRange(0, 80))
+        }
+    }
+
+    @Test
+    fun dragStartSelectionHandle_outOfBounds_vertically() {
+        state = TextFieldState("abc def ".repeat(10), initialSelectionInChars = TextRange(77, 80))
+        val scrollState = ScrollState(0)
+        lateinit var scope: CoroutineScope
+        rule.setContent {
+            scope = rememberCoroutineScope()
+            BasicTextField2(
+                state,
+                textStyle = TextStyle(fontSize = fontSize, fontFamily = TEST_FONT_FAMILY),
+                lineLimits = TextFieldLineLimits.MultiLine(maxHeightInLines = 3),
+                scrollState = scrollState,
+                modifier = Modifier
+                    .testTag(TAG)
+                    .width(50.dp)
+            )
+        }
+
+        rule.waitForIdle()
+        scope.launch { scrollState.scrollTo(scrollState.maxValue) } // scroll to the bottom
+        focusAndWait() // selection handles show up
+
+        swipeUp(Handle.SelectionStart, scrollState.maxValue.toFloat() * 2)
+        // make sure that we also swipe to start on the first line
+        swipeToLeft(Handle.SelectionStart, fontSizePx * 10)
+        rule.runOnIdle {
+            assertThat(state.text.selectionInChars).isEqualTo(TextRange(0, 80))
+        }
+    }
+
+    @Test
+    fun dragEndSelectionHandle_outOfBounds_horizontally() {
+        state = TextFieldState("abc def ".repeat(10), initialSelectionInChars = TextRange(0, 3))
+        rule.setContent {
+            BasicTextField2(
+                state,
+                textStyle = TextStyle(fontSize = fontSize, fontFamily = TEST_FONT_FAMILY),
+                lineLimits = TextFieldLineLimits.SingleLine,
+                modifier = Modifier
+                    .testTag(TAG)
+                    .width(100.dp)
+            )
+        }
+
+        rule.waitForIdle()
+        focusAndWait() // selection handles show up
+
+        repeat(80) {
+            swipeToRight(Handle.SelectionEnd, fontSizePx)
+        }
+        rule.runOnIdle {
+            assertThat(state.text.selectionInChars).isEqualTo(TextRange(0, 80))
+        }
+    }
+
+    @Test
+    fun dragEndSelectionHandle_outOfBounds_vertically() {
+        state = TextFieldState("abc def ".repeat(10), initialSelectionInChars = TextRange(0, 3))
+        lateinit var layoutResult: TextLayoutResult
+        rule.setContent {
+            BasicTextField2(
+                state,
+                textStyle = TextStyle(fontSize = fontSize, fontFamily = TEST_FONT_FAMILY),
+                lineLimits = TextFieldLineLimits.MultiLine(maxHeightInLines = 3),
+                onTextLayout = { layoutResult = it },
+                modifier = Modifier
+                    .testTag(TAG)
+                    .width(100.dp)
+            )
+        }
+
+        rule.waitForIdle()
+        focusAndWait() // selection handles show up
+
+        swipeDown(Handle.SelectionEnd, layoutResult.size.height.toFloat())
+        swipeToRight(Handle.SelectionEnd, layoutResult.size.width.toFloat())
+        rule.runOnIdle {
+            assertThat(state.text.selectionInChars).isEqualTo(TextRange(0, 80))
+        }
+    }
+
+    @Test
+    fun dragStartSelectionHandle_extendsByWord() {
+        state = TextFieldState("abc def ghj", initialSelectionInChars = TextRange(4, 7))
+        rule.setContent {
+            BasicTextField2(
+                state,
+                textStyle = TextStyle(fontSize = fontSize, fontFamily = TEST_FONT_FAMILY),
+                modifier = Modifier
+                    .testTag(TAG)
+                    .width(200.dp)
+            )
+        }
+
+        focusAndWait()
+
+        swipeToLeft(Handle.SelectionStart, fontSizePx * 2) // only move by 2 characters
+        rule.runOnIdle {
+            // selection extends by a word
+            assertThat(state.text.selectionInChars).isEqualTo(TextRange(0, 7))
+        }
+    }
+
+    @Test
+    fun dragEndSelectionHandle_extendsByWord() {
+        state = TextFieldState("abc def ghj", initialSelectionInChars = TextRange(4, 7))
+        rule.setContent {
+            BasicTextField2(
+                state,
+                textStyle = TextStyle(fontSize = fontSize, fontFamily = TEST_FONT_FAMILY),
+                modifier = Modifier
+                    .testTag(TAG)
+                    .width(200.dp)
+            )
+        }
+
+        focusAndWait()
+
+        swipeToRight(Handle.SelectionEnd, fontSizePx * 2) // only move by 2 characters
+        rule.runOnIdle {
+            // selection extends by a word
+            assertThat(state.text.selectionInChars).isEqualTo(TextRange(4, 11))
+        }
+    }
+
+    @Test
+    fun dragStartSelectionHandle_shrinksByCharacter() {
+        state = TextFieldState("abc def ghj", initialSelectionInChars = TextRange(4, 7))
+        rule.setContent {
+            BasicTextField2(
+                state,
+                textStyle = TextStyle(fontSize = fontSize, fontFamily = TEST_FONT_FAMILY),
+                modifier = Modifier
+                    .testTag(TAG)
+                    .width(200.dp)
+            )
+        }
+
+        focusAndWait()
+
+        swipeToRight(Handle.SelectionStart, fontSizePx) // only move by a single character
+        rule.runOnIdle {
+            // selection shrinks by a character
+            assertThat(state.text.selectionInChars).isEqualTo(TextRange(5, 7))
+        }
+    }
+
+    @Test
+    fun dragEndSelectionHandle_shrinksByCharacter() {
+        state = TextFieldState("abc def ghj", initialSelectionInChars = TextRange(4, 7))
+        rule.setContent {
+            BasicTextField2(
+                state,
+                textStyle = TextStyle(fontSize = fontSize, fontFamily = TEST_FONT_FAMILY),
+                modifier = Modifier
+                    .testTag(TAG)
+                    .width(200.dp)
+            )
+        }
+
+        focusAndWait()
+
+        swipeToLeft(Handle.SelectionEnd, fontSizePx) // only move by a single character
+        rule.runOnIdle {
+            // selection shrinks by a character
+            assertThat(state.text.selectionInChars).isEqualTo(TextRange(4, 6))
+        }
+    }
+
+    @Test
+    fun dragStartSelectionHandle_cannotExtendSelectionPastEndHandle() {
+        state = TextFieldState("abc def ghj", initialSelectionInChars = TextRange(4, 7))
+        rule.setContent {
+            BasicTextField2(
+                state,
+                textStyle = TextStyle(fontSize = fontSize, fontFamily = TEST_FONT_FAMILY),
+                modifier = Modifier
+                    .testTag(TAG)
+                    .width(200.dp)
+            )
+        }
+
+        focusAndWait()
+
+        swipeToRight(Handle.SelectionStart, fontSizePx * 7)
+        rule.runOnIdle {
+            assertThat(state.text.selectionInChars).isEqualTo(TextRange(7))
+        }
+    }
+
+    @Test
+    fun dragEndSelectionHandle_cannotExtendSelectionPastStartHandle() {
+        state = TextFieldState("abc def ghj", initialSelectionInChars = TextRange(4, 7))
+        rule.setContent {
+            BasicTextField2(
+                state,
+                textStyle = TextStyle(fontSize = fontSize, fontFamily = TEST_FONT_FAMILY),
+                modifier = Modifier
+                    .testTag(TAG)
+                    .width(200.dp)
+            )
+        }
+
+        focusAndWait()
+
+        swipeToLeft(Handle.SelectionEnd, fontSizePx * 7)
+        rule.runOnIdle {
+            assertThat(state.text.selectionInChars).isEqualTo(TextRange(4))
+        }
+    }
+
     private fun focusAndWait() {
         rule.onNode(hasSetTextAction()).performSemanticsAction(SemanticsActions.RequestFocus)
     }
@@ -384,4 +724,57 @@
             rule.onNode(isSelectionHandle(Handle.SelectionEnd)).assertDoesNotExist()
         }
     }
+
+    private fun swipeUp(handle: Handle, swipeDistance: Float) =
+        performHandleDrag(handle, true, swipeDistance, Orientation.Vertical)
+
+    private fun swipeDown(handle: Handle, swipeDistance: Float) =
+        performHandleDrag(handle, false, swipeDistance, Orientation.Vertical)
+
+    private fun swipeToLeft(handle: Handle, swipeDistance: Float) =
+        performHandleDrag(handle, true, swipeDistance)
+
+    private fun swipeToRight(handle: Handle, swipeDistance: Float) =
+        performHandleDrag(handle, false, swipeDistance)
+
+    private fun performHandleDrag(
+        handle: Handle,
+        toStart: Boolean,
+        swipeDistance: Float = 1f,
+        orientation: Orientation = Orientation.Horizontal
+    ) {
+        val handleNode = rule.onNode(isSelectionHandle(handle))
+
+        handleNode.performTouchInput {
+            if (orientation == Orientation.Horizontal) {
+                if (toStart) {
+                    swipeLeft(
+                        startX = centerX,
+                        endX = centerX - viewConfiguration.touchSlop - swipeDistance,
+                        durationMillis = 1000
+                    )
+                } else {
+                    swipeRight(
+                        startX = centerX,
+                        endX = centerX + viewConfiguration.touchSlop + swipeDistance,
+                        durationMillis = 1000
+                    )
+                }
+            } else {
+                if (toStart) {
+                    swipeUp(
+                        startY = centerY,
+                        endY = centerY - viewConfiguration.touchSlop - swipeDistance,
+                        durationMillis = 1000
+                    )
+                } else {
+                    swipeDown(
+                        startY = centerY,
+                        endY = centerY + viewConfiguration.touchSlop + swipeDistance,
+                        durationMillis = 1000
+                    )
+                }
+            }
+        }
+    }
 }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/selection/TextFieldTextToolbarTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/selection/TextFieldTextToolbarTest.kt
index 3c1d00f..93e8de8 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/selection/TextFieldTextToolbarTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text2/selection/TextFieldTextToolbarTest.kt
@@ -27,6 +27,7 @@
 import androidx.compose.foundation.text.selection.FakeTextToolbar
 import androidx.compose.foundation.text.selection.isSelectionHandle
 import androidx.compose.foundation.text2.BasicTextField2
+import androidx.compose.foundation.text2.input.TextEditFilter
 import androidx.compose.foundation.text2.input.TextFieldLineLimits
 import androidx.compose.foundation.text2.input.TextFieldState
 import androidx.compose.foundation.text2.input.placeCursorAtEnd
@@ -52,8 +53,10 @@
 import androidx.compose.ui.test.performKeyInput
 import androidx.compose.ui.test.performMouseInput
 import androidx.compose.ui.test.performTextInput
+import androidx.compose.ui.test.performTextInputSelection
 import androidx.compose.ui.test.performTouchInput
 import androidx.compose.ui.test.pressKey
+import androidx.compose.ui.test.requestFocus
 import androidx.compose.ui.test.swipeLeft
 import androidx.compose.ui.test.swipeRight
 import androidx.compose.ui.text.AnnotatedString
@@ -91,9 +94,13 @@
         setupContent(state, textToolbar)
 
         rule.onNodeWithTag(TAG).performTouchInput { click(Offset(fontSizePx * 2, fontSizePx / 2)) }
-        assertThat(textToolbar.status).isEqualTo(TextToolbarStatus.Hidden)
+        rule.runOnIdle {
+            assertThat(textToolbar.status).isEqualTo(TextToolbarStatus.Hidden)
+        }
         rule.onNode(isSelectionHandle(Handle.Cursor)).performClick()
-        assertThat(textToolbar.status).isEqualTo(TextToolbarStatus.Shown)
+        rule.runOnIdle {
+            assertThat(textToolbar.status).isEqualTo(TextToolbarStatus.Shown)
+        }
     }
 
     @Test
@@ -104,9 +111,13 @@
 
         rule.onNodeWithTag(TAG).performTouchInput { click(Offset(fontSizePx * 2, fontSizePx / 2)) }
         rule.onNode(isSelectionHandle(Handle.Cursor)).performClick()
-        assertThat(textToolbar.status).isEqualTo(TextToolbarStatus.Shown)
+        rule.runOnIdle {
+            assertThat(textToolbar.status).isEqualTo(TextToolbarStatus.Shown)
+        }
         rule.onNode(isSelectionHandle(Handle.Cursor)).performClick()
-        assertThat(textToolbar.status).isEqualTo(TextToolbarStatus.Hidden)
+        rule.runOnIdle {
+            assertThat(textToolbar.status).isEqualTo(TextToolbarStatus.Hidden)
+        }
     }
 
     @Test
@@ -117,7 +128,9 @@
 
         rule.onNodeWithTag(TAG).performTouchInput { click(Offset(fontSizePx * 2, fontSizePx / 2)) }
         rule.onNode(isSelectionHandle(Handle.Cursor)).performClick()
-        assertThat(textToolbar.status).isEqualTo(TextToolbarStatus.Shown)
+        rule.runOnIdle {
+            assertThat(textToolbar.status).isEqualTo(TextToolbarStatus.Shown)
+        }
 
         state.edit {
             append(" World!")
@@ -137,7 +150,9 @@
 
         rule.onNodeWithTag(TAG).performTouchInput { click(Offset(fontSizePx * 2, fontSizePx / 2)) }
         rule.onNode(isSelectionHandle(Handle.Cursor)).performClick()
-        assertThat(textToolbar.status).isEqualTo(TextToolbarStatus.Shown)
+        rule.runOnIdle {
+            assertThat(textToolbar.status).isEqualTo(TextToolbarStatus.Shown)
+        }
 
         rule.onNodeWithTag(TAG).performTextInput(" World!")
 
@@ -155,7 +170,10 @@
 
         rule.onNodeWithTag(TAG).performTouchInput { click(Offset(fontSizePx * 2, fontSizePx / 2)) }
         rule.onNode(isSelectionHandle(Handle.Cursor)).performClick()
-        assertThat(textToolbar.status).isEqualTo(TextToolbarStatus.Shown)
+
+        rule.runOnIdle {
+            assertThat(textToolbar.status).isEqualTo(TextToolbarStatus.Shown)
+        }
 
         rule.onNodeWithTag(TAG).performKeyInput {
             pressKey(Key.W)
@@ -176,7 +194,9 @@
 
         with(rule.onNode(isSelectionHandle(Handle.Cursor))) {
             performClick()
-            assertThat(textToolbar.status).isEqualTo(TextToolbarStatus.Shown)
+            rule.runOnIdle {
+                assertThat(textToolbar.status).isEqualTo(TextToolbarStatus.Shown)
+            }
             performTouchInput {
                 down(center)
                 moveBy(Offset(viewConfiguration.touchSlop, 0f))
@@ -372,6 +392,142 @@
         }
     }
 
+    @OptIn(ExperimentalTestApi::class)
+    @Test
+    fun toolbarDoesNotShowCopyOrCut_whenSelectionIsCollapsed() {
+        var cutOptionAvailable = false
+        var copyOptionAvailable = false
+        val textToolbar = FakeTextToolbar(
+            onShowMenu = { _, onCopyRequested, _, onCutRequested, _ ->
+                copyOptionAvailable = onCopyRequested != null
+                cutOptionAvailable = onCutRequested != null
+            },
+            onHideMenu = {}
+        )
+        val state = TextFieldState("Hello")
+        setupContent(state, textToolbar, true)
+
+        rule.onNodeWithTag(TAG).requestFocus()
+        rule.onNodeWithTag(TAG).performTextInputSelection(TextRange(2, 2))
+
+        rule.runOnIdle {
+            assertThat(copyOptionAvailable).isFalse()
+            assertThat(cutOptionAvailable).isFalse()
+        }
+    }
+
+    @OptIn(ExperimentalTestApi::class)
+    @Test
+    fun toolbarShowsCopyAndCut_whenSelectionIsExpanded() {
+        var cutOptionAvailable = false
+        var copyOptionAvailable = false
+        val textToolbar = FakeTextToolbar(
+            onShowMenu = { _, onCopyRequested, _, onCutRequested, _ ->
+                copyOptionAvailable = onCopyRequested != null
+                cutOptionAvailable = onCutRequested != null
+            },
+            onHideMenu = {}
+        )
+        val state = TextFieldState("Hello")
+        setupContent(state, textToolbar, true)
+
+        rule.onNodeWithTag(TAG).requestFocus()
+        rule.onNodeWithTag(TAG).performTextInputSelection(TextRange(2, 4))
+
+        rule.runOnIdle {
+            assertThat(copyOptionAvailable).isTrue()
+            assertThat(cutOptionAvailable).isTrue()
+        }
+    }
+
+    @OptIn(ExperimentalTestApi::class)
+    @Test
+    fun copyUpdatesClipboardManager_placesCursorAtTheEndOfSelectedRegion() {
+        var copyOption: (() -> Unit)? = null
+        val textToolbar = FakeTextToolbar(
+            onShowMenu = { _, onCopyRequested, _, _, _ ->
+                copyOption = onCopyRequested
+            },
+            onHideMenu = {}
+        )
+        val clipboardManager = FakeClipboardManager()
+        val state = TextFieldState("Hello")
+        setupContent(state, textToolbar, true, clipboardManager)
+
+        rule.onNodeWithTag(TAG).requestFocus()
+        rule.onNodeWithTag(TAG).performTextInputSelection(TextRange(0, 5))
+
+        rule.runOnIdle {
+            copyOption!!.invoke()
+        }
+
+        rule.runOnIdle {
+            assertThat(clipboardManager.getText()?.toString()).isEqualTo("Hello")
+            assertThat(state.text.selectionInChars).isEqualTo(TextRange(5))
+        }
+    }
+
+    @OptIn(ExperimentalTestApi::class)
+    @Test
+    fun cutUpdatesClipboardManager_placesCursorAtTheEndOfSelectedRegion_removesTheCutContent() {
+        var cutOption: (() -> Unit)? = null
+        val textToolbar = FakeTextToolbar(
+            onShowMenu = { _, _, _, onCutRequested, _ ->
+                cutOption = onCutRequested
+            },
+            onHideMenu = {}
+        )
+        val clipboardManager = FakeClipboardManager()
+        val state = TextFieldState("Hello World!")
+        setupContent(state, textToolbar, true, clipboardManager)
+
+        rule.onNodeWithTag(TAG).requestFocus()
+        rule.onNodeWithTag(TAG).performTextInputSelection(TextRange(1, 5))
+
+        rule.runOnIdle {
+            cutOption!!.invoke()
+        }
+
+        rule.runOnIdle {
+            assertThat(clipboardManager.getText()?.toString()).isEqualTo("ello")
+            assertThat(state.text.toString()).isEqualTo("H World!")
+            assertThat(state.text.selectionInChars).isEqualTo(TextRange(1))
+        }
+    }
+
+    @OptIn(ExperimentalTestApi::class)
+    @Test
+    fun cutAppliesFilter() {
+        var cutOption: (() -> Unit)? = null
+        val textToolbar = FakeTextToolbar(
+            onShowMenu = { _, _, _, onCutRequested, _ ->
+                cutOption = onCutRequested
+            },
+            onHideMenu = {}
+        )
+        val clipboardManager = FakeClipboardManager()
+        val state = TextFieldState("Hello World!")
+        setupContent(state, textToolbar, true, clipboardManager) { original, changes ->
+            // only reject text changes, accept selection
+            val selection = changes.selectionInChars
+            changes.replace(0, changes.length, original.toString())
+            changes.selectCharsIn(selection)
+        }
+
+        rule.onNodeWithTag(TAG).requestFocus()
+        rule.onNodeWithTag(TAG).performTextInputSelection(TextRange(1, 5))
+
+        rule.runOnIdle {
+            cutOption!!.invoke()
+        }
+
+        rule.runOnIdle {
+            assertThat(clipboardManager.getText()?.toString()).isEqualTo("ello")
+            assertThat(state.text.toString()).isEqualTo("Hello World!")
+            assertThat(state.text.selectionInChars).isEqualTo(TextRange(1))
+        }
+    }
+
     @Test
     fun tappingTextField_hidesTheToolbar() {
         val textToolbar = FakeTextToolbar()
@@ -380,10 +536,15 @@
 
         rule.onNodeWithTag(TAG).performTouchInput { click(Offset(fontSizePx * 2, fontSizePx / 2)) }
         rule.onNode(isSelectionHandle(Handle.Cursor)).performClick()
-        assertThat(textToolbar.status).isEqualTo(TextToolbarStatus.Shown)
+        rule.runOnIdle {
+            assertThat(textToolbar.status).isEqualTo(TextToolbarStatus.Shown)
+        }
 
+        rule.mainClock.advanceTimeBy(1000) // to not cause double click
         rule.onNodeWithTag(TAG).performTouchInput { click(Offset(fontSizePx * 2, fontSizePx / 2)) }
-        assertThat(textToolbar.status).isEqualTo(TextToolbarStatus.Hidden)
+        rule.runOnIdle {
+            assertThat(textToolbar.status).isEqualTo(TextToolbarStatus.Hidden)
+        }
     }
 
     @OptIn(ExperimentalTestApi::class)
@@ -430,12 +591,13 @@
 
         rule.onNodeWithTag(TAG).performTouchInput { click(Offset(fontSizePx * 2, fontSizePx / 2)) }
         rule.onNode(isSelectionHandle(Handle.Cursor)).performClick()
-        assertThat(textToolbar.status).isEqualTo(TextToolbarStatus.Shown)
 
         rule.runOnIdle {
-            focusRequester.requestFocus()
+            assertThat(textToolbar.status).isEqualTo(TextToolbarStatus.Shown)
         }
 
+        focusRequester.requestFocus()
+
         rule.runOnIdle {
             assertThat(textToolbar.status).isEqualTo(TextToolbarStatus.Hidden)
         }
@@ -467,7 +629,10 @@
 
         rule.onNodeWithTag(TAG).performTouchInput { click(Offset(fontSizePx * 2, fontSizePx / 2)) }
         rule.onNode(isSelectionHandle(Handle.Cursor)).performClick()
-        assertThat(textToolbar.status).isEqualTo(TextToolbarStatus.Shown)
+
+        rule.runOnIdle {
+            assertThat(textToolbar.status).isEqualTo(TextToolbarStatus.Shown)
+        }
 
         toggleState.value = false
 
@@ -481,6 +646,7 @@
         toolbar: TextToolbar = FakeTextToolbar(),
         singleLine: Boolean = false,
         clipboardManager: ClipboardManager = FakeClipboardManager(),
+        filter: TextEditFilter? = null
     ) {
         rule.setContent {
             CompositionLocalProvider(
@@ -500,7 +666,8 @@
                         TextFieldLineLimits.SingleLine
                     } else {
                         TextFieldLineLimits.Default
-                    }
+                    },
+                    filter = filter
                 )
             }
         }
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/AndroidOverscroll.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/AndroidOverscroll.kt
index 48fccdc..2da447e 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/AndroidOverscroll.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/AndroidOverscroll.kt
@@ -27,9 +27,10 @@
 import androidx.compose.foundation.gestures.awaitEachGesture
 import androidx.compose.foundation.gestures.awaitFirstDown
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.neverEqualPolicy
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.DrawModifier
 import androidx.compose.ui.geometry.Offset
@@ -123,7 +124,9 @@
         allEffects.fastForEach { it.color = overscrollConfig.glowColor.toArgb() }
     }
 
-    private val redrawSignal = mutableStateOf(Unit, neverEqualPolicy())
+    // TODO replace with mutableStateOf(Unit, neverEqualPolicy()) after b/291647821 is addressed
+    private var consumeCount = -1
+    private var invalidateCount by mutableIntStateOf(0)
 
     @VisibleForTesting
     internal var invalidationEnabled = true
@@ -349,7 +352,7 @@
             return
         }
         this.drawIntoCanvas {
-            redrawSignal.value // <-- value read to redraw if needed
+            consumeCount = invalidateCount // <-- value read to redraw if needed
             val canvas = it.nativeCanvas
             var needsInvalidate = false
             // each side workflow:
@@ -435,7 +438,9 @@
 
     private fun invalidateOverscroll() {
         if (invalidationEnabled) {
-            redrawSignal.value = Unit
+            if (consumeCount == invalidateCount) {
+                invalidateCount++
+            }
         }
     }
 
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/Magnifier.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/Magnifier.kt
index 925b531..662a7b3 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/Magnifier.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/Magnifier.kt
@@ -17,41 +17,42 @@
 package androidx.compose.foundation
 
 import android.os.Build
+import android.view.View
 import android.widget.Magnifier
 import androidx.annotation.ChecksSdkIntAtLeast
-import androidx.annotation.RequiresApi
-import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.Stable
-import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.runtime.setValue
-import androidx.compose.runtime.snapshotFlow
+import androidx.compose.runtime.withFrameMillis
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.composed
-import androidx.compose.ui.draw.drawBehind
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.isSpecified
-import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.graphics.drawscope.ContentDrawScope
+import androidx.compose.ui.layout.LayoutCoordinates
 import androidx.compose.ui.layout.positionInRoot
+import androidx.compose.ui.node.CompositionLocalConsumerModifierNode
+import androidx.compose.ui.node.DrawModifierNode
+import androidx.compose.ui.node.GlobalPositionAwareModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.ObserverModifierNode
+import androidx.compose.ui.node.SemanticsModifierNode
+import androidx.compose.ui.node.currentValueOf
+import androidx.compose.ui.node.observeReads
+import androidx.compose.ui.platform.InspectorInfo
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalView
 import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.platform.inspectable
 import androidx.compose.ui.semantics.SemanticsPropertyKey
-import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.SemanticsPropertyReceiver
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.DpOffset
 import androidx.compose.ui.unit.DpSize
+import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.toSize
-import kotlinx.coroutines.channels.BufferOverflow
-import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.collect
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
 
 /**
  * A function on elements that are magnified with a [magnifier] modifier that returns the position
@@ -227,24 +228,57 @@
     zoom: Float = Float.NaN,
     style: MagnifierStyle = MagnifierStyle.Default,
     onSizeChanged: ((DpSize) -> Unit)? = null
-): Modifier = inspectable(
-    // Publish inspector info even if magnification isn't supported.
-    inspectorInfo = debugInspectorInfo {
-        name = if (isPlatformMagnifierSupported()) "magnifier" else "magnifier (not supported)"
-        properties["sourceCenter"] = sourceCenter
-        properties["magnifierCenter"] = magnifierCenter
-        properties["zoom"] = zoom
-        properties["style"] = style
+): Modifier {
+    return if (isPlatformMagnifierSupported()) {
+        then(
+            MagnifierElement(
+                sourceCenter = sourceCenter,
+                magnifierCenter = magnifierCenter,
+                zoom = zoom,
+                style = style,
+                onSizeChanged = onSizeChanged,
+                platformMagnifierFactory = PlatformMagnifierFactory.getForCurrentPlatform()
+            )
+        )
+    } else {
+        // Magnifier is only supported in >=28. So avoid doing all the work to manage the magnifier
+        // state if it's not needed.
+        // TODO(b/202739980) Investigate supporting Magnifier on earlier versions.
+        inspectable(
+            // Publish inspector info even if magnification isn't supported.
+            inspectorInfo = debugInspectorInfo {
+                name = "magnifier (not supported)"
+                properties["sourceCenter"] = sourceCenter
+                properties["magnifierCenter"] = magnifierCenter
+                properties["zoom"] = zoom
+                properties["style"] = style
+            }
+        ) { this }
     }
-) {
-    if (isPlatformMagnifierSupported()) {
-        magnifier(
-            sourceCenter = sourceCenter,
-            magnifierCenter = magnifierCenter,
-            zoom = zoom,
-            style = style,
-            onSizeChanged = onSizeChanged,
-            platformMagnifierFactory = PlatformMagnifierFactory.getForCurrentPlatform()
+}
+
+/**
+ * For testing purposes.
+ */
+@OptIn(ExperimentalFoundationApi::class)
+internal fun Modifier.magnifier(
+    sourceCenter: Density.() -> Offset,
+    magnifierCenter: Density.() -> Offset = { Offset.Unspecified },
+    zoom: Float = Float.NaN,
+    style: MagnifierStyle = MagnifierStyle.Default,
+    onSizeChanged: ((DpSize) -> Unit)? = null,
+    platformMagnifierFactory: PlatformMagnifierFactory
+): Modifier {
+    return if (isPlatformMagnifierSupported()) {
+        then(
+            MagnifierElement(
+                sourceCenter = sourceCenter,
+                magnifierCenter = magnifierCenter,
+                zoom = zoom,
+                style = style,
+                onSizeChanged = onSizeChanged,
+                platformMagnifierFactory = platformMagnifierFactory
+            )
         )
     } else {
         // Magnifier is only supported in >=28. So avoid doing all the work to manage the magnifier
@@ -254,136 +288,246 @@
     }
 }
 
-/**
- * @param platformMagnifierFactory Creates a [PlatformMagnifier] whenever the configuration changes.
- */
 @OptIn(ExperimentalFoundationApi::class)
-// The InspectorInfo this modifier reports is for the above public overload, and intentionally
-// doesn't include the platformMagnifierFactory parameter.
-@RequiresApi(28)
-internal fun Modifier.magnifier(
-    sourceCenter: Density.() -> Offset,
-    magnifierCenter: Density.() -> Offset,
-    zoom: Float,
-    style: MagnifierStyle,
-    onSizeChanged: ((DpSize) -> Unit)?,
-    platformMagnifierFactory: PlatformMagnifierFactory
-): Modifier = composed {
-    val view = LocalView.current
-    val density = LocalDensity.current
-    var anchorPositionInRoot: Offset by remember { mutableStateOf(Offset.Unspecified) }
-    val updatedSourceCenter by rememberUpdatedState(sourceCenter)
-    val updatedMagnifierCenter by rememberUpdatedState(magnifierCenter)
-    val updatedZoom by rememberUpdatedState(zoom)
-    val updatedOnSizeChanged by rememberUpdatedState(onSizeChanged)
-    val sourceCenterInRoot by remember {
-        derivedStateOf {
-            val sourceCenterOffset = updatedSourceCenter(density)
+internal class MagnifierElement(
+    private val sourceCenter: Density.() -> Offset,
+    private val magnifierCenter: Density.() -> Offset = { Offset.Unspecified },
+    private val zoom: Float = Float.NaN,
+    private val style: MagnifierStyle = MagnifierStyle.Default,
+    private val onSizeChanged: ((DpSize) -> Unit)? = null,
+    private val platformMagnifierFactory: PlatformMagnifierFactory
+) : ModifierNodeElement<MagnifierNode>() {
+
+    override fun create(): MagnifierNode {
+        return MagnifierNode(
+            sourceCenter = sourceCenter,
+            magnifierCenter = magnifierCenter,
+            zoom = zoom,
+            style = style,
+            onSizeChanged = onSizeChanged,
+            platformMagnifierFactory = platformMagnifierFactory
+        )
+    }
+
+    override fun update(node: MagnifierNode) {
+        node.update(
+            sourceCenter = sourceCenter,
+            magnifierCenter = magnifierCenter,
+            zoom = zoom,
+            style = style,
+            onSizeChanged = onSizeChanged,
+            platformMagnifierFactory = platformMagnifierFactory
+        )
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is MagnifierElement) return false
+
+        if (sourceCenter != other.sourceCenter) return false
+        if (magnifierCenter != other.magnifierCenter) return false
+        if (zoom != other.zoom) return false
+        if (style != other.style) return false
+        if (onSizeChanged != other.onSizeChanged) return false
+        if (platformMagnifierFactory != other.platformMagnifierFactory) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = sourceCenter.hashCode()
+        result = 31 * result + magnifierCenter.hashCode()
+        result = 31 * result + zoom.hashCode()
+        result = 31 * result + style.hashCode()
+        result = 31 * result + (onSizeChanged?.hashCode() ?: 0)
+        result = 31 * result + platformMagnifierFactory.hashCode()
+        return result
+    }
+
+    override fun InspectorInfo.inspectableProperties() {
+        name = "magnifier"
+        properties["sourceCenter"] = sourceCenter
+        properties["magnifierCenter"] = magnifierCenter
+        properties["zoom"] = zoom
+        properties["style"] = style
+    }
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+internal class MagnifierNode(
+    var sourceCenter: Density.() -> Offset,
+    var magnifierCenter: Density.() -> Offset = { Offset.Unspecified },
+    var zoom: Float = Float.NaN,
+    var style: MagnifierStyle = MagnifierStyle.Default,
+    var onSizeChanged: ((DpSize) -> Unit)? = null,
+    var platformMagnifierFactory: PlatformMagnifierFactory =
+        PlatformMagnifierFactory.getForCurrentPlatform()
+) : Modifier.Node(),
+    CompositionLocalConsumerModifierNode,
+    GlobalPositionAwareModifierNode,
+    DrawModifierNode,
+    SemanticsModifierNode,
+    ObserverModifierNode {
+
+    /**
+     * Android [View] that this modifier node is attached to in Compose hierarchy.
+     */
+    private var view: View? = null
+
+    /**
+     * Current density provided by [LocalDensity]. Used as a receiver to callback functions that
+     * are expected return pixel targeted offsets.
+     */
+    private var density: Density? = null
+
+    /**
+     * Current magnifier instance.
+     */
+    private var magnifier: PlatformMagnifier? = null
+
+    /**
+     * Anchor Composable's position in root layout.
+     */
+    private var anchorPositionInRoot: Offset by mutableStateOf(Offset.Unspecified)
+
+    /**
+     * Position where [sourceCenter] is mapped on root layout. This is passed to platform magnifier
+     * to precisely target the requested location.
+     */
+    private var sourceCenterInRoot: Offset = Offset.Unspecified
+
+    /**
+     * Last reported size to [onSizeChanged]. This is compared to the current size before calling
+     * the lambda again.
+     */
+    private var previousSize: IntSize? = null
+
+    fun update(
+        sourceCenter: Density.() -> Offset,
+        magnifierCenter: Density.() -> Offset,
+        zoom: Float,
+        style: MagnifierStyle,
+        onSizeChanged: ((DpSize) -> Unit)?,
+        platformMagnifierFactory: PlatformMagnifierFactory
+    ) {
+        val previousZoom = this.zoom
+        val previousStyle = this.style
+        val previousPlatformMagnifierFactory = this.platformMagnifierFactory
+
+        this.sourceCenter = sourceCenter
+        this.magnifierCenter = magnifierCenter
+        this.zoom = zoom
+        this.style = style
+        this.onSizeChanged = onSizeChanged
+        this.platformMagnifierFactory = platformMagnifierFactory
+
+        // On platforms >=Q, the zoom level can be updated dynamically on an existing magnifier, so
+        // if the zoom changes between recompositions we don't need to recreate the magnifier. On
+        // older platforms, the zoom can only be set initially, so we use the zoom itself as a key
+        // so the magnifier gets recreated if it changes.
+        if ((zoom != previousZoom && !platformMagnifierFactory.canUpdateZoom) ||
+            style != previousStyle ||
+            platformMagnifierFactory != previousPlatformMagnifierFactory) {
+            recreateMagnifier()
+        }
+        updateMagnifier()
+    }
+
+    override fun onAttach() {
+        onObservedReadsChanged()
+    }
+
+    override fun onDetach() {
+        magnifier?.dismiss()
+        magnifier = null
+    }
+
+    override fun onObservedReadsChanged() {
+        observeReads {
+            val previousView = view
+            val view = currentValueOf(LocalView).also { this.view = it }
+            val previousDensity = density
+            val density = currentValueOf(LocalDensity).also { this.density = it }
+
+            if (view != previousView || density != previousDensity) {
+                recreateMagnifier()
+            }
+
+            updateMagnifier()
+        }
+    }
+
+    private fun recreateMagnifier() {
+        magnifier?.dismiss()
+        val view = view ?: return
+        val density = density ?: return
+        magnifier = platformMagnifierFactory.create(style, view, density, zoom)
+        updateSizeIfNecessary()
+    }
+
+    private fun updateMagnifier() {
+        val magnifier = magnifier ?: return
+        val density = density ?: return
+
+        val sourceCenterOffset = sourceCenter(density)
+        sourceCenterInRoot =
             if (anchorPositionInRoot.isSpecified && sourceCenterOffset.isSpecified) {
                 anchorPositionInRoot + sourceCenterOffset
             } else {
                 Offset.Unspecified
             }
-        }
-    }
-    val isMagnifierShown by remember { derivedStateOf { sourceCenterInRoot.isSpecified } }
 
-    /**
-     * Used to request that the magnifier updates its buffer when the layer is redrawn.
-     */
-    val onNeedsUpdate = remember {
-        MutableSharedFlow<Unit>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
-    }
-
-    // On platforms >=Q, the zoom level can be updated dynamically on an existing magnifier, so if
-    // the zoom changes between recompositions we don't need to recreate the magnifier. On older
-    // platforms, the zoom can only be set initially, so we use the zoom itself as a key so the
-    // magnifier gets recreated if it changes.
-    val zoomEffectKey = if (platformMagnifierFactory.canUpdateZoom) 0f else zoom
-
-    // centerOffset, sourceToMagnifierOffset, and zoom are not in this key list because they can be
-    // updated without re-creating the Magnifier.
-    LaunchedEffect(
-        view,
-        density,
-        zoomEffectKey,
-        style,
-        // This is a separate key because otherwise a change from Default to TextDefault won't
-        // reconfigure. Note that this checks for reference equality, not structural equality, since
-        // TextDefault == Default already.
-        style == MagnifierStyle.TextDefault
-    ) {
-        val magnifier = platformMagnifierFactory.create(style, view, density, zoom)
-        var previousSize = magnifier.size.also { newSize ->
-            updatedOnSizeChanged?.invoke(
-                with(density) {
-                    newSize.toSize().toDpSize()
-                }
-            )
-        }
-
-        // Ask the magnifier to do another pixel copy whenever the nearest layer is redrawn.
-        onNeedsUpdate
-            .onEach { magnifier.updateContent() }
-            .launchIn(this)
-
-        try {
-            // Update the modifier in a snapshotFlow so it will be restarted whenever any state read
-            // by the update function changes.
-            snapshotFlow {
-                // Once the position is set, it's never null again, so we don't need to worry about
-                // dismissing the magnifier if this expression changes value.
-                if (isMagnifierShown) {
-                    magnifier.update(
-                        sourceCenter = sourceCenterInRoot,
-                        magnifierCenter = updatedMagnifierCenter(density).let {
-                            if (it.isSpecified) {
-                                anchorPositionInRoot + it
-                            } else {
-                                Offset.Unspecified
-                            }
-                        },
-                        zoom = updatedZoom
-                    )
-
-                    magnifier.size.let { size ->
-                        if (size != previousSize) {
-                            previousSize = size
-                            updatedOnSizeChanged?.invoke(
-                                with(density) {
-                                    size.toSize().toDpSize()
-                                }
-                            )
-                        }
+        // Once the position is set, it's never null again, so we don't need to worry
+        // about dismissing the magnifier if this expression changes value.
+        if (sourceCenterInRoot.isSpecified) {
+            magnifier.update(
+                sourceCenter = sourceCenterInRoot,
+                magnifierCenter = magnifierCenter(density).let {
+                    if (it.isSpecified) {
+                        anchorPositionInRoot + it
+                    } else {
+                        Offset.Unspecified
                     }
-                } else {
-                    // Can't place the magnifier at an unspecified location, so just hide it.
-                    magnifier.dismiss()
-                }
-            }.collect()
-        } finally {
-            // Dismiss the magnifier whenever it needs to be recreated or it's removed from the
-            // composition.
+                },
+                zoom = zoom
+            )
+            updateSizeIfNecessary()
+        } else {
+            // Can't place the magnifier at an unspecified location, so just hide it.
             magnifier.dismiss()
         }
     }
 
-    return@composed this
-        .onGloballyPositioned {
-            // The mutable state must store the Offset, not the LocalCoordinates, because the same
-            // LocalCoordinates instance may be sent to this callback multiple times, not implement
-            // equals, or be stable, and so won't invalidate the snapshotFlow.
-            anchorPositionInRoot = it.positionInRoot()
+    private fun updateSizeIfNecessary() {
+        val magnifier = magnifier ?: return
+        val density = density ?: return
+
+        if (magnifier.size != previousSize) {
+            onSizeChanged?.invoke(with(density) { magnifier.size.toSize().toDpSize() })
+            previousSize = magnifier.size
         }
-        .drawBehind {
-            // Tell the magnifier to update itself every time the layer is re-drawn.
-            // Note that this won't do anything if the magnifier is showing a different layer,
-            // but it handles the case where the cursor is blinking so it's better than nothing.
-            onNeedsUpdate.tryEmit(Unit)
+    }
+
+    override fun ContentDrawScope.draw() {
+        drawContent()
+        // don't update the magnifier immediately, actual frame draw happens right after all draw
+        // commands are recorded. Magnifier update should happen in the next frame.
+        coroutineScope.launch {
+            withFrameMillis { }
+            magnifier?.updateContent()
         }
-        .semantics {
-            this[MagnifierPositionInRoot] = { sourceCenterInRoot }
-        }
+    }
+
+    override fun onGloballyPositioned(coordinates: LayoutCoordinates) {
+        // The mutable state must store the Offset, not the LocalCoordinates, because the same
+        // LocalCoordinates instance may be sent to this callback multiple times, not implement
+        // equals, or be stable, and so won't invalidate the snapshotFlow.
+        anchorPositionInRoot = coordinates.positionInRoot()
+    }
+
+    override fun SemanticsPropertyReceiver.applySemantics() {
+        this[MagnifierPositionInRoot] = { sourceCenterInRoot }
+    }
 }
 
 @ChecksSdkIntAtLeast(api = 28)
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/StatelessInputConnection.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/StatelessInputConnection.android.kt
index ead875a..c2dfcfc 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/StatelessInputConnection.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/StatelessInputConnection.android.kt
@@ -71,25 +71,6 @@
     private val editCommands = mutableListOf<EditCommand>()
 
     /**
-     * If this InputConnection itself is active. This value becomes false only if [closeConnection]
-     * gets called.
-     */
-    private var isICActive: Boolean = true
-
-    /**
-     * Returns whether this input connection is still active and also executes the given lambda if
-     * it is active.
-     */
-    private inline fun ensureActive(block: () -> Unit): Boolean {
-        val combinedActive = isICActive
-        return combinedActive.also {
-            if (it) {
-                block()
-            }
-        }
-    }
-
-    /**
      * Add edit op to internal list with wrapping batch edit. It's not guaranteed by IME that
      * batch editing will be used for every operation. Instead, [StatelessInputConnection] creates
      * its own mini batches for every edit op. These batches are only applied when batch depth
@@ -106,7 +87,7 @@
     }
 
     // region Methods for batch editing and session control
-    override fun beginBatchEdit(): Boolean = ensureActive {
+    override fun beginBatchEdit(): Boolean {
         logDebug("beginBatchEdit()")
         return beginBatchEditInternal()
     }
@@ -135,58 +116,57 @@
         logDebug("closeConnection()")
         editCommands.clear()
         batchDepth = 0
-        isICActive = false
     }
 
     //endregion
 
     // region Callbacks for text editing
 
-    override fun commitText(text: CharSequence?, newCursorPosition: Int): Boolean = ensureActive {
+    override fun commitText(text: CharSequence?, newCursorPosition: Int): Boolean {
         logDebug("commitText(\"$text\", $newCursorPosition)")
         addEditCommandWithBatch(CommitTextCommand(text.toString(), newCursorPosition))
+        return true
     }
 
-    override fun setComposingRegion(start: Int, end: Int): Boolean = ensureActive {
+    override fun setComposingRegion(start: Int, end: Int): Boolean {
         logDebug("setComposingRegion($start, $end)")
         addEditCommandWithBatch(SetComposingRegionCommand(start, end))
+        return true
     }
 
-    override fun setComposingText(text: CharSequence?, newCursorPosition: Int): Boolean =
-        ensureActive {
-            logDebug("setComposingText(\"$text\", $newCursorPosition)")
-            addEditCommandWithBatch(SetComposingTextCommand(text.toString(), newCursorPosition))
-        }
+    override fun setComposingText(text: CharSequence?, newCursorPosition: Int): Boolean {
+        logDebug("setComposingText(\"$text\", $newCursorPosition)")
+        addEditCommandWithBatch(SetComposingTextCommand(text.toString(), newCursorPosition))
+        return true
+    }
 
-    override fun deleteSurroundingTextInCodePoints(beforeLength: Int, afterLength: Int): Boolean =
-        ensureActive {
-            logDebug("deleteSurroundingTextInCodePoints($beforeLength, $afterLength)")
-            addEditCommandWithBatch(
-                DeleteSurroundingTextInCodePointsCommand(beforeLength, afterLength)
-            )
-            return true
-        }
+    override fun deleteSurroundingTextInCodePoints(beforeLength: Int, afterLength: Int): Boolean {
+        logDebug("deleteSurroundingTextInCodePoints($beforeLength, $afterLength)")
+        addEditCommandWithBatch(
+            DeleteSurroundingTextInCodePointsCommand(beforeLength, afterLength)
+        )
+        return true
+    }
 
-    override fun deleteSurroundingText(beforeLength: Int, afterLength: Int): Boolean =
-        ensureActive {
-            logDebug("deleteSurroundingText($beforeLength, $afterLength)")
-            addEditCommandWithBatch(DeleteSurroundingTextCommand(beforeLength, afterLength))
-            return true
-        }
+    override fun deleteSurroundingText(beforeLength: Int, afterLength: Int): Boolean {
+        logDebug("deleteSurroundingText($beforeLength, $afterLength)")
+        addEditCommandWithBatch(DeleteSurroundingTextCommand(beforeLength, afterLength))
+        return true
+    }
 
-    override fun setSelection(start: Int, end: Int): Boolean = ensureActive {
+    override fun setSelection(start: Int, end: Int): Boolean {
         logDebug("setSelection($start, $end)")
         addEditCommandWithBatch(SetSelectionCommand(start, end))
         return true
     }
 
-    override fun finishComposingText(): Boolean = ensureActive {
+    override fun finishComposingText(): Boolean {
         logDebug("finishComposingText()")
         addEditCommandWithBatch(FinishComposingTextCommand)
         return true
     }
 
-    override fun sendKeyEvent(event: KeyEvent): Boolean = ensureActive {
+    override fun sendKeyEvent(event: KeyEvent): Boolean {
         logDebug("sendKeyEvent($event)")
         session.sendKeyEvent(event)
         return true
@@ -222,7 +202,7 @@
         return result
     }
 
-    override fun requestCursorUpdates(cursorUpdateMode: Int): Boolean = ensureActive {
+    override fun requestCursorUpdates(cursorUpdateMode: Int): Boolean {
         logDebug("requestCursorUpdates($cursorUpdateMode)")
         return false
     }
@@ -247,7 +227,7 @@
 
     // region Editor action and Key events.
 
-    override fun performContextMenuAction(id: Int): Boolean = ensureActive {
+    override fun performContextMenuAction(id: Int): Boolean {
         logDebug("performContextMenuAction($id)")
         when (id) {
             android.R.id.selectAll -> {
@@ -273,7 +253,7 @@
         sendKeyEvent(KeyEvent(KeyEvent.ACTION_UP, code))
     }
 
-    override fun performEditorAction(editorAction: Int): Boolean = ensureActive {
+    override fun performEditorAction(editorAction: Int): Boolean {
         logDebug("performEditorAction($editorAction)")
 
         val imeAction = when (editorAction) {
@@ -336,7 +316,7 @@
         return false // This value is ignored according to the API docs.
     }
 
-    override fun performPrivateCommand(action: String?, data: Bundle?): Boolean = ensureActive {
+    override fun performPrivateCommand(action: String?, data: Bundle?): Boolean {
         logDebug("performPrivateCommand($action, $data)")
         return true // API doc says we should return true even if we didn't understand the command.
     }
@@ -345,7 +325,7 @@
         inputContentInfo: InputContentInfo,
         flags: Int,
         opts: Bundle?
-    ): Boolean = ensureActive {
+    ): Boolean {
         logDebug("commitContent($inputContentInfo, $flags, $opts)")
         // TODO(halilibo): Support commit content in BasicTextField2
         return false
@@ -355,7 +335,7 @@
 
     private fun logDebug(message: String) {
         if (SIC_DEBUG) {
-            Log.d(TAG, "$DEBUG_CLASS.$message, $isICActive")
+            Log.d(TAG, "$DEBUG_CLASS.$message")
         }
     }
 }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Background.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Background.kt
index fd8caf9..2a770bc 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Background.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Background.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.foundation
 
+import androidx.annotation.FloatRange
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.Brush
@@ -72,7 +73,7 @@
 fun Modifier.background(
     brush: Brush,
     shape: Shape = RectangleShape,
-    /*@FloatRange(from = 0.0, to = 1.0)*/
+    @FloatRange(from = 0.0, to = 1.0)
     alpha: Float = 1.0f
 ) = this.then(
     BackgroundElement(
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
index 87e9610..f117895 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
@@ -432,7 +432,8 @@
 
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
-        if (javaClass != other?.javaClass) return false
+        if (other === null) return false
+        if (this::class != other::class) return false
 
         other as ClickableElement
 
@@ -494,7 +495,8 @@
 
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
-        if (javaClass != other?.javaClass) return false
+        if (other === null) return false
+        if (this::class != other::class) return false
 
         other as CombinedClickableElement
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/DarkTheme.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/DarkTheme.kt
index f56a5200..ae18a72 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/DarkTheme.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/DarkTheme.kt
@@ -40,4 +40,6 @@
 @ReadOnlyComposable
 fun isSystemInDarkTheme() = _isSystemInDarkTheme()
 
+@Composable
+@ReadOnlyComposable
 internal expect fun _isSystemInDarkTheme(): Boolean
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/ProgressSemantics.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/ProgressSemantics.kt
index 2c5e67d..a6a7b72 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/ProgressSemantics.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/ProgressSemantics.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.foundation
 
+import androidx.annotation.IntRange
 import androidx.compose.runtime.Stable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.semantics.ProgressBarRangeInfo
@@ -41,7 +42,7 @@
 fun Modifier.progressSemantics(
     value: Float,
     valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
-    /*@IntRange(from = 0)*/
+    @IntRange(from = 0)
     steps: Int = 0
 ): Modifier {
     // Older versions of Talkback will ignore nodes with range info which aren't focusable or
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.kt
index 2426948..327eed6 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.foundation.gestures
 
+import androidx.annotation.FloatRange
 import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.animate
 import androidx.compose.foundation.ExperimentalFoundationApi
@@ -352,7 +353,7 @@
      * The fraction of the progress going from [currentValue] to [closestValue], within [0f..1f]
      * bounds, or 1f if the [AnchoredDraggableState] is in a settled state.
      */
-    /*@FloatRange(from = 0f, to = 1f)*/
+    @get:FloatRange(from = 0.0, to = 1.0)
     val progress: Float by derivedStateOf(structuralEqualityPolicy()) {
         val a = anchors.positionOf(currentValue)
         val b = anchors.positionOf(closestValue)
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable.kt
index ca3d8a2..7a08add 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable.kt
@@ -51,7 +51,6 @@
 import kotlin.coroutines.cancellation.CancellationException
 import kotlin.math.sign
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.CoroutineStart
 import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.channels.SendChannel
 import kotlinx.coroutines.coroutineScope
@@ -168,10 +167,15 @@
  * pressing on it. It's useful to set it when value you're dragging is settling / animating.
  * @param onDragStarted callback that will be invoked when drag is about to start at the starting
  * position, allowing user to suspend and perform preparation for drag, if desired. This suspend
- * function is invoked with the draggable scope, allowing for async processing, if desired
+ * function is invoked with the draggable scope, allowing for async processing, if desired. Note
+ * that the scope used here is the one provided by the draggable node, for long running work that
+ * needs to outlast the modifier being in the composition you should use a scope that fits the
+ * lifecycle needed.
  * @param onDragStopped callback that will be invoked when drag is finished, allowing the
  * user to react on velocity and process it. This suspend function is invoked with the draggable
- * scope, allowing for async processing, if desired
+ * scope, allowing for async processing, if desired.  Note that the scope used here is the one
+ * provided by the draggable node, for long running work that needs to outlast the modifier being
+ * in the composition you should use a scope that fits the lifecycle needed.
  * @param reverseDirection reverse the direction of the scroll, so top to bottom scroll will
  * behave like bottom to top and left to right will behave like right to left.
  */
@@ -235,7 +239,8 @@
 
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
-        if (javaClass != other?.javaClass) return false
+        if (other === null) return false
+        if (this::class != other::class) return false
 
         other as DraggableElement
 
@@ -296,32 +301,42 @@
     private val _startDragImmediately: () -> Boolean = { startDragImmediately() }
     private val velocityTracker = VelocityTracker()
 
+    /**
+     * To preserve the original behavior we had (before the Modifier.Node migration) we need to
+     * scope the DragStopped and DragCancel methods to the node's coroutine scope instead of using
+     * the one provided by the pointer input modifier, this is to ensure that even when the pointer
+     * input scope is reset we will continue any coroutine scope scope that we started from these
+     * methods while the pointer input scope was active.
+     */
+    override fun onAttach() {
+        coroutineScope.launch {
+            while (isActive) {
+                var event = channel.receive()
+                if (event !is DragStarted) continue
+                processDragStart(event)
+                try {
+                    state.drag(MutatePriority.UserInput) {
+                        while (event !is DragStopped && event !is DragCancelled) {
+                            (event as? DragDelta)?.let { dragBy(it.delta.toFloat(orientation)) }
+                            event = channel.receive()
+                        }
+                    }
+                    if (event is DragStopped) {
+                        processDragStop(event as DragStopped)
+                    } else if (event is DragCancelled) {
+                        processDragCancel()
+                    }
+                } catch (c: CancellationException) {
+                    processDragCancel()
+                }
+            }
+        }
+    }
+
     private val pointerInputNode = delegate(SuspendingPointerInputModifierNode {
         // TODO: conditionally undelegate when aosp/2462416 lands?
         if (!enabled) return@SuspendingPointerInputModifierNode
         coroutineScope {
-            launch(start = CoroutineStart.UNDISPATCHED) {
-                while (isActive) {
-                    var event = channel.receive()
-                    if (event !is DragStarted) continue
-                    processDragStart(event)
-                    try {
-                        state.drag(MutatePriority.UserInput) {
-                            while (event !is DragStopped && event !is DragCancelled) {
-                                (event as? DragDelta)?.let { dragBy(it.delta.toFloat(orientation)) }
-                                event = channel.receive()
-                            }
-                        }
-                        if (event is DragStopped) {
-                            processDragStop(event as DragStopped)
-                        } else if (event is DragCancelled) {
-                            processDragCancel()
-                        }
-                    } catch (c: CancellationException) {
-                        processDragCancel()
-                    }
-                }
-            }
             try {
                 awaitPointerEventScope {
                     while (isActive) {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Transformable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Transformable.kt
index b183823..1e4abd9 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Transformable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Transformable.kt
@@ -21,27 +21,25 @@
 import androidx.compose.foundation.gestures.TransformEvent.TransformDelta
 import androidx.compose.foundation.gestures.TransformEvent.TransformStarted
 import androidx.compose.foundation.gestures.TransformEvent.TransformStopped
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.State
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.composed
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.input.pointer.AwaitPointerEventScope
 import androidx.compose.ui.input.pointer.PointerEventPass
-import androidx.compose.ui.input.pointer.PointerInputScope
-import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode
 import androidx.compose.ui.input.pointer.positionChanged
-import androidx.compose.ui.platform.debugInspectorInfo
+import androidx.compose.ui.node.DelegatingNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.platform.InspectorInfo
 import androidx.compose.ui.util.fastAny
 import androidx.compose.ui.util.fastForEach
 import kotlin.math.PI
 import kotlin.math.abs
 import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.CoroutineStart
 import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.isActive
+import kotlinx.coroutines.launch
 
 /**
  * Enable transformation gestures of the modified UI element.
@@ -94,13 +92,68 @@
     canPan: (Offset) -> Boolean,
     lockRotationOnZoomPan: Boolean = false,
     enabled: Boolean = true
-) = composed(
-    factory = {
-        val updatePanZoomLock = rememberUpdatedState(lockRotationOnZoomPan)
-        val updatedCanPan = rememberUpdatedState(canPan)
-        val channel = remember { Channel<TransformEvent>(capacity = Channel.UNLIMITED) }
-        if (enabled) {
-            LaunchedEffect(state) {
+) = this then TransformableElement(state, canPan, lockRotationOnZoomPan, enabled)
+
+private class TransformableElement(
+    private val state: TransformableState,
+    private val canPan: (Offset) -> Boolean,
+    private val lockRotationOnZoomPan: Boolean,
+    private val enabled: Boolean
+) : ModifierNodeElement<TransformableNode>() {
+    override fun create(): TransformableNode = TransformableNode(
+        state, canPan, lockRotationOnZoomPan, enabled
+    )
+
+    override fun update(node: TransformableNode) {
+        node.update(state, canPan, lockRotationOnZoomPan, enabled)
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other === null) return false
+        if (this::class != other::class) return false
+
+        other as TransformableElement
+
+        if (state != other.state) return false
+        if (canPan != other.canPan) return false
+        if (lockRotationOnZoomPan != other.lockRotationOnZoomPan) return false
+        if (enabled != other.enabled) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = state.hashCode()
+        result = 31 * result + canPan.hashCode()
+        result = 31 * result + lockRotationOnZoomPan.hashCode()
+        result = 31 * result + enabled.hashCode()
+        return result
+    }
+
+    override fun InspectorInfo.inspectableProperties() {
+        name = "transformable"
+        properties["state"] = state
+        properties["canPan"] = canPan
+        properties["enabled"] = enabled
+        properties["lockRotationOnZoomPan"] = lockRotationOnZoomPan
+    }
+}
+
+private class TransformableNode(
+    private var state: TransformableState,
+    private var canPan: (Offset) -> Boolean,
+    private var lockRotationOnZoomPan: Boolean,
+    private var enabled: Boolean
+) : DelegatingNode() {
+
+    private val updatedCanPan: (Offset) -> Boolean = { canPan.invoke(it) }
+    private val channel = Channel<TransformEvent>(capacity = Channel.UNLIMITED)
+
+    private val pointerInputNode = delegate(SuspendingPointerInputModifierNode {
+        if (!enabled) return@SuspendingPointerInputModifierNode
+        coroutineScope {
+            launch(start = CoroutineStart.UNDISPATCHED) {
                 while (isActive) {
                     var event = channel.receive()
                     if (event !is TransformStarted) continue
@@ -118,47 +171,41 @@
                     }
                 }
             }
-        }
-        val block: suspend PointerInputScope.() -> Unit = remember {
-            {
-                coroutineScope {
-                    awaitEachGesture {
-                        try {
-                            detectZoom(updatePanZoomLock, channel, updatedCanPan)
-                        } catch (exception: CancellationException) {
-                            if (!isActive) throw exception
-                        } finally {
-                            channel.trySend(TransformStopped)
-                        }
-                    }
+            awaitEachGesture {
+                try {
+                    detectZoom(lockRotationOnZoomPan, channel, updatedCanPan)
+                } catch (exception: CancellationException) {
+                    if (!isActive) throw exception
+                } finally {
+                    channel.trySend(TransformStopped)
                 }
             }
         }
-        if (enabled) Modifier.pointerInput(channel, block) else Modifier
-    },
-    inspectorInfo = debugInspectorInfo {
-        name = "transformable"
-        properties["state"] = state
-        properties["canPan"] = canPan
-        properties["enabled"] = enabled
-        properties["lockRotationOnZoomPan"] = lockRotationOnZoomPan
-    }
-)
+    })
 
-private sealed class TransformEvent {
-    object TransformStarted : TransformEvent()
-    object TransformStopped : TransformEvent()
-    class TransformDelta(
-        val zoomChange: Float,
-        val panChange: Offset,
-        val rotationChange: Float
-    ) : TransformEvent()
+    fun update(
+        state: TransformableState,
+        canPan: (Offset) -> Boolean,
+        lockRotationOnZoomPan: Boolean,
+        enabled: Boolean
+    ) {
+        this.canPan = canPan
+        val needsReset = this.state != state ||
+            this.enabled != enabled ||
+            this.lockRotationOnZoomPan != lockRotationOnZoomPan
+        if (needsReset) {
+            this.state = state
+            this.enabled = enabled
+            this.lockRotationOnZoomPan = lockRotationOnZoomPan
+            pointerInputNode.resetPointerInputHandler()
+        }
+    }
 }
 
 private suspend fun AwaitPointerEventScope.detectZoom(
-    panZoomLock: State<Boolean>,
+    panZoomLock: Boolean,
     channel: Channel<TransformEvent>,
-    canPan: State<(Offset) -> Boolean>
+    canPan: (Offset) -> Boolean
 ) {
     var rotation = 0f
     var zoom = 1f
@@ -187,10 +234,10 @@
 
                 if (zoomMotion > touchSlop ||
                     rotationMotion > touchSlop ||
-                    (panMotion > touchSlop && canPan.value.invoke(panChange))
+                    (panMotion > touchSlop && canPan.invoke(panChange))
                 ) {
                     pastTouchSlop = true
-                    lockedToPanZoom = panZoomLock.value && rotationMotion < touchSlop
+                    lockedToPanZoom = panZoomLock && rotationMotion < touchSlop
                     channel.trySend(TransformStarted)
                 }
             }
@@ -199,7 +246,7 @@
                 val effectiveRotation = if (lockedToPanZoom) 0f else rotationChange
                 if (effectiveRotation != 0f ||
                     zoomChange != 1f ||
-                    (panChange != Offset.Zero && canPan.value.invoke(panChange))
+                    (panChange != Offset.Zero && canPan.invoke(panChange))
                 ) {
                     channel.trySend(TransformDelta(zoomChange, panChange, effectiveRotation))
                 }
@@ -217,3 +264,13 @@
         val finallyCanceled = finalEvent.changes.fastAny { it.isConsumed } && !pastTouchSlop
     } while (!canceled && !finallyCanceled && event.changes.fastAny { it.pressed })
 }
+
+private sealed class TransformEvent {
+    object TransformStarted : TransformEvent()
+    object TransformStopped : TransformEvent()
+    class TransformDelta(
+        val zoomChange: Float,
+        val panChange: Offset,
+        val rotationChange: Float
+    ) : TransformEvent()
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyItemScope.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyItemScope.kt
index adba3f6..bca3df6 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyItemScope.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyItemScope.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.foundation.lazy
 
+import androidx.annotation.FloatRange
 import androidx.compose.animation.core.FiniteAnimationSpec
 import androidx.compose.animation.core.Spring
 import androidx.compose.animation.core.VisibilityThreshold
@@ -46,7 +47,7 @@
      * measured with [Constraints.Infinity] as the constraints for the main axis.
      */
     fun Modifier.fillParentMaxSize(
-        /*@FloatRange(from = 0.0, to = 1.0)*/
+        @FloatRange(from = 0.0, to = 1.0)
         fraction: Float = 1f
     ): Modifier
 
@@ -61,7 +62,7 @@
      * items are measured with [Constraints.Infinity] as the constraints for the main axis.
      */
     fun Modifier.fillParentMaxWidth(
-        /*@FloatRange(from = 0.0, to = 1.0)*/
+        @FloatRange(from = 0.0, to = 1.0)
         fraction: Float = 1f
     ): Modifier
 
@@ -76,7 +77,7 @@
      * items are measured with [Constraints.Infinity] as the constraints for the main axis.
      */
     fun Modifier.fillParentMaxHeight(
-        /*@FloatRange(from = 0.0, to = 1.0)*/
+        @FloatRange(from = 0.0, to = 1.0)
         fraction: Float = 1f
     ): Modifier
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyLayoutSemanticState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyLayoutSemanticState.kt
index 5709700..f668923 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyLayoutSemanticState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyLayoutSemanticState.kt
@@ -25,8 +25,10 @@
     isVertical: Boolean
 ): LazyLayoutSemanticState = object : LazyLayoutSemanticState {
 
-    override val currentPosition: Float
-        get() = state.firstVisibleItemIndex + state.firstVisibleItemScrollOffset / 100_000f
+    override val firstVisibleItemScrollOffset: Int
+        get() = state.firstVisibleItemScrollOffset
+    override val firstVisibleItemIndex: Int
+        get() = state.firstVisibleItemIndex
     override val canScrollForward: Boolean
         get() = state.canScrollForward
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
index 7a613c9..e4b170a 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.foundation.lazy
 
+import androidx.annotation.IntRange as AndroidXIntRange
 import androidx.compose.animation.core.AnimationState
 import androidx.compose.animation.core.AnimationVector1D
 import androidx.compose.animation.core.Spring
@@ -254,7 +255,7 @@
      * scroll the item further upward (taking it partly offscreen).
      */
     suspend fun scrollToItem(
-        /*@IntRange(from = 0)*/
+        @AndroidXIntRange(from = 0)
         index: Int,
         scrollOffset: Int = 0
     ) {
@@ -391,7 +392,7 @@
      * scroll the item further upward (taking it partly offscreen).
      */
     suspend fun animateScrollToItem(
-        /*@IntRange(from = 0)*/
+        @AndroidXIntRange(from = 0)
         index: Int,
         scrollOffset: Int = 0
     ) {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridState.kt
index 2ab8a16..1b18637 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridState.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.foundation.lazy.grid
 
+import androidx.annotation.IntRange as AndroidXIntRange
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.MutatePriority
 import androidx.compose.foundation.gestures.Orientation
@@ -249,7 +250,7 @@
      * scroll the item further upward (taking it partly offscreen).
      */
     suspend fun scrollToItem(
-        /*@IntRange(from = 0)*/
+        @AndroidXIntRange(from = 0)
         index: Int,
         scrollOffset: Int = 0
     ) {
@@ -402,7 +403,7 @@
      * scroll the item further upward (taking it partly offscreen).
      */
     suspend fun animateScrollToItem(
-        /*@IntRange(from = 0)*/
+        @AndroidXIntRange(from = 0)
         index: Int,
         scrollOffset: Int = 0
     ) {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazySemantics.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazySemantics.kt
index 1f9c542..e5746d4 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazySemantics.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazySemantics.kt
@@ -31,8 +31,10 @@
 ): LazyLayoutSemanticState =
     remember(state, reverseScrolling) {
         object : LazyLayoutSemanticState {
-            override val currentPosition: Float
-                get() = state.firstVisibleItemIndex + state.firstVisibleItemScrollOffset / 100_000f
+            override val firstVisibleItemScrollOffset: Int
+                get() = state.firstVisibleItemScrollOffset
+            override val firstVisibleItemIndex: Int
+                get() = state.firstVisibleItemIndex
             override val canScrollForward: Boolean
                 get() = state.canScrollForward
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutSemantics.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutSemantics.kt
index 3861647..ad9c7fb 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutSemantics.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutSemantics.kt
@@ -67,24 +67,8 @@
             }
 
             val accessibilityScrollState = ScrollAxisRange(
-                value = {
-                    // This is a simple way of representing the current position without
-                    // needing any lazy items to be measured. It's good enough so far, because
-                    // screen-readers care mostly about whether scroll position changed or not
-                    // rather than the actual offset in pixels.
-                    state.currentPosition
-                },
-                maxValue = {
-                    val itemProvider = itemProviderLambda()
-                    if (state.canScrollForward) {
-                        // If we can scroll further, we don't know the end yet,
-                        // but it's upper bounded by #items + 1
-                        itemProvider.itemCount + 1f
-                    } else {
-                        // If we can't scroll further, the current value is the max
-                        state.currentPosition
-                    }
-                },
+                value = { state.pseudoScrollOffset() },
+                maxValue = { state.pseudoMaxScrollOffset() },
                 reverseScrolling = reverseScrolling
             )
 
@@ -148,9 +132,38 @@
 }
 
 internal interface LazyLayoutSemanticState {
-    val currentPosition: Float
+    val firstVisibleItemScrollOffset: Int
+    val firstVisibleItemIndex: Int
     val canScrollForward: Boolean
     fun collectionInfo(): CollectionInfo
     suspend fun animateScrollBy(delta: Float)
     suspend fun scrollToItem(index: Int)
+
+    // It is impossible for lazy lists to provide an absolute scroll offset because the size of the
+    // items above the viewport is not known, but the AccessibilityEvent system API expects one
+    // anyway. So this provides a best-effort pseudo-offset that avoids breaking existing behavior.
+    //
+    // The key properties that A11y services are known to actually rely on are:
+    // A) each scroll change generates a TYPE_VIEW_SCROLLED AccessibilityEvent
+    // B) the integer offset in the AccessibilityEvent is different than the last one (note that the
+    // magnitude and direction of the change does not matter for the known use cases)
+    // C) scrollability is indicated by whether the scroll position is exactly 0 or exactly
+    // maxScrollOffset
+    //
+    // To preserve property B) as much as possible, the constant 500 is chosen to be larger than a
+    // single scroll delta would realistically be, while small enough to avoid losing precision due
+    // to the 24-bit float significand of ScrollAxisRange with realistic list sizes (if there are
+    // fewer than ~16000 items, the integer value is exactly preserved).
+    fun pseudoScrollOffset() =
+        (firstVisibleItemScrollOffset + firstVisibleItemIndex * 500).toFloat()
+
+    fun pseudoMaxScrollOffset() =
+        if (canScrollForward) {
+            // If we can scroll further, indicate that by setting it slightly higher than
+            // the current value
+            pseudoScrollOffset() + 100
+        } else {
+            // If we can't scroll further, the current value is the max
+            pseudoScrollOffset()
+        }.toFloat()
 }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridSemantics.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridSemantics.kt
index ef6fcda..3554fe1 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridSemantics.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridSemantics.kt
@@ -31,8 +31,10 @@
 ): LazyLayoutSemanticState =
     remember(state, reverseScrolling) {
         object : LazyLayoutSemanticState {
-            override val currentPosition: Float
-                get() = state.firstVisibleItemIndex + state.firstVisibleItemScrollOffset / 100_000f
+            override val firstVisibleItemScrollOffset: Int
+                get() = state.firstVisibleItemScrollOffset
+            override val firstVisibleItemIndex: Int
+                get() = state.firstVisibleItemIndex
             override val canScrollForward: Boolean
                 get() = state.canScrollForward
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt
index c90d260..2bae034 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutPager.kt
@@ -123,12 +123,6 @@
         PagerWrapperFlingBehavior(flingBehavior, state)
     }
 
-    val pagerSemantics = if (userScrollEnabled) {
-        Modifier.pagerSemantics(state, orientation == Orientation.Vertical)
-    } else {
-        Modifier
-    }
-
     val semanticState = rememberPagerSemanticState(
         state,
         reverseLayout,
@@ -141,7 +135,6 @@
         modifier = modifier
             .then(state.remeasurementModifier)
             .then(state.awaitLayoutModifier)
-            .then(pagerSemantics)
             .lazyLayoutSemantics(
                 itemProviderLambda = pagerItemProvider,
                 state = semanticState,
@@ -246,9 +239,10 @@
     pageCount: () -> Int
 ): () -> PagerLazyLayoutItemProvider {
     val latestContent = rememberUpdatedState(pageContent)
-    return remember(state, latestContent, key, pageCount) {
+    val latestKey = rememberUpdatedState(key)
+    return remember(state, latestContent, latestKey, pageCount) {
         val intervalContentState = derivedStateOf(referentialEqualityPolicy()) {
-            PagerLayoutIntervalContent(latestContent.value, key, pageCount())
+            PagerLayoutIntervalContent(latestContent.value, latestKey.value, pageCount())
         }
         val itemProviderState = derivedStateOf(referentialEqualityPolicy()) {
             val intervalContent = intervalContentState.value
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutSemanticState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutSemanticState.kt
index 6f98340..78c554f 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutSemanticState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/LazyLayoutSemanticState.kt
@@ -26,9 +26,10 @@
     state: PagerState,
     isVertical: Boolean
 ): LazyLayoutSemanticState = object : LazyLayoutSemanticState {
-
-    override val currentPosition: Float
-        get() = state.firstVisiblePage + state.firstVisiblePageOffset / 100_000f
+    override val firstVisibleItemScrollOffset: Int
+        get() = state.firstVisiblePageOffset
+    override val firstVisibleItemIndex: Int
+        get() = state.firstVisiblePage
     override val canScrollForward: Boolean
         get() = state.canScrollForward
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PageInfo.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PageInfo.kt
index a4fd6b3..0e2f4b1 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PageInfo.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PageInfo.kt
@@ -18,8 +18,20 @@
 
 import androidx.compose.foundation.ExperimentalFoundationApi
 
+/**
+ * This represents a single measured page in a [Pager] layout.
+ */
 @ExperimentalFoundationApi
-internal interface PageInfo {
+sealed interface PageInfo {
+
+    /**
+     * The index of this page.
+     */
     val index: Int
+
+    /**
+     * The main axis offset of the item in pixels. It is relative to the start of the [Pager]
+     * container.
+     */
     val offset: Int
 }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerBeyondBoundsModifier.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerBeyondBoundsModifier.kt
index 323ad15..10164bb 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerBeyondBoundsModifier.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerBeyondBoundsModifier.kt
@@ -65,7 +65,7 @@
     }
 
     override val itemCount: Int
-        get() = state.layoutInfo.pagesCount
+        get() = state.pageCount
     override val hasVisibleItems: Boolean
         get() = state.layoutInfo.visiblePagesInfo.isNotEmpty()
     override val firstPlacedIndex: Int
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerLayoutInfo.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerLayoutInfo.kt
index f3e2dbd..74f1f87 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerLayoutInfo.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerLayoutInfo.kt
@@ -20,19 +20,76 @@
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.ui.unit.IntSize
 
+/**
+ * Contains useful information about the currently displayed layout state of a [Pager]. This
+ * information is available after the first measure pass.
+ *
+ * Use [PagerState.layoutInfo] to retrieve this
+ */
 @ExperimentalFoundationApi
-internal interface PagerLayoutInfo {
+sealed interface PagerLayoutInfo {
+    /**
+     * A list of all pages that are currently visible in the [Pager]
+     */
     val visiblePagesInfo: List<PageInfo>
-    val closestPageToSnapPosition: PageInfo?
-    val pagesCount: Int
+
+    /**
+     * The size of the Pages in this [Pager] provided by the [PageSize] API in the Pager definition.
+     */
     val pageSize: Int
+
+    /**
+     * The spacing provided in the [Pager] creation.
+     */
     val pageSpacing: Int
+
+    /**
+     * The start offset of the layout's viewport in pixels. You can think of it as a minimum offset
+     * which would be visible. Usually it is 0, but it can be negative if non-zero
+     * beforeContentPadding was applied as the content displayed in the content padding area is
+     * still visible.
+     *
+     * You can use it to understand what items from [visiblePagesInfo] are fully visible.
+     */
     val viewportStartOffset: Int
+
+    /**
+     * The end offset of the layout's viewport in pixels. You can think of it as a maximum offset
+     * which would be visible. It is the size of the lazy list layout minus [beforeContentPadding].
+     *
+     * You can use it to understand what items from [visiblePagesInfo] are fully visible.
+     */
     val viewportEndOffset: Int
+
+    /**
+     * The content padding in pixels applied before the first page in the direction of scrolling.
+     * For example it is a top content padding for [VerticalPager] with reverseLayout set to false.
+     */
     val beforeContentPadding: Int
+
+    /**
+     * The content padding in pixels applied after the last page in the direction of scrolling.
+     * For example it is a bottom content padding for [VerticalPager] with reverseLayout set to
+     * false.
+     */
     val afterContentPadding: Int
+
+    /**
+     * The size of the viewport in pixels. It is the [Pager] layout size including all the
+     * content paddings.
+     */
     val viewportSize: IntSize
+
+    /**
+     * The [Pager] orientation.
+     */
     val orientation: Orientation
+
+    /**
+     * True if the direction of scrolling and layout is reversed.
+     */
+    @Suppress("GetterSetterNames")
+    @get:Suppress("GetterSetterNames")
     val reverseLayout: Boolean
 }
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasure.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasure.kt
index 9142421..9aac2c0 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasure.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasure.kt
@@ -19,7 +19,6 @@
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.fastFilter
 import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.foundation.gestures.snapping.calculateDistanceToDesiredSnapPosition
 import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy
 import androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope
 import androidx.compose.ui.Alignment
@@ -32,7 +31,6 @@
 import androidx.compose.ui.unit.constrainHeight
 import androidx.compose.ui.unit.constrainWidth
 import androidx.compose.ui.util.fastForEach
-import androidx.compose.ui.util.fastMaxBy
 import kotlin.math.abs
 import kotlin.math.roundToInt
 import kotlin.math.sign
@@ -66,7 +64,6 @@
     return if (pageCount <= 0) {
         PagerMeasureResult(
             visiblePagesInfo = emptyList(),
-            pagesCount = 0,
             pageSize = pageAvailableSize,
             pageSpacing = spaceBetweenPages,
             afterContentPadding = afterContentPadding,
@@ -75,7 +72,6 @@
             viewportEndOffset = mainAxisAvailableSize + afterContentPadding,
             measureResult = layout(constraints.minWidth, constraints.minHeight) {},
             consumedScroll = 0f,
-            closestPageToSnapPosition = null,
             firstVisiblePage = null,
             firstVisiblePageOffset = 0,
             reverseLayout = false,
@@ -374,26 +370,10 @@
         val visiblePagesInfo = if (noExtraPages) positionedPages else positionedPages.fastFilter {
             (it.index >= visiblePages.first().index && it.index <= visiblePages.last().index)
         }
-        val viewPortSize = if (orientation == Orientation.Vertical) layoutHeight else layoutWidth
-
-        val closestPageToSnapPosition = visiblePagesInfo.fastMaxBy {
-            -abs(
-                calculateDistanceToDesiredSnapPosition(
-                    mainAxisViewPortSize = viewPortSize,
-                    beforeContentPadding = beforeContentPadding,
-                    afterContentPadding = afterContentPadding,
-                    itemSize = pageAvailableSize,
-                    itemOffset = it.offset,
-                    itemIndex = it.index,
-                    snapPositionInLayout = SnapAlignmentStartToStart
-                )
-            )
-        }
 
         return PagerMeasureResult(
             firstVisiblePage = firstPage,
             firstVisiblePageOffset = currentFirstPageScrollOffset,
-            closestPageToSnapPosition = closestPageToSnapPosition,
             consumedScroll = consumedScroll,
             measureResult = layout(layoutWidth, layoutHeight) {
                 positionedPages.fastForEach {
@@ -403,7 +383,6 @@
             viewportStartOffset = -beforeContentPadding,
             viewportEndOffset = maxOffset + afterContentPadding,
             visiblePagesInfo = visiblePagesInfo,
-            pagesCount = pageCount,
             reverseLayout = reverseLayout,
             orientation = orientation,
             pageSize = pageAvailableSize,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasureResult.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasureResult.kt
index ac69349..381c691 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasureResult.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasureResult.kt
@@ -24,7 +24,6 @@
 @OptIn(ExperimentalFoundationApi::class)
 internal class PagerMeasureResult(
     override val visiblePagesInfo: List<PageInfo>,
-    override val pagesCount: Int,
     override val pageSize: Int,
     override val pageSpacing: Int,
     override val afterContentPadding: Int,
@@ -34,7 +33,6 @@
     override val reverseLayout: Boolean,
     val consumedScroll: Float,
     val firstVisiblePage: MeasuredPage?,
-    override val closestPageToSnapPosition: PageInfo?,
     val firstVisiblePageOffset: Int,
     val canScrollForward: Boolean,
     measureResult: MeasureResult,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerScrollPosition.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerScrollPosition.kt
index 5597496..03395ab 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerScrollPosition.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerScrollPosition.kt
@@ -17,10 +17,15 @@
 package androidx.compose.foundation.pager
 
 import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.snapping.calculateDistanceToDesiredSnapPosition
 import androidx.compose.foundation.lazy.layout.LazyLayoutNearestRangeState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.setValue
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.util.fastMaxBy
+import kotlin.math.abs
 
 /**
  * Contains the current scroll position represented by the first visible page  and the first
@@ -29,7 +34,8 @@
 @OptIn(ExperimentalFoundationApi::class)
 internal class PagerScrollPosition(
     initialPage: Int = 0,
-    initialScrollOffset: Int = 0
+    initialScrollOffset: Int = 0,
+    val state: PagerState
 ) {
     var firstVisiblePage by mutableIntStateOf(initialPage)
     var currentPage by mutableIntStateOf(initialPage)
@@ -56,7 +62,7 @@
         // we ignore the index and offset from measureResult until we get at least one
         // measurement with real pages. otherwise the initial index and scroll passed to the
         // state would be lost and overridden with zeros.
-        if (hadFirstNotEmptyLayout || measureResult.pagesCount > 0) {
+        if (hadFirstNotEmptyLayout || measureResult.visiblePagesInfo.isNotEmpty()) {
             hadFirstNotEmptyLayout = true
             val scrollOffset = measureResult.firstVisiblePageOffset
             check(scrollOffset >= 0f) { "scrollOffset should be non-negative ($scrollOffset)" }
@@ -65,12 +71,32 @@
                 measureResult.firstVisiblePage?.index ?: 0,
                 scrollOffset
             )
-            measureResult.closestPageToSnapPosition?.index?.let {
+            measureResult.closestPageToSnapPosition(state.density)?.index?.let {
                 this.currentPage = it
             }
         }
     }
 
+    private fun PagerMeasureResult.closestPageToSnapPosition(density: Density): PageInfo? {
+        val viewPortSize =
+            if (orientation == Orientation.Vertical) viewportSize.height else viewportSize.width
+        return with(density) {
+            visiblePagesInfo.fastMaxBy {
+                -abs(
+                    calculateDistanceToDesiredSnapPosition(
+                        mainAxisViewPortSize = viewPortSize,
+                        beforeContentPadding = beforeContentPadding,
+                        afterContentPadding = afterContentPadding,
+                        itemSize = (pageSize + pageSpacing),
+                        itemOffset = it.offset,
+                        itemIndex = it.index,
+                        snapPositionInLayout = SnapAlignmentStartToStart
+                    )
+                )
+            }
+        }
+    }
+
     /**
      * Updates the scroll position - the passed values will be used as a start position for
      * composing the pages during the next measure pass and will be updated by the real
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt
index d7d2187..27d7a6f 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt
@@ -26,7 +26,6 @@
 import androidx.compose.foundation.gestures.ScrollableState
 import androidx.compose.foundation.gestures.animateScrollBy
 import androidx.compose.foundation.gestures.snapping.SnapPositionInLayout
-import androidx.compose.foundation.gestures.snapping.calculateDistanceToDesiredSnapPosition
 import androidx.compose.foundation.interaction.InteractionSource
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.lazy.layout.AwaitFirstLayoutModifier
@@ -188,7 +187,7 @@
     internal var upDownDifference: Offset by mutableStateOf(Offset.Zero)
     internal var snapRemainingScrollOffset by mutableFloatStateOf(0f)
 
-    private val scrollPosition = PagerScrollPosition(initialPage, 0)
+    private val scrollPosition = PagerScrollPosition(initialPage, 0, this)
 
     internal val firstVisiblePage: Int get() = scrollPosition.firstVisiblePage
 
@@ -231,9 +230,22 @@
     private var wasScrollingForward = false
 
     /** Backing state for PagerLayoutInfo */
-    private var pagerLayoutInfoState = mutableStateOf(EmptyLayoutInfo)
+    private var pagerLayoutInfoState = mutableStateOf<PagerLayoutInfo>(EmptyLayoutInfo)
 
-    internal val layoutInfo: PagerLayoutInfo get() = pagerLayoutInfoState.value
+    /**
+     * A [PagerLayoutInfo] that contains useful information about the Pager's last layout pass.
+     * For instance, you can query which pages are currently visible in the layout.
+     *
+     * This property is observable and is updated after every scroll or remeasure.
+     * If you use it in the composable function it will be recomposed on every change causing
+     * potential performance issues including infinity recomposition loop.
+     * Therefore, avoid using it in the composition.
+     *
+     * If you want to run some side effects like sending an analytics event or updating a state
+     * based on this value consider using "snapshotFlow":
+     * @sample androidx.compose.foundation.samples.UsingPagerLayoutInfoForSideEffectSample
+     */
+    val layoutInfo: PagerLayoutInfo get() = pagerLayoutInfoState.value
 
     internal val pageSpacing: Int
         get() = pagerLayoutInfoState.value.pageSpacing
@@ -259,19 +271,6 @@
             minThreshold / pageSize.toFloat()
         }
 
-    private val distanceToSnapPosition: Float
-        get() = layoutInfo.closestPageToSnapPosition?.let {
-            density.calculateDistanceToDesiredSnapPosition(
-                mainAxisViewPortSize = layoutInfo.mainAxisViewportSize,
-                beforeContentPadding = layoutInfo.beforeContentPadding,
-                afterContentPadding = layoutInfo.afterContentPadding,
-                itemSize = layoutInfo.pageSize,
-                itemOffset = it.offset,
-                itemIndex = it.index,
-                snapPositionInLayout = SnapAlignmentStartToStart
-            )
-        } ?: 0f
-
     internal val internalInteractionSource: MutableInteractionSource = MutableInteractionSource()
 
     /**
@@ -459,6 +458,7 @@
         }
         var currentPosition = currentPage
         val targetPage = page.coerceInPageRange()
+        var currentPositionOffsetFraction = currentPageOffsetFraction
         animationTargetPage = targetPage
         // If our future page is too far off, that is, outside of the current viewport
         val firstVisiblePageIndex = visiblePages.first().index
@@ -480,14 +480,21 @@
             // Pre-jump to 1 viewport away from destination page, if possible
             scrollToPage(preJumpPosition)
             currentPosition = preJumpPosition
+            currentPositionOffsetFraction = 0.0f
         }
 
         val targetOffset = targetPage * pageAvailableSpace
         val currentOffset = currentPosition * pageAvailableSpace
-        val pageOffsetToSnappedPosition =
-            distanceToSnapPosition + pageOffsetFraction * pageAvailableSpace
 
-        val displacement = targetOffset - currentOffset + pageOffsetToSnappedPosition
+        val targetPageOffsetToSnappedPosition = pageOffsetFraction * pageAvailableSpace
+
+        val offsetFromFraction = currentPositionOffsetFraction * pageAvailableSpace
+
+        // The final delta displacement will be the difference between the pages offsets
+        // discounting whatever offset the original page had scrolled plus the offset
+        // fraction requested by the user.
+        val displacement =
+            targetOffset - currentOffset - offsetFromFraction + targetPageOffsetToSnappedPosition
 
         debugLog { "animateScrollToPage $displacement pixels" }
         animateScrollBy(displacement, animationSpec)
@@ -588,7 +595,7 @@
                 info.visiblePagesInfo.first().index - 1
             }
             if (indexToPrefetch != this.indexToPrefetch &&
-                indexToPrefetch in 0 until info.pagesCount
+                indexToPrefetch in 0 until pageCount
             ) {
                 if (wasScrollingForward != scrollingForward) {
                     // the scrolling direction has been changed which means the last prefetched
@@ -656,10 +663,8 @@
 private const val MaxPagesForAnimateScroll = 3
 
 @OptIn(ExperimentalFoundationApi::class)
-internal val EmptyLayoutInfo = object : PagerLayoutInfo {
+internal object EmptyLayoutInfo : PagerLayoutInfo {
     override val visiblePagesInfo: List<PageInfo> = emptyList()
-    override val closestPageToSnapPosition: PageInfo? = null
-    override val pagesCount: Int = 0
     override val pageSize: Int = 0
     override val pageSpacing: Int = 0
     override val beforeContentPadding: Int = 0
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/AbsoluteCutCornerShape.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/AbsoluteCutCornerShape.kt
index 0f40fab..22e5952 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/AbsoluteCutCornerShape.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/AbsoluteCutCornerShape.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.foundation.shape
 
+import androidx.annotation.IntRange
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.geometry.toRect
 import androidx.compose.ui.graphics.Outline
@@ -181,13 +182,13 @@
  * with a range of 0 - 100.
  */
 fun AbsoluteCutCornerShape(
-    /*@IntRange(from = 0, to = 100)*/
+    @IntRange(from = 0, to = 100)
     topLeftPercent: Int = 0,
-    /*@IntRange(from = 0, to = 100)*/
+    @IntRange(from = 0, to = 100)
     topRightPercent: Int = 0,
-    /*@IntRange(from = 0, to = 100)*/
+    @IntRange(from = 0, to = 100)
     bottomRightPercent: Int = 0,
-    /*@IntRange(from = 0, to = 100)*/
+    @IntRange(from = 0, to = 100)
     bottomLeftPercent: Int = 0
 ) = AbsoluteCutCornerShape(
     topLeft = CornerSize(topLeftPercent),
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/AbsoluteRoundedCornerShape.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/AbsoluteRoundedCornerShape.kt
index 07dad48..bf5fa04 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/AbsoluteRoundedCornerShape.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/AbsoluteRoundedCornerShape.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.foundation.shape
 
+import androidx.annotation.IntRange
 import androidx.compose.ui.geometry.CornerRadius
 import androidx.compose.ui.geometry.RoundRect
 import androidx.compose.ui.geometry.Size
@@ -178,13 +179,13 @@
  * with a range of 0 - 100.
  */
 fun AbsoluteRoundedCornerShape(
-    /*@IntRange(from = 0, to = 100)*/
+    @IntRange(from = 0, to = 100)
     topLeftPercent: Int = 0,
-    /*@IntRange(from = 0, to = 100)*/
+    @IntRange(from = 0, to = 100)
     topRightPercent: Int = 0,
-    /*@IntRange(from = 0, to = 100)*/
+    @IntRange(from = 0, to = 100)
     bottomRightPercent: Int = 0,
-    /*@IntRange(from = 0, to = 100)*/
+    @IntRange(from = 0, to = 100)
     bottomLeftPercent: Int = 0
 ) = AbsoluteRoundedCornerShape(
     topLeft = CornerSize(topLeftPercent),
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/CornerSize.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/CornerSize.kt
index 3e2bb03..3614b58 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/CornerSize.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/CornerSize.kt
@@ -16,6 +16,8 @@
 
 package androidx.compose.foundation.shape
 
+import androidx.annotation.FloatRange
+import androidx.annotation.IntRange
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
 import androidx.compose.ui.geometry.Size
@@ -78,7 +80,7 @@
  * Can't be negative or larger then 100 percents.
  */
 @Stable
-fun CornerSize(/*@IntRange(from = 0, to = 100)*/ percent: Int): CornerSize =
+fun CornerSize(@IntRange(from = 0, to = 100) percent: Int): CornerSize =
     PercentCornerSize(percent.toFloat())
 
 /**
@@ -87,7 +89,7 @@
  * Can't be negative or larger then 100 percents.
  */
 private data class PercentCornerSize(
-    /*@FloatRange(from = 0.0, to = 100.0)*/
+    @FloatRange(from = 0.0, to = 100.0)
     private val percent: Float
 ) : CornerSize, InspectableValue {
     init {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/CutCornerShape.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/CutCornerShape.kt
index d1e1636..7bb7b76 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/CutCornerShape.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/CutCornerShape.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.foundation.shape
 
+import androidx.annotation.IntRange
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.geometry.toRect
 import androidx.compose.ui.graphics.Outline
@@ -181,13 +182,13 @@
  * with a range of 0 - 100.
  */
 fun CutCornerShape(
-    /*@IntRange(from = 0, to = 100)*/
+    @IntRange(from = 0, to = 100)
     topStartPercent: Int = 0,
-    /*@IntRange(from = 0, to = 100)*/
+    @IntRange(from = 0, to = 100)
     topEndPercent: Int = 0,
-    /*@IntRange(from = 0, to = 100)*/
+    @IntRange(from = 0, to = 100)
     bottomEndPercent: Int = 0,
-    /*@IntRange(from = 0, to = 100)*/
+    @IntRange(from = 0, to = 100)
     bottomStartPercent: Int = 0
 ) = CutCornerShape(
     topStart = CornerSize(topStartPercent),
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/RoundedCornerShape.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/RoundedCornerShape.kt
index 388eaed..d234583 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/RoundedCornerShape.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/shape/RoundedCornerShape.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.foundation.shape
 
+import androidx.annotation.IntRange
 import androidx.compose.ui.geometry.CornerRadius
 import androidx.compose.ui.geometry.RoundRect
 import androidx.compose.ui.geometry.Size
@@ -183,13 +184,13 @@
  * with a range of 0 - 100.
  */
 fun RoundedCornerShape(
-    /*@IntRange(from = 0, to = 100)*/
+    @IntRange(from = 0, to = 100)
     topStartPercent: Int = 0,
-    /*@IntRange(from = 0, to = 100)*/
+    @IntRange(from = 0, to = 100)
     topEndPercent: Int = 0,
-    /*@IntRange(from = 0, to = 100)*/
+    @IntRange(from = 0, to = 100)
     bottomEndPercent: Int = 0,
-    /*@IntRange(from = 0, to = 100)*/
+    @IntRange(from = 0, to = 100)
     bottomStartPercent: Int = 0
 ) = RoundedCornerShape(
     topStart = CornerSize(topStartPercent),
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
index 154e98e..91f3980 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/CoreTextField.kt
@@ -852,7 +852,7 @@
     /**
      * A flag to check if the floating toolbar should show.
      */
-    var showFloatingToolbar = false
+    var showFloatingToolbar by mutableStateOf(false)
 
     /**
      * True if the position of the selection start handle is within a visible part of the window
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.kt
index 6d55c9c..0093bd8 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManager.kt
@@ -416,11 +416,13 @@
                 draggingHandle = if (isStartHandle) Handle.SelectionStart else Handle.SelectionEnd
                 currentDragPosition = getAdjustedCoordinates(getHandlePosition(isStartHandle))
                 state?.isInTouchMode = true
+                state?.showFloatingToolbar = false
             }
 
             override fun onUp() {
                 draggingHandle = null
                 currentDragPosition = null
+                state?.showFloatingToolbar = true
             }
 
             override fun onStart(startPoint: Offset) {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/BasicSecureTextField.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/BasicSecureTextField.kt
index 1a8cc48..a22cb59 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/BasicSecureTextField.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/BasicSecureTextField.kt
@@ -33,6 +33,7 @@
 import androidx.compose.foundation.text2.input.mask
 import androidx.compose.foundation.text2.input.then
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.remember
@@ -40,9 +41,15 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.onFocusChanged
+import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Brush
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.platform.LocalTextToolbar
+import androidx.compose.ui.platform.TextToolbar
+import androidx.compose.ui.platform.TextToolbarStatus
+import androidx.compose.ui.semantics.copyText
+import androidx.compose.ui.semantics.cutText
 import androidx.compose.ui.semantics.password
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.text.TextLayoutResult
@@ -162,7 +169,11 @@
     }
 
     val secureTextFieldModifier = modifier
-        .semantics(mergeDescendants = true) { password() }
+        .semantics(mergeDescendants = true) {
+            password()
+            copyText { false }
+            cutText { false }
+        }
         .then(
             if (revealLastTypedEnabled) {
                 secureTextFieldController.focusChangeModifier
@@ -171,31 +182,33 @@
             }
         )
 
-    BasicTextField2(
-        state = state,
-        modifier = secureTextFieldModifier,
-        enabled = enabled,
-        readOnly = false,
-        filter = if (revealLastTypedEnabled) {
-            filter?.then(secureTextFieldController.passwordRevealFilter)
-                ?: secureTextFieldController.passwordRevealFilter
-        } else filter,
-        textStyle = textStyle,
-        interactionSource = interactionSource,
-        cursorBrush = cursorBrush,
-        lineLimits = TextFieldLineLimits.SingleLine,
-        scrollState = scrollState,
-        keyboardOptions = KeyboardOptions(
-            autoCorrect = false,
-            keyboardType = keyboardType,
-            imeAction = imeAction
-        ),
-        keyboardActions = onSubmit?.let { KeyboardActions(onSubmit = it) }
-            ?: KeyboardActions.Default,
-        onTextLayout = onTextLayout,
-        codepointTransformation = codepointTransformation,
-        decorationBox = decorationBox,
-    )
+    DisableCopyTextToolbar {
+        BasicTextField2(
+            state = state,
+            modifier = secureTextFieldModifier,
+            enabled = enabled,
+            readOnly = false,
+            filter = if (revealLastTypedEnabled) {
+                filter?.then(secureTextFieldController.passwordRevealFilter)
+                    ?: secureTextFieldController.passwordRevealFilter
+            } else filter,
+            textStyle = textStyle,
+            interactionSource = interactionSource,
+            cursorBrush = cursorBrush,
+            lineLimits = TextFieldLineLimits.SingleLine,
+            scrollState = scrollState,
+            keyboardOptions = KeyboardOptions(
+                autoCorrect = false,
+                keyboardType = keyboardType,
+                imeAction = imeAction
+            ),
+            keyboardActions = onSubmit?.let { KeyboardActions(onSubmit = it) }
+                ?: KeyboardActions.Default,
+            onTextLayout = onTextLayout,
+            codepointTransformation = codepointTransformation,
+            decorationBox = decorationBox,
+        )
+    }
 }
 
 @OptIn(ExperimentalFoundationApi::class)
@@ -304,3 +317,41 @@
     onSearch = { if (!onSubmit(ImeAction.Search)) defaultKeyboardAction(ImeAction.Search) },
     onSend = { if (!onSubmit(ImeAction.Send)) defaultKeyboardAction(ImeAction.Send) },
 )
+
+/**
+ * Overrides the TextToolbar provided by LocalTextToolbar to never show copy or cut options by the
+ * children composables.
+ */
+@Composable
+private fun DisableCopyTextToolbar(
+    content: @Composable () -> Unit
+) {
+    val currentToolbar = LocalTextToolbar.current
+    val copyDisabledToolbar = remember(currentToolbar) {
+        object : TextToolbar {
+            override fun showMenu(
+                rect: Rect,
+                onCopyRequested: (() -> Unit)?,
+                onPasteRequested: (() -> Unit)?,
+                onCutRequested: (() -> Unit)?,
+                onSelectAllRequested: (() -> Unit)?
+            ) {
+                currentToolbar.showMenu(
+                    rect = rect,
+                    onPasteRequested = onPasteRequested,
+                    onSelectAllRequested = onSelectAllRequested,
+                    onCopyRequested = null,
+                    onCutRequested = null
+                )
+            }
+
+            override fun hide() {
+                currentToolbar.hide()
+            }
+
+            override val status: TextToolbarStatus
+                get() = currentToolbar.status
+        }
+    }
+    CompositionLocalProvider(LocalTextToolbar provides copyDisabledToolbar, content = content)
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/BasicTextField2.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/BasicTextField2.kt
index f43b025..1a4d160 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/BasicTextField2.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/BasicTextField2.kt
@@ -261,7 +261,6 @@
 
                 if (enabled && isFocused && textFieldSelectionState.isInTouchMode) {
                     TextFieldSelectionHandles(
-                        textFieldState = state,
                         selectionState = textFieldSelectionState
                     )
                     if (!readOnly) {
@@ -297,10 +296,8 @@
     }
 }
 
-@OptIn(ExperimentalFoundationApi::class)
 @Composable
 internal fun TextFieldSelectionHandles(
-    textFieldState: TextFieldState,
     selectionState: TextFieldSelectionState
 ) {
     val startHandleState = selectionState.startSelectionHandle
@@ -309,8 +306,10 @@
             position = startHandleState.position,
             isStartHandle = true,
             direction = startHandleState.direction,
-            handlesCrossed = textFieldState.text.selectionInChars.reversed,
-            modifier = Modifier,
+            handlesCrossed = startHandleState.handlesCrossed,
+            modifier = Modifier.pointerInput(selectionState) {
+                with(selectionState) { detectSelectionHandleDrag(true) }
+            },
             content = null
         )
     }
@@ -321,8 +320,10 @@
             position = endHandleState.position,
             isStartHandle = false,
             direction = endHandleState.direction,
-            handlesCrossed = textFieldState.text.selectionInChars.reversed,
-            modifier = Modifier,
+            handlesCrossed = endHandleState.handlesCrossed,
+            modifier = Modifier.pointerInput(selectionState) {
+                with(selectionState) { detectSelectionHandleDrag(false) }
+            },
             content = null
         )
     }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/input/TextEditFilter.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/input/TextEditFilter.kt
index eb1532b..c9f0889 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/input/TextEditFilter.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/input/TextEditFilter.kt
@@ -99,7 +99,8 @@
 
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
-        if (javaClass != other?.javaClass) return false
+        if (other === null) return false
+        if (this::class != other::class) return false
 
         other as FilterChain
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/input/TextFieldCharSequence.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/input/TextFieldCharSequence.kt
index 4a4f8c0..d04e4fb 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/input/TextFieldCharSequence.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/input/TextFieldCharSequence.kt
@@ -132,7 +132,8 @@
      */
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
-        if (javaClass != other?.javaClass) return false
+        if (other === null) return false
+        if (this::class != other::class) return false
 
         other as TextFieldCharSequenceWrapper
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/input/TextFieldLineLimits.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/input/TextFieldLineLimits.kt
index fcb8edc..062d09a 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/input/TextFieldLineLimits.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/input/TextFieldLineLimits.kt
@@ -63,7 +63,8 @@
 
         override fun equals(other: Any?): Boolean {
             if (this === other) return true
-            if (javaClass != other?.javaClass) return false
+            if (other === null) return false
+            if (this::class != other::class) return false
             other as MultiLine
             if (minHeightInLines != other.minHeightInLines) return false
             if (maxHeightInLines != other.maxHeightInLines) return false
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/input/internal/GapBuffer.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/input/internal/GapBuffer.kt
index dd64e57f..01c13aa 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/input/internal/GapBuffer.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/input/internal/GapBuffer.kt
@@ -243,7 +243,7 @@
             val rightCopyCount = minOf(this.text.length - end, SURROUNDING_SIZE)
 
             // Copy left surrounding
-            this.text.toString().toCharArray(charArray, 0, start - leftCopyCount, start)
+            this.text.toCharArray(charArray, 0, start - leftCopyCount, start)
 
             // Copy right surrounding
             this.text.toCharArray(
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/input/internal/TextFieldCoreModifier.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/input/internal/TextFieldCoreModifier.kt
index 56dd35f..cfa1f2e 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/input/internal/TextFieldCoreModifier.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/input/internal/TextFieldCoreModifier.kt
@@ -91,7 +91,7 @@
         textFieldState = textFieldState,
         textFieldSelectionState = textFieldSelectionState,
         cursorBrush = cursorBrush,
-        writable = writeable,
+        writeable = writeable,
         scrollState = scrollState,
         orientation = orientation,
     )
@@ -122,7 +122,7 @@
     private var textFieldState: TextFieldState,
     private var textFieldSelectionState: TextFieldSelectionState,
     private var cursorBrush: Brush,
-    private var writable: Boolean,
+    private var writeable: Boolean,
     private var scrollState: ScrollState,
     private var orientation: Orientation,
 ) : Modifier.Node(),
@@ -142,7 +142,7 @@
      * and brush at a given time.
      */
     private val showCursor: Boolean
-        get() = writable && isFocused && cursorBrush.isSpecified
+        get() = writeable && isFocused && cursorBrush.isSpecified
 
     /**
      * Observes the [textFieldState] for any changes to content or selection. If a change happens,
@@ -152,6 +152,7 @@
 
     // TODO: kdoc
     private var previousSelection: TextRange = TextRange.Zero
+    private var lastFollowing: Int = -1
     private var previousCursorRect: Rect = Rect.Zero
 
     /**
@@ -167,6 +168,7 @@
         scrollState: ScrollState,
         orientation: Orientation,
     ) {
+        val previousShowCursor = this.showCursor
         val wasFocused = this.isFocused
         val previousTextFieldState = this.textFieldState
         val previousTextLayoutState = this.textLayoutState
@@ -177,7 +179,7 @@
         this.textFieldState = textFieldState
         this.textFieldSelectionState = textFieldSelectionState
         this.cursorBrush = cursorBrush
-        this.writable = writeable
+        this.writeable = writeable
         this.scrollState = scrollState
         this.orientation = orientation
 
@@ -185,7 +187,10 @@
             changeObserverJob?.cancel()
             changeObserverJob = null
             coroutineScope.launch { cursorAlpha.snapTo(0f) }
-        } else if (!wasFocused || previousTextFieldState != textFieldState) {
+        } else if (!wasFocused ||
+            previousTextFieldState != textFieldState ||
+            !previousShowCursor
+        ) {
             // this node is writeable, focused and gained that focus just now.
             // start the state value observation
             changeObserverJob = coroutineScope.launch {
@@ -238,10 +243,12 @@
     ): MeasureResult {
         val currSelection = textFieldState.text.selectionInChars
         val offsetToFollow = when {
-            currSelection.start != previousSelection.start -> currSelection.start
             currSelection.end != previousSelection.end -> currSelection.end
+            currSelection.start != previousSelection.start -> currSelection.start
+            lastFollowing >= 0 -> lastFollowing
             else -> currSelection.min
         }
+        lastFollowing = offsetToFollow
         previousSelection = currSelection
 
         // remove any height constraints for TextField since it'll be able to scroll vertically.
@@ -272,13 +279,15 @@
         measurable: Measurable,
         constraints: Constraints
     ): MeasureResult {
-        val value = textFieldState.text
+        val currSelection = textFieldState.text.selectionInChars
         val offsetToFollow = when {
-            value.selectionInChars.start != previousSelection.start -> value.selectionInChars.start
-            value.selectionInChars.end != previousSelection.end -> value.selectionInChars.end
-            else -> value.selectionInChars.min
+            currSelection.end != previousSelection.end -> currSelection.end
+            currSelection.start != previousSelection.start -> currSelection.start
+            lastFollowing >= 0 -> lastFollowing
+            else -> currSelection.min
         }
-        previousSelection = value.selectionInChars
+        lastFollowing = offsetToFollow
+        previousSelection = currSelection
 
         // If the maxIntrinsicWidth of the children is already smaller than the constraint, pass
         // the original constraints so that the children has more information to determine its
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/input/internal/TextFieldDecoratorModifier.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/input/internal/TextFieldDecoratorModifier.kt
index e2682b1..67c26c3 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/input/internal/TextFieldDecoratorModifier.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/input/internal/TextFieldDecoratorModifier.kt
@@ -22,7 +22,6 @@
 import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.foundation.text2.BasicTextField2
 import androidx.compose.foundation.text2.input.TextEditFilter
-import androidx.compose.foundation.text2.input.TextFieldCharSequence
 import androidx.compose.foundation.text2.input.TextFieldState
 import androidx.compose.foundation.text2.input.deselect
 import androidx.compose.foundation.text2.selection.TextFieldSelectionState
@@ -45,6 +44,7 @@
 import androidx.compose.ui.node.PointerInputModifierNode
 import androidx.compose.ui.node.SemanticsModifierNode
 import androidx.compose.ui.node.currentValueOf
+import androidx.compose.ui.node.invalidateSemantics
 import androidx.compose.ui.platform.InspectorInfo
 import androidx.compose.ui.platform.LocalFocusManager
 import androidx.compose.ui.platform.LocalSoftwareKeyboardController
@@ -53,17 +53,19 @@
 import androidx.compose.ui.platform.SoftwareKeyboardController
 import androidx.compose.ui.platform.textInputSession
 import androidx.compose.ui.semantics.SemanticsPropertyReceiver
+import androidx.compose.ui.semantics.copyText
+import androidx.compose.ui.semantics.cutText
 import androidx.compose.ui.semantics.disabled
 import androidx.compose.ui.semantics.editableText
 import androidx.compose.ui.semantics.getTextLayoutResult
 import androidx.compose.ui.semantics.insertTextAtCursor
 import androidx.compose.ui.semantics.onClick
 import androidx.compose.ui.semantics.onImeAction
+import androidx.compose.ui.semantics.pasteText
 import androidx.compose.ui.semantics.setSelection
 import androidx.compose.ui.semantics.setText
 import androidx.compose.ui.semantics.textSelectionRange
 import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.TextRange
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.ImeOptions
 import androidx.compose.ui.text.input.KeyboardCapitalization
@@ -230,6 +232,8 @@
         // Find the diff: current previous and new values before updating current.
         val previousWriteable = this.enabled && !this.readOnly
         val writeable = enabled && !readOnly
+
+        val previousEnabled = this.enabled
         val previousTextFieldState = this.textFieldState
         val previousKeyboardOptions = this.keyboardOptions
         val previousTextFieldSelectionState = this.textFieldSelectionState
@@ -262,6 +266,10 @@
             }
         }
 
+        if (previousEnabled != enabled) {
+            invalidateSemantics()
+        }
+
         textFieldKeyEventHandler.setFilter(filter)
 
         if (textFieldSelectionState != previousTextFieldSelectionState) {
@@ -306,12 +314,9 @@
             } else if (start.coerceAtMost(end) >= 0 &&
                 start.coerceAtLeast(end) <= text.length
             ) {
-                // reset is required to make sure IME gets the update.
-                textFieldState.editProcessor.reset(
-                    TextFieldCharSequence(
-                        text = textFieldState.text,
-                        selection = TextRange(start, end)
-                    )
+                textFieldState.editProcessor.update(
+                    listOf(SetSelectionCommand(start, end)),
+                    filter
                 )
                 true
             } else {
@@ -339,9 +344,29 @@
             // even if the state is 'disabled'
             if (!isFocused) {
                 requestFocus()
+            } else if (!readOnly) {
+                requireKeyboardController().show()
             }
             true
         }
+        if (!selection.collapsed) {
+            copyText {
+                textFieldSelectionState.copy()
+                true
+            }
+            if (enabled && !readOnly) {
+                cutText {
+                    textFieldSelectionState.cut()
+                    true
+                }
+            }
+        }
+        if (enabled && !readOnly) {
+            pasteText {
+                textFieldSelectionState.paste()
+                true
+            }
+        }
     }
 
     override fun onFocusEvent(focusState: FocusState) {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/input/internal/ToCharArray.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/input/internal/ToCharArray.kt
index 3b3f0b2..d817c12 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/input/internal/ToCharArray.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/input/internal/ToCharArray.kt
@@ -26,7 +26,7 @@
 ) = toCharArray(destination, destinationOffset, startIndex = 0, endIndex = this.length)
 
 /**
- * Copies characters from this [String] into [destination].
+ * Copies characters from this [CharSequence] into [destination].
  *
  * Platform-specific implementations should use native functions for performing this operation if
  * they exist, since they will likely be more efficient than copying each character individually.
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/selection/TapAndDoubleTapGesture.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/selection/TapAndDoubleTapGesture.kt
new file mode 100644
index 0000000..123b03c
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/selection/TapAndDoubleTapGesture.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.text2.selection
+
+import androidx.compose.foundation.gestures.awaitEachGesture
+import androidx.compose.foundation.gestures.awaitFirstDown
+import androidx.compose.foundation.gestures.detectTapGestures
+import androidx.compose.foundation.gestures.waitForUpOrCancellation
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.pointer.AwaitPointerEventScope
+import androidx.compose.ui.input.pointer.PointerEventTimeoutCancellationException
+import androidx.compose.ui.input.pointer.PointerInputChange
+import androidx.compose.ui.input.pointer.PointerInputScope
+import androidx.compose.ui.platform.ViewConfiguration
+import androidx.compose.ui.util.fastAny
+import androidx.compose.ui.util.fastForEach
+import kotlinx.coroutines.coroutineScope
+
+/**
+ * Detect tap and double tap gestures. This is a special gesture detector that's copied from
+ * [PointerInputScope.detectTapGestures] to always call [onTap] even when a double tap is detected.
+ * In that case both [onTap] and [onDoubleTap] are called successively. However, if there is a long
+ * tap, neither of the callbacks are called.
+ */
+internal suspend fun PointerInputScope.detectTapAndDoubleTap(
+    onTap: TapOnPosition? = null,
+    onDoubleTap: TapOnPosition? = null,
+) = coroutineScope {
+    awaitEachGesture {
+        val down = awaitFirstDown()
+        down.consume()
+        val longPressTimeout = viewConfiguration.longPressTimeoutMillis
+        var upOrCancel: PointerInputChange? = null
+        try {
+            // wait for first tap up or long press
+            upOrCancel = withTimeout(longPressTimeout) {
+                waitForUpOrCancellation()
+            }
+        } catch (_: PointerEventTimeoutCancellationException) {
+            consumeUntilUp()
+        }
+
+        if (upOrCancel != null) {
+            upOrCancel.consume()
+            // Tap was successful. There was no long click. Now evaluate whether it's gonna be
+            // a double tap.
+            onTap?.onEvent(upOrCancel.position)
+            if (onDoubleTap != null) {
+                // check for second tap
+                val secondDown = awaitSecondDown(upOrCancel)
+
+                if (secondDown != null) {
+                    try {
+                        // Might have a long second press as the second tap
+                        withTimeout(longPressTimeout) {
+                            val secondUp = waitForUpOrCancellation()
+                            if (secondUp != null) {
+                                secondUp.consume()
+                                onDoubleTap.onEvent(secondUp.position)
+                            }
+                        }
+                    } catch (e: PointerEventTimeoutCancellationException) {
+                        consumeUntilUp()
+                    }
+                }
+            }
+        }
+    }
+}
+
+/**
+ * Waits for [ViewConfiguration.doubleTapTimeoutMillis] for a second press event. If a
+ * second press event is received before the time out, it is returned or `null` is returned
+ * if no second press is received.
+ */
+private suspend fun AwaitPointerEventScope.awaitSecondDown(
+    firstUp: PointerInputChange
+): PointerInputChange? = withTimeoutOrNull(viewConfiguration.doubleTapTimeoutMillis) {
+    val minUptime = firstUp.uptimeMillis + viewConfiguration.doubleTapMinTimeMillis
+    var change: PointerInputChange
+    // The second tap doesn't count if it happens before DoubleTapMinTime of the first tap
+    do {
+        change = awaitFirstDown()
+    } while (change.uptimeMillis < minUptime)
+    change
+}
+
+/**
+ * Consumes all pointer events until nothing is pressed and then returns. This method assumes
+ * that something is currently pressed.
+ */
+private suspend fun AwaitPointerEventScope.consumeUntilUp() {
+    do {
+        val event = awaitPointerEvent()
+        event.changes.fastForEach { it.consume() }
+    } while (event.changes.fastAny { it.pressed })
+}
+
+internal fun interface TapOnPosition {
+    fun onEvent(offset: Offset)
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/selection/TextFieldHandleState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/selection/TextFieldHandleState.kt
index 274fa5c..910130d 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/selection/TextFieldHandleState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/selection/TextFieldHandleState.kt
@@ -25,13 +25,15 @@
 internal data class TextFieldHandleState(
     val visible: Boolean,
     val position: Offset,
-    val direction: ResolvedTextDirection
+    val direction: ResolvedTextDirection,
+    val handlesCrossed: Boolean
 ) {
     companion object {
         val Hidden = TextFieldHandleState(
             visible = false,
             position = Offset.Unspecified,
-            direction = ResolvedTextDirection.Ltr
+            direction = ResolvedTextDirection.Ltr,
+            handlesCrossed = false
         )
     }
 }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/selection/TextFieldSelectionState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/selection/TextFieldSelectionState.kt
index e224b5c..f6111aa 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/selection/TextFieldSelectionState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/selection/TextFieldSelectionState.kt
@@ -19,8 +19,9 @@
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.gestures.detectDragGestures
 import androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress
-import androidx.compose.foundation.gestures.detectTapAndPress
+import androidx.compose.foundation.gestures.detectTapGestures
 import androidx.compose.foundation.text.DefaultCursorThickness
+import androidx.compose.foundation.text.Handle
 import androidx.compose.foundation.text.selection.SelectionAdjustment
 import androidx.compose.foundation.text.selection.containsInclusive
 import androidx.compose.foundation.text.selection.getAdjustedCoordinates
@@ -32,6 +33,7 @@
 import androidx.compose.foundation.text2.input.TextFieldBuffer
 import androidx.compose.foundation.text2.input.TextFieldCharSequence
 import androidx.compose.foundation.text2.input.TextFieldState
+import androidx.compose.foundation.text2.input.getSelectedText
 import androidx.compose.foundation.text2.input.internal.TextLayoutState
 import androidx.compose.foundation.text2.input.selectAll
 import androidx.compose.runtime.derivedStateOf
@@ -40,20 +42,22 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.snapshotFlow
 import androidx.compose.runtime.snapshots.Snapshot
+import androidx.compose.runtime.structuralEqualityPolicy
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.hapticfeedback.HapticFeedback
 import androidx.compose.ui.hapticfeedback.HapticFeedbackType
 import androidx.compose.ui.input.pointer.PointerEventPass
 import androidx.compose.ui.input.pointer.PointerInputScope
+import androidx.compose.ui.layout.LayoutCoordinates
 import androidx.compose.ui.platform.ClipboardManager
 import androidx.compose.ui.platform.TextToolbar
 import androidx.compose.ui.platform.TextToolbarStatus
+import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.TextRange
 import androidx.compose.ui.text.style.ResolvedTextDirection
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.LayoutDirection
-import androidx.compose.ui.unit.dp
 import kotlin.math.max
 import kotlin.math.min
 import kotlinx.coroutines.CoroutineStart
@@ -97,32 +101,35 @@
     private var showCursorHandle by mutableStateOf(false)
 
     /**
-     * Whether cursor handle is currently being dragged.
+     * Request to show the text toolbar right now, anchored to the cursor handle. This is not the
+     * final decider for showing the toolbar. Please refer to [observeTextToolbarVisibility] docs.
      */
-    private var isCursorDragging by mutableStateOf(false)
+    private var cursorHandleShowToolbar by mutableStateOf(false)
 
     /**
-     * Request to show the text toolbar right now. This is not the final decider for showing the
-     * toolbar. Please refer to [observeTextToolbarVisibility] docs.
+     * Which selection handle is currently being dragged.
      */
-    private var textToolbarVisible by mutableStateOf(false)
+    private var draggingHandle by mutableStateOf<Handle?>(null)
+
+    /**
+     * Access helper for inner text field coordinates that checks attached state.
+     */
+    private val innerCoordinates: LayoutCoordinates?
+        get() = textLayoutState.innerTextFieldCoordinates?.takeIf { it.isAttached }
 
     /**
      * State of the cursor handle that includes its visibility and position.
      */
     val cursorHandle by derivedStateOf {
-        if (!showCursorHandle || !textFieldState.text.selectionInChars.collapsed)
-            return@derivedStateOf TextFieldHandleState.Hidden
-
-        // either cursor is dragging or inside visible bounds.
-        val visible = isCursorDragging ||
-            textLayoutState.innerTextFieldCoordinates
-                ?.visibleBounds()
-                // Visibility of cursor handle should only be decided by changes to showHandles and
-                // innerTextFieldCoordinates. If we also react to position changes of cursor, cursor
-                // handle may start flickering while moving and scrolling the text field.
-                ?.containsInclusive(Snapshot.withoutReadObservation { cursorRect.bottomCenter })
-            ?: false
+        // For cursor handle to be visible, [showCursorHandle] must be true and the selection
+        // must be collapsed.
+        // Also, cursor handle should be in visible bounds of inner TextField. However, if cursor
+        // is dragging and gets out of bounds, we cannot remove it from composition because that
+        // would stop the drag gesture defined on it. Instead, we allow the handle to be visible
+        // as long as it's being dragged.
+        // Visible bounds calculation lags one frame behind to let auto-scrolling settle.
+        val visible = showCursorHandle && textFieldState.text.selectionInChars.collapsed &&
+            (draggingHandle == Handle.Cursor || cursorHandleInBounds)
 
         if (!visible) return@derivedStateOf TextFieldHandleState.Hidden
 
@@ -130,11 +137,26 @@
         TextFieldHandleState(
             visible = true,
             position = cursorRect.bottomCenter,
-            direction = ResolvedTextDirection.Ltr
+            direction = ResolvedTextDirection.Ltr,
+            handlesCrossed = false
         )
     }
 
     /**
+     * Whether currently cursor handle is in visible bounds. This derived state does not react to
+     * selection changes immediately because every selection change is processed in layout phase
+     * by auto-scroll behavior.
+     */
+    private val cursorHandleInBounds by derivedStateOf(policy = structuralEqualityPolicy()) {
+        val position = Snapshot.withoutReadObservation { cursorRect.bottomCenter }
+
+        innerCoordinates
+            ?.visibleBounds()
+            ?.containsInclusive(position)
+            ?: false
+    }
+
+    /**
      * Where the cursor should be at any given time in InnerTextField coordinates.
      */
     val cursorRect: Rect by derivedStateOf {
@@ -153,15 +175,13 @@
             (cursorRect.right - cursorWidth / 2)
         }
 
-        val coercedCursorCenterX = textLayoutState.innerTextFieldCoordinates?.let { coordinates ->
-            // don't let cursor go beyond the bounds of inner text field or cursor will be clipped.
-            // but also make sure that empty Text Layout still draws a cursor.
-            cursorCenterX
-                // do not use coerceIn because it is not guaranteed that minimum value is smaller
-                // than the maximum value.
-                .coerceAtMost(coordinates.size.width - cursorWidth / 2)
-                .coerceAtLeast(cursorWidth / 2)
-        } ?: cursorCenterX
+        // don't let cursor go beyond the bounds of inner text field or cursor will be clipped.
+        // but also make sure that empty Text Layout still draws a cursor.
+        val coercedCursorCenterX = cursorCenterX
+            // do not use coerceIn because it is not guaranteed that minimum value is smaller
+            // than the maximum value.
+            .coerceAtMost(layoutResult.size.width - cursorWidth / 2)
+            .coerceAtLeast(cursorWidth / 2)
 
         Rect(
             left = coercedCursorCenterX - cursorWidth / 2,
@@ -191,7 +211,9 @@
                 detectCursorHandleDragGestures()
             }
             launch(start = CoroutineStart.UNDISPATCHED) {
-                detectTapAndPress { textToolbarVisible = !textToolbarVisible }
+                detectTapGestures(onTap = {
+                    cursorHandleShowToolbar = !cursorHandleShowToolbar
+                })
             }
         }
     }
@@ -208,30 +230,8 @@
                 detectTouchMode()
             }
             launch(start = CoroutineStart.UNDISPATCHED) {
-                detectTapAndPress(onTap = { offset ->
-                    logDebug { "onTapTextField" }
-                    requestFocus()
-
-                    if (editable && isFocused) {
-                        showKeyboard()
-                        if (textFieldState.text.isNotEmpty()) {
-                            showCursorHandle = true
-                        }
-
-                        textToolbarVisible = false
-
-                        // find the cursor position
-                        val cursorIndex = textLayoutState.getOffsetForPosition(offset)
-                        // update the state
-                        if (cursorIndex >= 0) {
-                            editWithFilter {
-                                selectCharsIn(TextRange(cursorIndex))
-                            }
-                        }
-                    }
-                })
+                detectTextFieldTapGestures(requestFocus, showKeyboard)
             }
-
             launch(start = CoroutineStart.UNDISPATCHED) {
                 detectTextFieldLongPressAndAfterDrag(requestFocus)
             }
@@ -239,6 +239,109 @@
     }
 
     /**
+     * Gesture detector for dragging the selection handles to change the selection in TextField.
+     */
+    suspend fun PointerInputScope.detectSelectionHandleDrag(isStartHandle: Boolean) {
+        // keep track of how visible bounds change while moving the selection handle.
+        var startContentVisibleOffset: Offset = Offset.Zero
+
+        var dragBeginPosition: Offset = Offset.Unspecified
+        var dragTotalDistance: Offset = Offset.Unspecified
+        var previousDragOffset = -1
+        val handle = if (isStartHandle) Handle.SelectionStart else Handle.SelectionEnd
+
+        // b/288931376: detectDragGestures do not call onDragCancel when composable is disposed.
+        try {
+            detectDragGestures(
+                onDragStart = {
+                    draggingHandle = handle
+                    // The position of the character where the drag gesture should begin. This is in
+                    // the composable coordinates.
+                    dragBeginPosition = getAdjustedCoordinates(getHandlePosition(isStartHandle))
+
+                    startContentVisibleOffset = innerCoordinates
+                        ?.visibleBounds()
+                        ?.topLeft ?: Offset.Zero
+
+                    // Zero out the total distance that being dragged.
+                    dragTotalDistance = Offset.Zero
+                    previousDragOffset = if (isStartHandle) {
+                        textFieldState.text.selectionInChars.start
+                    } else {
+                        textFieldState.text.selectionInChars.end
+                    }
+                },
+                onDragEnd = {
+                    draggingHandle = null
+                    dragBeginPosition = Offset.Unspecified
+                    dragTotalDistance = Offset.Zero
+                    startContentVisibleOffset = Offset.Zero
+                },
+                onDragCancel = {
+                    draggingHandle = null
+                    dragBeginPosition = Offset.Unspecified
+                    dragTotalDistance = Offset.Zero
+                    startContentVisibleOffset = Offset.Zero
+                },
+                onDrag = onDrag@{ _, delta ->
+                    draggingHandle = handle
+                    dragTotalDistance += delta
+                    val layoutResult = textLayoutState.layoutResult ?: return@onDrag
+
+                    val currentContentVisibleOffset = innerCoordinates
+                        ?.visibleBounds()
+                        ?.topLeft ?: startContentVisibleOffset
+
+                    // "start position + total delta" is not enough to understand the current
+                    // pointer position relative to text layout. We need to also account for any
+                    // changes to visible offset that's caused by auto-scrolling while dragging.
+                    val currentDragPosition = dragBeginPosition + dragTotalDistance +
+                        (currentContentVisibleOffset - startContentVisibleOffset)
+
+                    val startOffset = if (isStartHandle) {
+                        layoutResult.getOffsetForPosition(currentDragPosition)
+                    } else {
+                        textFieldState.text.selectionInChars.start
+                    }
+
+                    val endOffset = if (isStartHandle) {
+                        textFieldState.text.selectionInChars.end
+                    } else {
+                        layoutResult.getOffsetForPosition(currentDragPosition)
+                    }
+
+                    val prevSelection = textFieldState.text.selectionInChars
+                    var newSelection = updateSelection(
+                        textFieldCharSequence = textFieldState.text,
+                        startOffset = startOffset,
+                        endOffset = endOffset,
+                        isStartHandle = isStartHandle,
+                        previousHandleOffset = previousDragOffset,
+                        adjustment = SelectionAdjustment.CharacterWithWordAccelerate,
+                    )
+                    // selection drag should never reverse the selection. It can only collapse the
+                    // selection as the handles are about to pass each other.
+                    if (newSelection.reversed != prevSelection.reversed) {
+                        // the common offset should be the one that is not being dragged
+                        val offset = if (isStartHandle) prevSelection.end else prevSelection.start
+                        newSelection = TextRange(offset)
+                    }
+                    editWithFilter {
+                        selectCharsIn(newSelection)
+                    }
+                    previousDragOffset = if (isStartHandle) startOffset else endOffset
+                }
+            )
+        } finally {
+            logDebug {
+                "Selection Handle drag cancelled for " +
+                    "draggingHandle: $draggingHandle definedOn: $handle"
+            }
+            if (draggingHandle == handle) draggingHandle = null
+        }
+    }
+
+    /**
      * Starts observing changes in the current state for reactive rules. For example, the cursor
      * handle or the selection handles should hide whenever the text content changes.
      */
@@ -250,16 +353,14 @@
             }
         } finally {
             showCursorHandle = false
-            if (textToolbarVisible) {
+            if (cursorHandleShowToolbar) {
                 hideTextToolbar()
             }
         }
     }
 
     fun dispose() {
-        if (textToolbarVisible) {
-            hideTextToolbar()
-        }
+        hideTextToolbar()
 
         textToolbar = null
         clipboardManager = null
@@ -280,6 +381,61 @@
         }
     }
 
+    private suspend fun PointerInputScope.detectTextFieldTapGestures(
+        requestFocus: () -> Unit,
+        showKeyboard: () -> Unit
+    ) {
+        detectTapAndDoubleTap(
+            onTap = { offset ->
+                logDebug { "onTapTextField" }
+                requestFocus()
+
+                if (editable && isFocused) {
+                    showKeyboard()
+                    if (textFieldState.text.isNotEmpty()) {
+                        showCursorHandle = true
+                    }
+
+                    cursorHandleShowToolbar = false
+
+                    // find the cursor position
+                    val cursorIndex = textLayoutState.getOffsetForPosition(offset)
+                    // update the state
+                    if (cursorIndex >= 0) {
+                        editWithFilter {
+                            selectCharsIn(TextRange(cursorIndex))
+                        }
+                    }
+                }
+            },
+            onDoubleTap = { offset ->
+                logDebug { "onDoubleTapTextField" }
+                // onTap is already called at this point. Focus is requested.
+
+                showCursorHandle = false
+                cursorHandleShowToolbar = false
+
+                val index = textLayoutState.getOffsetForPosition(offset)
+                val newSelection = updateSelection(
+                    // reset selection, otherwise a previous selection may be used
+                    // as context for creating the next selection
+                    textFieldCharSequence = TextFieldCharSequence(
+                        textFieldState.text,
+                        TextRange.Zero
+                    ),
+                    startOffset = index,
+                    endOffset = index,
+                    isStartHandle = false,
+                    previousHandleOffset = -1, // there is no previous drag.
+                    adjustment = SelectionAdjustment.Word,
+                )
+                editWithFilter {
+                    selectCharsIn(newSelection)
+                }
+            }
+        )
+    }
+
     private suspend fun PointerInputScope.detectCursorHandleDragGestures() {
         // keep track of how visible bounds change while moving the cursor handle.
         var startContentVisibleOffset: Offset = Offset.Zero
@@ -292,33 +448,31 @@
                 // mark start drag point
                 cursorDragStart = getAdjustedCoordinates(cursorRect.bottomCenter)
                 cursorDragDelta = Offset.Zero
-                startContentVisibleOffset = textLayoutState.innerTextFieldCoordinates
-                    ?.takeIf { textLayoutState.innerTextFieldCoordinates?.isAttached == true }
+                startContentVisibleOffset = innerCoordinates
                     ?.visibleBounds()
                     ?.topLeft ?: Offset.Zero
                 isInTouchMode = true
-                isCursorDragging = true
+                draggingHandle = Handle.Cursor
             },
             onDragEnd = {
                 // clear any dragging state
                 cursorDragStart = Offset.Unspecified
                 cursorDragDelta = Offset.Unspecified
                 startContentVisibleOffset = Offset.Zero
-                isCursorDragging = false
+                draggingHandle = null
             },
             onDragCancel = {
                 // another gesture consumed the pointer, or composable is disposed
                 cursorDragStart = Offset.Unspecified
                 cursorDragDelta = Offset.Unspecified
                 startContentVisibleOffset = Offset.Zero
-                isCursorDragging = false
+                draggingHandle = null
             },
             onDrag = onDrag@{ change, dragAmount ->
                 cursorDragDelta += dragAmount
 
-                val currentContentVisibleOffset = textLayoutState.innerTextFieldCoordinates
+                val currentContentVisibleOffset = innerCoordinates
                     ?.visibleBounds()
-                    ?.takeIf { textLayoutState.innerTextFieldCoordinates?.isAttached == true }
                     ?.topLeft ?: startContentVisibleOffset
 
                 // "start position + total delta" is not enough to understand the current pointer
@@ -336,6 +490,7 @@
                 if (newSelection == textFieldState.text.selectionInChars) return@onDrag
 
                 change.consume()
+                // TODO: only perform haptic feedback if filter does not override the change
                 hapticFeedBack?.performHapticFeedback(HapticFeedbackType.TextHandleMove)
                 editWithFilter {
                     selectCharsIn(newSelection)
@@ -352,7 +507,7 @@
                 logDebug { "onDragStart after longPress" }
                 // at the beginning of selection disable toolbar, re-evaluate visibility after
                 // drag gesture is finished
-                textToolbarVisible = false
+                cursorHandleShowToolbar = false
                 requestFocus()
 
                 // Long Press at the blank area, the cursor should show up at the end of the line.
@@ -367,7 +522,7 @@
                 } else {
                     if (textFieldState.text.isEmpty()) return@onDragStart
                     val offset = textLayoutState.getOffsetForPosition(dragStartOffset)
-                    updateSelection(
+                    val newSelection = updateSelection(
                         // reset selection, otherwise a previous selection may be used
                         // as context for creating the next selection
                         textFieldCharSequence = TextFieldCharSequence(
@@ -377,8 +532,13 @@
                         startOffset = offset,
                         endOffset = offset,
                         isStartHandle = false,
+                        previousHandleOffset = -1, // there is no previous drag.
                         adjustment = SelectionAdjustment.CharacterWithWordAccelerate,
                     )
+                    editWithFilter {
+                        selectCharsIn(newSelection)
+                    }
+                    showCursorHandle = false
                 }
             },
             onDragEnd = {},
@@ -394,7 +554,7 @@
             .drop(1)
             .collect {
                 showCursorHandle = false
-                textToolbarVisible = false
+                cursorHandleShowToolbar = false
             }
     }
 
@@ -402,35 +562,45 @@
      * Manages the visibility of text toolbar according to current state and received events from
      * various sources.
      *
-     * - Tapping the cursor handle toggles the visibility of the toolbar [textToolbarVisible].
-     * - Dragging the cursor handle temporarily hides the toolbar [isCursorDragging].
+     * - Tapping the cursor handle toggles the visibility of the toolbar [cursorHandleShowToolbar].
+     * - Dragging the cursor handle or selection handles temporarily hides the toolbar
+     * [draggingHandle].
      * - Tapping somewhere on the textfield, whether it causes a cursor position change or not,
-     * fully hides the toolbar [textToolbarVisible].
+     * fully hides the toolbar [cursorHandleShowToolbar].
      * - Scrolling the textfield temporarily hides the toolbar [getContentRect].
      * - When cursor leaves the visible bounds, text toolbar is temporarily hidden.
      */
     private suspend fun observeTextToolbarVisibility() {
         snapshotFlow {
-            val toolbarVisibility = textToolbarVisible && // toolbar is requested
-                !isCursorDragging && // not dragging the cursor handle
-                isInTouchMode
+            val isCollapsed = textFieldState.text.selectionInChars.collapsed
+            val toolbarVisibility =
+                // either toolbar is requested specifically or selection is active
+                (cursorHandleShowToolbar || !isCollapsed) &&
+                    draggingHandle == null && // not dragging any selection handles
+                    isInTouchMode
 
             // final visibility decision is made by contentRect visibility.
             // if contentRect is not in visible bounds, just pass Rect.Zero to the observer so that
             // it hides the toolbar. If Rect is successfully passed to the observer, toolbar will
-            // be shown.
+            // be displayed.
             if (!toolbarVisibility) {
                 Rect.Zero
             } else {
                 // contentRect is calculated in root coordinates. VisibleBounds are in parent
                 // coordinates. Convert visibleBounds to root before checking the overlap.
-                val visibleBounds = textLayoutState.innerTextFieldCoordinates?.visibleBounds()
+                val visibleBounds = innerCoordinates?.visibleBounds()
                 if (visibleBounds != null) {
-                    val visibleBoundsTopLeftInRoot = textLayoutState
-                        .innerTextFieldCoordinates
-                        ?.localToRoot(visibleBounds.topLeft)
-                    val visibleBoundsInRoot = Rect(visibleBoundsTopLeftInRoot!!, visibleBounds.size)
-                    getContentRect().takeIf { visibleBoundsInRoot.overlaps(it) } ?: Rect.Zero
+                    val visibleBoundsTopLeftInRoot =
+                        innerCoordinates?.localToRoot(visibleBounds.topLeft)
+                    val visibleBoundsInRoot =
+                        Rect(visibleBoundsTopLeftInRoot!!, visibleBounds.size)
+
+                    // contentRect can be very wide if a big part of text is selected. Our toolbar
+                    // should be aligned only to visible region.
+                    getContentRect()
+                        .takeIf { visibleBoundsInRoot.overlaps(it) }
+                        ?.intersect(visibleBoundsInRoot)
+                        ?: Rect.Zero
                 } else {
                     Rect.Zero
                 }
@@ -461,19 +631,15 @@
         // contentRect is defined in innerTextField coordinates, so it needs to be realigned to
         // root container.
         if (text.selectionInChars.collapsed) {
-            val topLeft = textLayoutState.innerTextFieldCoordinates?.localToRoot(
-                cursorRect.topLeft
-            ) ?: Offset.Zero
+            val topLeft = innerCoordinates?.localToRoot(cursorRect.topLeft) ?: Offset.Zero
             return Rect(topLeft, cursorRect.size)
         }
         val startOffset =
-            textLayoutState.innerTextFieldCoordinates?.localToRoot(getHandlePosition(true))
-                ?: Offset.Zero
+            innerCoordinates?.localToRoot(getHandlePosition(true)) ?: Offset.Zero
         val endOffset =
-            textLayoutState.innerTextFieldCoordinates?.localToRoot(getHandlePosition(false))
-                ?: Offset.Zero
+            innerCoordinates?.localToRoot(getHandlePosition(false)) ?: Offset.Zero
         val startTop =
-            textLayoutState.innerTextFieldCoordinates?.localToRoot(
+            innerCoordinates?.localToRoot(
                 Offset(
                     0f,
                     textLayoutState.layoutResult?.getCursorRect(
@@ -482,7 +648,7 @@
                 )
             )?.y ?: 0f
         val endTop =
-            textLayoutState.innerTextFieldCoordinates?.localToRoot(
+            innerCoordinates?.localToRoot(
                 Offset(
                     0f,
                     textLayoutState.layoutResult?.getCursorRect(
@@ -495,11 +661,13 @@
             left = min(startOffset.x, endOffset.x),
             right = max(startOffset.x, endOffset.x),
             top = min(startTop, endTop),
-            bottom = max(startOffset.y, endOffset.y) + with(density) { 25.dp.roundToPx() }
+            bottom = max(startOffset.y, endOffset.y)
         )
     }
 
     private fun getSelectionHandleState(isStartHandle: Boolean): TextFieldHandleState {
+        val handle = if (isStartHandle) Handle.SelectionStart else Handle.SelectionEnd
+
         val layoutResult = textLayoutState.layoutResult ?: return TextFieldHandleState.Hidden
 
         val selection = textFieldState.text.selectionInChars
@@ -508,42 +676,91 @@
 
         val position = getHandlePosition(isStartHandle)
 
-        val visible = textLayoutState.innerTextFieldCoordinates
-            ?.visibleBounds()
-            ?.containsInclusive(position)
-            ?: false
+        val visible = draggingHandle == handle ||
+            (innerCoordinates
+                ?.visibleBounds()
+                ?.containsInclusive(position)
+                ?: false)
 
         if (!visible) return TextFieldHandleState.Hidden
 
         val directionOffset = if (isStartHandle) selection.start else max(selection.end - 1, 0)
         val direction = layoutResult.getBidiRunDirection(directionOffset)
-        return TextFieldHandleState(true, position, direction)
+        val handlesCrossed = selection.reversed
+        return TextFieldHandleState(true, position, direction, handlesCrossed)
     }
 
     private fun getHandlePosition(isStartHandle: Boolean): Offset {
+        val layoutResult = textLayoutState.layoutResult ?: return Offset.Zero
+        val selection = textFieldState.text.selectionInChars
         val offset = if (isStartHandle) {
-            textFieldState.text.selectionInChars.start
+            selection.start
         } else {
-            textFieldState.text.selectionInChars.end
+            selection.end
         }
         return getSelectionHandleCoordinates(
-            textLayoutResult = textLayoutState.layoutResult!!,
+            textLayoutResult = layoutResult,
             offset = offset,
             isStart = isStartHandle,
-            areHandlesCrossed = textFieldState.text.selectionInChars.reversed
+            areHandlesCrossed = selection.reversed
         )
     }
 
     /**
+     * The method for cutting text.
+     *
+     * If there is no selection, return.
+     * Put the selected text into the [ClipboardManager].
+     * The new text should be the text before the selection plus the text after the selection.
+     * And the new cursor offset should be between the text before the selection, and the text
+     * after the selection.
+     */
+    internal fun cut() {
+        val text = textFieldState.text
+        if (text.selectionInChars.collapsed) return
+
+        clipboardManager?.setText(AnnotatedString(text.getSelectedText().toString()))
+
+        editWithFilter {
+            replace(selectionInChars.min, selectionInChars.max, "")
+            selectCharsIn(TextRange(selectionInChars.min))
+        }
+        // TODO(halilibo): undoManager force snapshot
+    }
+
+    /**
+     * The method for copying text.
+     *
+     * If there is no selection, return.
+     * Put the selected text into the [ClipboardManager], and cancel the selection, if
+     * [cancelSelection] is true.
+     * The text in the text field should be unchanged.
+     * If [cancelSelection] is true, the new cursor offset should be at the end of the previous
+     * selected text.
+     */
+    fun copy(cancelSelection: Boolean = true) {
+        val text = textFieldState.text
+        if (text.selectionInChars.collapsed) return
+
+        clipboardManager?.setText(AnnotatedString(text.getSelectedText().toString()))
+
+        if (!cancelSelection) return
+
+        editWithFilter {
+            selectCharsIn(TextRange(selectionInChars.max))
+        }
+    }
+
+    /**
      * The method for pasting text.
      *
      * Get the text from [ClipboardManager]. If it's null, return.
-     * The new text should be the text before the selected text, plus the text from the
+     * The new content should be the text before the selected text, plus the text from the
      * [ClipboardManager], and plus the text after the selected text.
-     * Then the selection should collapse, and the new cursor offset should be the end of the
+     * Then the selection should collapse, and the new cursor offset should be at the end of the
      * newly added text.
      */
-    private fun paste() {
+    fun paste() {
         val clipboardText = clipboardManager?.getText()?.text ?: return
 
         editWithFilter {
@@ -555,8 +772,6 @@
             )
             selectCharsIn(TextRange(selection.min + clipboardText.length))
         }
-
-        showCursorHandle = false
         // TODO(halilibo): undoManager force snapshot
     }
 
@@ -573,19 +788,36 @@
         val paste: (() -> Unit)? = if (editable && clipboardManager?.hasText() == true) {
             {
                 paste()
-                textToolbarVisible = false
+                cursorHandleShowToolbar = false
+            }
+        } else null
+
+        val copy: (() -> Unit)? = if (!selection.collapsed) {
+            {
+                copy()
+                cursorHandleShowToolbar = false
+            }
+        } else null
+
+        val cut: (() -> Unit)? = if (!selection.collapsed && editable) {
+            {
+                cut()
+                cursorHandleShowToolbar = false
             }
         } else null
 
         val selectAll: (() -> Unit)? = if (selection.length != textFieldState.text.length) {
             {
                 editWithFilter { selectAll() }
+                cursorHandleShowToolbar = false
             }
         } else null
 
         textToolbar?.showMenu(
             rect = contentRect,
+            onCopyRequested = copy,
             onPasteRequested = paste,
+            onCutRequested = cut,
             onSelectAllRequested = selectAll
         )
     }
@@ -637,13 +869,14 @@
         endOffset: Int,
         isStartHandle: Boolean,
         adjustment: SelectionAdjustment,
+        previousHandleOffset: Int,
         allowPreviousSelectionCollapsed: Boolean = false,
     ): TextRange {
         val newSelection = getTextFieldSelection(
             textLayoutResult = textLayoutState.layoutResult,
             rawStartOffset = startOffset,
             rawEndOffset = endOffset,
-            previousHandleOffset = -1,
+            previousHandleOffset = previousHandleOffset,
             previousSelection = textFieldCharSequence.selectionInChars
                 .takeIf { allowPreviousSelectionCollapsed || !it.collapsed },
             isStartHandle = isStartHandle,
@@ -654,17 +887,13 @@
 
         val onlyChangeIsReversed =
             newSelection.reversed != textFieldCharSequence.selectionInChars.reversed &&
-            newSelection.run { TextRange(end, start) } == textFieldCharSequence.selectionInChars
+                newSelection.run { TextRange(end, start) } == textFieldCharSequence.selectionInChars
 
         // don't haptic if we are using a mouse or if we aren't moving the selection bounds
         if (isInTouchMode && !onlyChangeIsReversed) {
             hapticFeedBack?.performHapticFeedback(HapticFeedbackType.TextHandleMove)
         }
 
-        editWithFilter {
-            selectCharsIn(newSelection)
-        }
-
         return newSelection
     }
 }
@@ -673,5 +902,7 @@
 private val DEBUG_TAG = "TextFieldSelectionState"
 
 private fun logDebug(text: () -> String) {
-    if (DEBUG) { println("$DEBUG_TAG: ${text()}") }
+    if (DEBUG) {
+        println("$DEBUG_TAG: ${text()}")
+    }
 }
diff --git a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/interoperability/Interoperability.kt b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/interoperability/Interoperability.kt
index 1f5a4ef..369ff5f 100644
--- a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/interoperability/Interoperability.kt
+++ b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/interoperability/Interoperability.kt
@@ -178,7 +178,7 @@
                 CustomView(context).apply {
                     // Sets up listeners for View -> Compose communication
                     myView.setOnClickListener {
-                        selectedItem.value = 1
+                        selectedItem.intValue = 1
                     }
                 }
             },
@@ -189,7 +189,7 @@
                 // As selectedItem is read here, AndroidView will recompose
                 // whenever the state changes
                 // Example of Compose -> View communication
-                view.coordinator.selectedItem = selectedItem.value
+                view.coordinator.selectedItem = selectedItem.intValue
             }
         )
     }
diff --git a/compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml b/compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
index 39a5d41..1b763e5 100644
--- a/compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
+++ b/compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
@@ -47,6 +47,19 @@
             </intent-filter>
         </activity>
         <activity
+            android:name=".TrivialStartupTracingActivity"
+            android:label="C TrivialTracing"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="androidx.compose.integration.macrobenchmark.target.TRIVIAL_STARTUP_TRACING_ACTIVITY" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+        <activity
             android:name=".FullyDrawnStartupActivity"
             android:exported="true">
             <intent-filter>
diff --git a/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/TrivialStartupTracingActivity.kt b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/TrivialStartupTracingActivity.kt
new file mode 100644
index 0000000..a3f9cbc
--- /dev/null
+++ b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/TrivialStartupTracingActivity.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.integration.macrobenchmark.target
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material.Text
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+
+class TrivialStartupTracingActivity : ComponentActivity() {
+    private var incrementActivityResumedCount = {}
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        setContent {
+            var resumedCount by remember { mutableIntStateOf(0) }
+            incrementActivityResumedCount = { ++resumedCount }
+            Text("Compose Macrobenchmark Target (resumed $resumedCount times)")
+        }
+    }
+
+    override fun onResume() {
+        super.onResume()
+        incrementActivityResumedCount()
+    }
+}
diff --git a/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/TrivialStartupTracingBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/TrivialStartupTracingBenchmark.kt
new file mode 100644
index 0000000..47cbb44
--- /dev/null
+++ b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/TrivialStartupTracingBenchmark.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.integration.macrobenchmark
+
+import androidx.benchmark.Arguments
+import androidx.benchmark.macro.CompilationMode
+import androidx.benchmark.macro.ExperimentalMetricApi
+import androidx.benchmark.macro.StartupMode
+import androidx.benchmark.macro.TraceSectionMetric
+import androidx.benchmark.macro.junit4.MacrobenchmarkRule
+import androidx.test.filters.LargeTest
+import androidx.testutils.measureStartup
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@OptIn(ExperimentalMetricApi::class)
+@LargeTest
+@RunWith(Parameterized::class)
+class TrivialStartupTracingBenchmark(
+    private val startupMode: StartupMode,
+    private val compilationMode: CompilationMode,
+    private val isFullTracingEnabled: Boolean
+) {
+    @get:Rule
+    val benchmarkRule = MacrobenchmarkRule()
+
+    // TODO(283953019): enable alongside StartupTracingInitializer (pending performance testing)
+    @Ignore
+    @Test
+    fun startup() = try {
+        Arguments.fullTracingEnableOverride = isFullTracingEnabled
+        assertThat(Arguments.fullTracingEnable, `is`(isFullTracingEnabled))
+
+        try {
+            val perfettoSdkTraceSection = TraceSectionMetric(
+                "androidx.compose.integration.macrobenchmark.target." +
+                    "TrivialStartupTracingActivity.onCreate.<anonymous>" +
+                    " (TrivialStartupTracingActivity.kt:33)"
+            )
+            benchmarkRule.measureStartup(
+                compilationMode = compilationMode,
+                startupMode = startupMode,
+                iterations = 1, // we are only verifying presence of entries (not the timing data)
+                metrics = listOf(perfettoSdkTraceSection),
+                packageName = "androidx.compose.integration.macrobenchmark.target"
+            ) {
+                action = "androidx.compose.integration.macrobenchmark.target." +
+                    "TRIVIAL_STARTUP_TRACING_ACTIVITY"
+            }
+        } catch (e: IllegalArgumentException) {
+            if (!isFullTracingEnabled &&
+                e.message?.contains("Unable to read any metrics during benchmark") == true
+            ) {
+                // this is expected, we don't expect Perfetto SDK Tracing section present
+                // when full tracing is disabled
+            } else throw e // this is a legitimate failure
+        }
+    } finally {
+        Arguments.fullTracingEnableOverride = null
+    }
+
+    companion object {
+        @Parameterized.Parameters(name = "startup={0},compilation={1},fullTracing={2}")
+        @JvmStatic
+        fun parameters() = listOf(
+            arrayOf(StartupMode.COLD, CompilationMode.DEFAULT, /* fullTracing = */ true),
+            arrayOf(StartupMode.COLD, CompilationMode.DEFAULT, /* fullTracing = */ false),
+            arrayOf(StartupMode.WARM, CompilationMode.DEFAULT, /* fullTracing = */ true),
+            arrayOf(StartupMode.WARM, CompilationMode.DEFAULT, /* fullTracing = */ false),
+        )
+    }
+}
diff --git a/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/TrivialTracingBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/TrivialTracingBenchmark.kt
index 82f9c03..3115e6b 100644
--- a/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/TrivialTracingBenchmark.kt
+++ b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/TrivialTracingBenchmark.kt
@@ -23,6 +23,8 @@
 import androidx.benchmark.macro.TraceSectionMetric
 import androidx.benchmark.macro.junit4.MacrobenchmarkRule
 import androidx.benchmark.perfetto.PerfettoCapture
+import androidx.benchmark.perfetto.PerfettoCapture.PerfettoSdkConfig
+import androidx.benchmark.perfetto.PerfettoCapture.PerfettoSdkConfig.InitialProcessState
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
 import org.junit.Rule
@@ -55,8 +57,7 @@
             iterations = 1, // we are only verifying the presence of entries (not the timing data)
             setupBlock = {
                 PerfettoCapture().enableAndroidxTracingPerfetto(
-                    PACKAGE_NAME,
-                    provideBinariesIfMissing = true
+                    PerfettoSdkConfig(PACKAGE_NAME, InitialProcessState.Alive)
                 )
             }
         ) {
diff --git a/compose/lint/common/src/main/java/androidx/compose/lint/KotlinUtils.kt b/compose/lint/common/src/main/java/androidx/compose/lint/KotlinUtils.kt
index 5a39288..9ec00a4 100644
--- a/compose/lint/common/src/main/java/androidx/compose/lint/KotlinUtils.kt
+++ b/compose/lint/common/src/main/java/androidx/compose/lint/KotlinUtils.kt
@@ -16,17 +16,12 @@
 
 package androidx.compose.lint
 
-import org.jetbrains.kotlin.psi.KtElement
 import org.jetbrains.kotlin.psi.KtLambdaExpression
 import org.jetbrains.kotlin.psi.KtParameter
 import org.jetbrains.kotlin.psi.KtSimpleNameExpression
 import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
 import org.jetbrains.kotlin.psi.psiUtil.isAncestor
-import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
 import org.jetbrains.uast.ULambdaExpression
-import org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression
-import org.jetbrains.uast.kotlin.KotlinUastResolveProviderService
-import org.jetbrains.uast.sourcePsiElement
 import org.jetbrains.uast.toUElement
 
 /**
@@ -148,10 +143,3 @@
             else -> true
         }
     }
-
-fun KotlinUFunctionCallExpression.resolveCall() =
-    (sourcePsiElement as KtElement).getResolvedCall(
-        context = sourcePsi.project
-            .getService(KotlinUastResolveProviderService::class.java)
-            .getBindingContext(sourcePsi)
-    )
diff --git a/compose/lint/common/src/main/java/androidx/compose/lint/Names.kt b/compose/lint/common/src/main/java/androidx/compose/lint/Names.kt
index f51de29..0c192ef 100644
--- a/compose/lint/common/src/main/java/androidx/compose/lint/Names.kt
+++ b/compose/lint/common/src/main/java/androidx/compose/lint/Names.kt
@@ -134,6 +134,11 @@
     val kmClassName: ClassName
         get() = pkg.segments.joinToString("/", postfix = "/") +
             nameSegments.joinToString(".")
+
+    /**
+     * The [PackageName] of this element.
+     */
+    val packageName: PackageName get() = pkg
 }
 
 /**
diff --git a/compose/lint/internal-lint-checks/src/main/java/androidx/compose/lint/ComposeIssueRegistry.kt b/compose/lint/internal-lint-checks/src/main/java/androidx/compose/lint/ComposeIssueRegistry.kt
index 223ceaf..7e43f93 100644
--- a/compose/lint/internal-lint-checks/src/main/java/androidx/compose/lint/ComposeIssueRegistry.kt
+++ b/compose/lint/internal-lint-checks/src/main/java/androidx/compose/lint/ComposeIssueRegistry.kt
@@ -32,7 +32,8 @@
             ListIteratorDetector.ISSUE,
             SteppedForLoopDetector.ISSUE,
             UnnecessaryLambdaCreationDetector.ISSUE,
-            PlatformImportInCommonModuleDetector.ISSUE,
+            PlatformReferenceInCommonModuleDetector.IMPORT_ISSUE,
+            PlatformReferenceInCommonModuleDetector.REFERENCE_ISSUE,
             PrimitiveInLambdaDetector.ISSUE,
         )
     }
diff --git a/compose/lint/internal-lint-checks/src/main/java/androidx/compose/lint/PlatformImportInCommonModuleDetector.kt b/compose/lint/internal-lint-checks/src/main/java/androidx/compose/lint/PlatformImportInCommonModuleDetector.kt
deleted file mode 100644
index 856bc57..0000000
--- a/compose/lint/internal-lint-checks/src/main/java/androidx/compose/lint/PlatformImportInCommonModuleDetector.kt
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.lint
-
-import com.android.tools.lint.client.api.UElementHandler
-import com.android.tools.lint.detector.api.Category
-import com.android.tools.lint.detector.api.Detector
-import com.android.tools.lint.detector.api.Implementation
-import com.android.tools.lint.detector.api.Issue
-import com.android.tools.lint.detector.api.JavaContext
-import com.android.tools.lint.detector.api.Scope
-import com.android.tools.lint.detector.api.Severity
-import com.android.tools.lint.detector.api.SourceCodeScanner
-import org.jetbrains.uast.UImportStatement
-import org.jetbrains.uast.getContainingUFile
-import org.jetbrains.uast.getIoFile
-
-/**
- * Lint [Detector] that catches platform-dependent imports in a common module.
- */
-class PlatformImportInCommonModuleDetector : Detector(), SourceCodeScanner {
-
-    override fun getApplicableUastTypes() =
-        listOf(UImportStatement::class.java)
-
-    override fun createUastHandler(context: JavaContext): UElementHandler =
-        object : UElementHandler() {
-            // this only verifies imports and not fqname references
-            // potentially we want to make sure to search for those too
-
-            override fun visitImportStatement(node: UImportStatement) {
-                val reference = node.importReference?.asRenderString() ?: return
-                val isPlatformImport = PLATFORM_PACKAGES.any { platformPackage ->
-                    (platformPackage == reference && node.isOnDemand) ||
-                        reference.startsWith("$platformPackage.")
-                }
-                if (!isPlatformImport) return
-
-                val file = node.getContainingUFile()?.getIoFile() ?: return
-                val isInCommonModule = file.absolutePath.contains(COMMON_MAIN_PATH_PREFIX)
-                if (!isInCommonModule) return
-
-                val target = node.importReference!!
-                context.report(
-                    ISSUE,
-                    target,
-                    context.getLocation(target),
-                    "Platform-dependent import in a common module"
-                )
-            }
-        }
-
-    companion object {
-        val ISSUE = Issue.create(
-            id = "PlatformImportInCommonModule",
-            briefDescription = "Platform-dependent import in a common module",
-            explanation = "Common Kotlin module cannot contain references to JVM or Android " +
-                "classes, as it reduces future portability to other Kotlin targets. Instead of " +
-                "referencing them directly, use expect/actual declarations.",
-            category = Category.CORRECTNESS,
-            priority = 5,
-            severity = Severity.ERROR,
-            implementation = Implementation(
-                PlatformImportInCommonModuleDetector::class.java,
-                Scope.JAVA_FILE_SCOPE
-            )
-        )
-
-        private const val COMMON_MAIN_PATH_PREFIX = "src/commonMain"
-        private val PLATFORM_PACKAGES = listOf(
-            "java",
-            "javax",
-            "android"
-        )
-    }
-}
diff --git a/compose/lint/internal-lint-checks/src/main/java/androidx/compose/lint/PlatformReferenceInCommonModuleDetector.kt b/compose/lint/internal-lint-checks/src/main/java/androidx/compose/lint/PlatformReferenceInCommonModuleDetector.kt
new file mode 100644
index 0000000..77982f8
--- /dev/null
+++ b/compose/lint/internal-lint-checks/src/main/java/androidx/compose/lint/PlatformReferenceInCommonModuleDetector.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.lint
+
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiMethod
+import org.jetbrains.uast.UElement
+import org.jetbrains.uast.UImportStatement
+import org.jetbrains.uast.USimpleNameReferenceExpression
+import org.jetbrains.uast.tryResolve
+
+/**
+ * Lint [Detector] that catches platform-dependent references in a common module.
+ */
+class PlatformReferenceInCommonModuleDetector : Detector(), SourceCodeScanner {
+
+    override fun getApplicableUastTypes(): List<Class<out UElement>> =
+        listOf(UImportStatement::class.java, USimpleNameReferenceExpression::class.java)
+
+    override fun createUastHandler(context: JavaContext): UElementHandler {
+        if (!context.file.absolutePath.contains(COMMON_MAIN_PATH_PREFIX)) {
+            return UElementHandler.NONE
+        }
+
+        return object : UElementHandler() {
+            override fun visitImportStatement(node: UImportStatement) {
+                val reference = node.importReference?.asRenderString() ?: return
+                val isPlatformImport = PLATFORM_PACKAGES.any { platformPackage ->
+                    (platformPackage == reference && node.isOnDemand) ||
+                        reference.startsWith("$platformPackage.")
+                }
+                if (!isPlatformImport) return
+
+                val target = node.importReference!!
+                context.report(
+                    IMPORT_ISSUE,
+                    target,
+                    context.getLocation(target),
+                    "Platform-dependent import in a common module"
+                )
+            }
+
+            override fun visitSimpleNameReferenceExpression(node: USimpleNameReferenceExpression) {
+                if (node.identifier in RESTRICTED_PROPERTIES) {
+                    val method = node.tryResolve()
+                    if (method !is PsiMethod) return
+
+                    val fqName = RESTRICTED_PROPERTIES[node.identifier]!!
+                    if (method.name != fqName.shortName) return
+                    if (!method.isInPackageName(fqName.packageName)) return
+
+                    context.report(
+                        REFERENCE_ISSUE,
+                        node,
+                        context.getLocation(node),
+                        "Platform reference in a common module"
+                    )
+                }
+            }
+        }
+    }
+
+    companion object {
+        val IMPORT_ISSUE = Issue.create(
+            id = "PlatformImportInCommonModule",
+            briefDescription = "Platform-dependent import in a common module",
+            explanation = "Common Kotlin module cannot contain references to JVM or Android " +
+                "classes, as it reduces future portability to other Kotlin targets. Consider " +
+                "alternative methods allowed in common Kotlin code, or use expect/actual " +
+                "to reference the platform code instead.",
+            category = Category.CORRECTNESS,
+            priority = 5,
+            severity = Severity.ERROR,
+            implementation = Implementation(
+                PlatformReferenceInCommonModuleDetector::class.java,
+                Scope.JAVA_FILE_SCOPE
+            )
+        )
+
+        val REFERENCE_ISSUE = Issue.create(
+            id = "PlatformReferenceInCommonModule",
+            briefDescription = "Platform-dependent reference in a common module",
+            explanation = "Common Kotlin module cannot contain references to JVM or Android " +
+                "classes, as it reduces future portability to other Kotlin targets. Consider " +
+                "alternative methods allowed in common Kotlin code, or use expect/actual " +
+                "to reference the platform code instead.",
+            category = Category.CORRECTNESS,
+            priority = 5,
+            severity = Severity.ERROR,
+            implementation = Implementation(
+                PlatformReferenceInCommonModuleDetector::class.java,
+                Scope.JAVA_FILE_SCOPE
+            )
+        )
+
+        private const val COMMON_MAIN_PATH_PREFIX = "src/commonMain"
+        private val PLATFORM_PACKAGES = listOf(
+            "java",
+            "javax",
+            "android"
+        )
+        private val RESTRICTED_PROPERTIES = mapOf(
+            "javaClass" to Name(Package("kotlin.jvm"), "getJavaClass"),
+            "java" to Name(Package("kotlin.jvm"), "getJavaClass"),
+        )
+    }
+}
diff --git a/compose/lint/internal-lint-checks/src/test/java/androidx/compose/lint/PlatformImportInCommonModuleDetectorTest.kt b/compose/lint/internal-lint-checks/src/test/java/androidx/compose/lint/PlatformImportInCommonModuleDetectorTest.kt
deleted file mode 100644
index e716456..0000000
--- a/compose/lint/internal-lint-checks/src/test/java/androidx/compose/lint/PlatformImportInCommonModuleDetectorTest.kt
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-@file:Suppress("UnstableApiUsage")
-
-package androidx.compose.lint
-
-import com.android.tools.lint.checks.infrastructure.LintDetectorTest
-import com.android.tools.lint.detector.api.Detector
-import com.android.tools.lint.detector.api.Issue
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-
-/* ktlint-disable max-line-length */
-@RunWith(JUnit4::class)
-class PlatformImportInCommonModuleDetectorTest : LintDetectorTest() {
-    override fun getDetector(): Detector = PlatformImportInCommonModuleDetector()
-
-    override fun getIssues(): MutableList<Issue> = mutableListOf(
-        PlatformImportInCommonModuleDetector.ISSUE
-    )
-
-    @Test
-    fun detectsImportInCommonMain() {
-        val file = kotlin(
-            "commonMain/test/TestFile.kt",
-            """
-                package test
-
-                import java.util.ArrayList as MyList
-                import java.util.*
-                import java.*
-                import android.os.Bundle
-                import android.*
-            """
-        ).within("src")
-
-        lint().files(
-            file
-        )
-            .run()
-            .expect(
-                """
-src/commonMain/test/TestFile.kt:4: Error: Platform-dependent import in a common module [PlatformImportInCommonModule]
-                import java.util.ArrayList as MyList
-                       ~~~~~~~~~~~~~~~~~~~
-src/commonMain/test/TestFile.kt:5: Error: Platform-dependent import in a common module [PlatformImportInCommonModule]
-                import java.util.*
-                       ~~~~~~~~~
-src/commonMain/test/TestFile.kt:6: Error: Platform-dependent import in a common module [PlatformImportInCommonModule]
-                import java.*
-                       ~~~~
-src/commonMain/test/TestFile.kt:7: Error: Platform-dependent import in a common module [PlatformImportInCommonModule]
-                import android.os.Bundle
-                       ~~~~~~~~~~~~~~~~~
-src/commonMain/test/TestFile.kt:8: Error: Platform-dependent import in a common module [PlatformImportInCommonModule]
-                import android.*
-                       ~~~~~~~
-5 errors, 0 warnings
-                """.trimIndent()
-            )
-    }
-
-    @Test
-    fun ignoresImportInOtherModules() {
-        val jvmFile = kotlin(
-            "jvmMain/test/TestFile.kt",
-            """
-                package test
-
-                import java.util.ArrayList as MyList
-                import java.util.*
-                import java.*
-                import android.os.Bundle
-                import android.*
-            """
-        ).within("src")
-
-        val androidFile = kotlin(
-            "androidMain/test/TestFile.kt",
-            """
-                package test
-
-                import java.util.*
-                import java.*
-                import android.os.Bundle
-                import android.*
-            """
-        ).within("src")
-
-        val file = kotlin(
-            "main/test/TestFile.kt",
-            """
-                package test
-
-                import java.util.*
-                import java.*
-                import android.os.Bundle
-                import android.*
-            """
-        ).within("src")
-
-        lint().files(
-            file,
-            androidFile,
-            jvmFile
-        )
-            .run()
-            .expectClean()
-    }
-}
-/* ktlint-enable max-line-length */
diff --git a/compose/lint/internal-lint-checks/src/test/java/androidx/compose/lint/PlatformReferenceInCommonModuleDetectorTest.kt b/compose/lint/internal-lint-checks/src/test/java/androidx/compose/lint/PlatformReferenceInCommonModuleDetectorTest.kt
new file mode 100644
index 0000000..811a9a1
--- /dev/null
+++ b/compose/lint/internal-lint-checks/src/test/java/androidx/compose/lint/PlatformReferenceInCommonModuleDetectorTest.kt
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.compose.lint
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+/* ktlint-disable max-line-length */
+@RunWith(JUnit4::class)
+class PlatformReferenceInCommonModuleDetectorTest : LintDetectorTest() {
+    override fun getDetector(): Detector = PlatformReferenceInCommonModuleDetector()
+
+    override fun getIssues(): MutableList<Issue> = mutableListOf(
+        PlatformReferenceInCommonModuleDetector.IMPORT_ISSUE,
+        PlatformReferenceInCommonModuleDetector.REFERENCE_ISSUE
+    )
+
+    @Test
+    fun detectsImportInCommonMain() {
+        val file = kotlin(
+            "commonMain/test/TestFile.kt",
+            """
+                package test
+
+                import java.util.ArrayList as MyList
+                import java.util.*
+                import java.*
+                import android.os.Bundle
+                import android.*
+            """
+        ).within("src")
+
+        lint().files(
+            file
+        )
+            .run()
+            .expect(
+                """
+src/commonMain/test/TestFile.kt:4: Error: Platform-dependent import in a common module [PlatformImportInCommonModule]
+                import java.util.ArrayList as MyList
+                       ~~~~~~~~~~~~~~~~~~~
+src/commonMain/test/TestFile.kt:5: Error: Platform-dependent import in a common module [PlatformImportInCommonModule]
+                import java.util.*
+                       ~~~~~~~~~
+src/commonMain/test/TestFile.kt:6: Error: Platform-dependent import in a common module [PlatformImportInCommonModule]
+                import java.*
+                       ~~~~
+src/commonMain/test/TestFile.kt:7: Error: Platform-dependent import in a common module [PlatformImportInCommonModule]
+                import android.os.Bundle
+                       ~~~~~~~~~~~~~~~~~
+src/commonMain/test/TestFile.kt:8: Error: Platform-dependent import in a common module [PlatformImportInCommonModule]
+                import android.*
+                       ~~~~~~~
+5 errors, 0 warnings
+                """.trimIndent()
+            )
+    }
+
+    @Test
+    fun detectsJavaClassCallsInCommonMain() {
+        val file = kotlin(
+            "commonMain/test/TestFile.kt",
+            """
+                package test
+
+                fun test(test: String) {
+                    test.javaClass
+                    test::class.java
+                }
+
+                class AttributeSnapshot {
+                  // since data classes can't be subclassed
+                  override fun equals(other: Any?): Boolean {
+                    if (javaClass != other?.javaClass) return false
+                    return true
+                  }
+                }
+            """
+        ).within("src")
+
+        lint().files(
+            file
+        )
+            .run()
+            .expect(
+                """
+src/commonMain/test/TestFile.kt:5: Error: Platform reference in a common module [PlatformReferenceInCommonModule]
+                    test.javaClass
+                         ~~~~~~~~~
+src/commonMain/test/TestFile.kt:6: Error: Platform reference in a common module [PlatformReferenceInCommonModule]
+                    test::class.java
+                                ~~~~
+src/commonMain/test/TestFile.kt:12: Error: Platform reference in a common module [PlatformReferenceInCommonModule]
+                    if (javaClass != other?.javaClass) return false
+                        ~~~~~~~~~
+src/commonMain/test/TestFile.kt:12: Error: Platform reference in a common module [PlatformReferenceInCommonModule]
+                    if (javaClass != other?.javaClass) return false
+                                            ~~~~~~~~~
+4 errors, 0 warnings
+                """.trimIndent()
+            )
+    }
+
+    @Test
+    fun ignoresImportInOtherModules() {
+        val jvmFile = kotlin(
+            "jvmMain/test/TestFile.kt",
+            """
+                package test
+
+                import java.util.ArrayList as MyList
+                import java.util.*
+                import java.*
+                import android.os.Bundle
+                import android.*
+            """
+        ).within("src")
+
+        val androidFile = kotlin(
+            "androidMain/test/TestFile.kt",
+            """
+                package test
+
+                import java.util.*
+                import java.*
+                import android.os.Bundle
+                import android.*
+            """
+        ).within("src")
+
+        val file = kotlin(
+            "main/test/TestFile.kt",
+            """
+                package test
+
+                import java.util.*
+                import java.*
+                import android.os.Bundle
+                import android.*
+            """
+        ).within("src")
+
+        lint().files(
+            file,
+            androidFile,
+            jvmFile
+        )
+            .run()
+            .expectClean()
+    }
+
+    @Test
+    fun ignoresJavaClassCallsInOtherSourceSets() {
+        val jvmFile = kotlin(
+            "jvmMain/test/TestFile.kt",
+            """
+                package test
+
+                fun test(test: String) {
+                    test.javaClass
+                    test::class.java
+                }
+
+                class AttributeSnapshot {
+                  // since data classes can't be subclassed
+                  override fun equals(other: Any?): Boolean {
+                    if (javaClass != other?.javaClass) return false
+                    return true
+                  }
+                }
+            """
+        ).within("src")
+
+        val androidFile = kotlin(
+            "androidMain/test/TestFile.kt",
+            """
+                package test
+
+                fun test(test: String) {
+                    test.javaClass
+                    test::class.java
+                }
+
+                class AttributeSnapshot {
+                  // since data classes can't be subclassed
+                  override fun equals(other: Any?): Boolean {
+                    if (javaClass != other?.javaClass) return false
+                    return true
+                  }
+                }
+            """
+        ).within("src")
+
+        val file = kotlin(
+            "main/test/TestFile.kt",
+            """
+                package test
+
+                fun test(test: String) {
+                    test.javaClass
+                    test::class.java
+                }
+
+                class AttributeSnapshot {
+                  // since data classes can't be subclassed
+                  override fun equals(other: Any?): Boolean {
+                    if (javaClass != other?.javaClass) return false
+                    return true
+                  }
+                }
+            """
+        ).within("src")
+
+        lint().files(
+            file,
+            androidFile,
+            jvmFile
+        )
+            .run()
+            .expectClean()
+    }
+}
+/* ktlint-enable max-line-length */
diff --git a/compose/lint/internal-lint-checks/src/test/java/androidx/compose/lint/SteppedForLoopDetectorTest.kt b/compose/lint/internal-lint-checks/src/test/java/androidx/compose/lint/SteppedForLoopDetectorTest.kt
index be654b0..15559c9 100644
--- a/compose/lint/internal-lint-checks/src/test/java/androidx/compose/lint/SteppedForLoopDetectorTest.kt
+++ b/compose/lint/internal-lint-checks/src/test/java/androidx/compose/lint/SteppedForLoopDetectorTest.kt
@@ -21,6 +21,7 @@
 import com.android.tools.lint.checks.infrastructure.LintDetectorTest
 import com.android.tools.lint.detector.api.Detector
 import com.android.tools.lint.detector.api.Issue
+import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
@@ -81,6 +82,7 @@
             .expectClean()
     }
 
+    @Ignore("b/289814619")
     @Test
     fun calledOnSteppedLoop() {
         lint().files(
@@ -113,6 +115,7 @@
             )
     }
 
+    @Ignore("b/289814619")
     @Test
     fun calledOnConstantSteppedLoop() {
         lint().files(
@@ -145,6 +148,7 @@
             )
     }
 
+    @Ignore("b/289814619")
     @Test
     fun calledOnUnitSteppedLoop() {
         lint().files(
@@ -177,6 +181,7 @@
             )
     }
 
+    @Ignore("b/289814619")
     @Test
     fun calledOnExpressionSteppedLoop() {
         lint().files(
diff --git a/compose/material/material-icons-core/build.gradle b/compose/material/material-icons-core/build.gradle
index dc8b062..fe670b2 100644
--- a/compose/material/material-icons-core/build.gradle
+++ b/compose/material/material-icons-core/build.gradle
@@ -100,7 +100,6 @@
         if (desktopEnabled) {
             desktopTest {
                 dependsOn(jvmTest)
-                dependsOn(desktopMain)
                 dependencies {
                 }
             }
diff --git a/compose/material/material-icons-extended/build.gradle b/compose/material/material-icons-extended/build.gradle
index 89ea04b..ddff520 100644
--- a/compose/material/material-icons-extended/build.gradle
+++ b/compose/material/material-icons-extended/build.gradle
@@ -119,7 +119,6 @@
         if (desktopEnabled) {
             desktopTest {
                 dependsOn(jvmTest)
-                dependsOn(desktopMain)
                 dependencies {
                 }
             }
diff --git a/compose/material/material-ripple/benchmark/build.gradle b/compose/material/material-ripple/benchmark/build.gradle
new file mode 100644
index 0000000..ef1a72f
--- /dev/null
+++ b/compose/material/material-ripple/benchmark/build.gradle
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import androidx.build.LibraryType
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("AndroidXComposePlugin")
+    id("org.jetbrains.kotlin.android")
+    id("androidx.benchmark")
+}
+
+dependencies {
+    androidTestImplementation project(":benchmark:benchmark-junit4")
+    androidTestImplementation project(":compose:benchmark-utils")
+    androidTestImplementation project(":compose:foundation:foundation")
+    androidTestImplementation project(":compose:material:material-ripple")
+    androidTestImplementation project(":compose:runtime:runtime")
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.junit)
+    androidTestImplementation(libs.kotlinStdlib)
+    androidTestImplementation(libs.kotlinReflect)
+    androidTestImplementation(libs.kotlinTestCommon)
+    androidTestImplementation(libs.truth)
+}
+
+android {
+    namespace "androidx.compose.material.ripple.benchmark"
+    defaultConfig {
+        // must be one of: 'None', 'StackSampling', or 'MethodTracing'
+        testInstrumentationRunnerArguments["androidx.benchmark.profiling.mode"]= 'None'
+    }
+}
+
+androidx {
+    type = LibraryType.INTERNAL_TEST_LIBRARY
+}
diff --git a/compose/material/material-ripple/benchmark/src/androidTest/AndroidManifest.xml b/compose/material/material-ripple/benchmark/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..9face20
--- /dev/null
+++ b/compose/material/material-ripple/benchmark/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+    <application>
+        <!-- enable profiling by shell for non-intrusive profiling tools -->
+        <profileable android:shell="true"/>
+    </application>
+</manifest>
diff --git a/compose/material/material-ripple/benchmark/src/androidTest/java/androidx/compose/material/ripple/benchmark/RippleBenchmark.kt b/compose/material/material-ripple/benchmark/src/androidTest/java/androidx/compose/material/ripple/benchmark/RippleBenchmark.kt
new file mode 100644
index 0000000..21647ce
--- /dev/null
+++ b/compose/material/material-ripple/benchmark/src/androidTest/java/androidx/compose/material/ripple/benchmark/RippleBenchmark.kt
@@ -0,0 +1,315 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material.ripple.benchmark
+
+import androidx.compose.foundation.Indication
+import androidx.compose.foundation.interaction.HoverInteraction
+import androidx.compose.foundation.interaction.Interaction
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.PressInteraction
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.ripple.rememberRipple
+import androidx.compose.runtime.Composable
+import androidx.compose.testutils.ComposeBenchmarkScope
+import androidx.compose.testutils.LayeredComposeTestCase
+import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
+import androidx.compose.testutils.benchmark.benchmarkFirstCompose
+import androidx.compose.testutils.doFramesUntilNoChangesPending
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawWithContent
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.platform.ViewRootForTest
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.runBlocking
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Benchmark for Android ripple performance
+ */
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class RippleBenchmark {
+
+    @get:Rule
+    val benchmarkRule = ComposeBenchmarkRule()
+
+    /**
+     * Composition cost of rememberRipple() - this is just a remembered factory object so it
+     * doesn't do much.
+     */
+    @Test
+    fun rememberRippleFirstComposition() {
+        benchmarkRule.benchmarkFirstCompose {
+            object : LayeredComposeTestCase() {
+                @Composable
+                override fun MeasuredContent() {
+                    rememberRipple()
+                }
+            }
+        }
+    }
+
+    /**
+     * Composition cost of creating a ripple instance - this is what actually allocates
+     * ripple-related machinery and is later responsible for drawing ripples.
+     */
+    @Test
+    fun initialRippleRememberUpdatedInstanceFirstComposition() {
+        benchmarkRule.benchmarkFirstCompose {
+            object : LayeredComposeTestCase() {
+                val interactionSource = MutableInteractionSource()
+                var ripple: Indication? = null
+
+                @Composable
+                override fun ContentWrappers(content: @Composable () -> Unit) {
+                    // Create a ripple from outside the measured content
+                    ripple = rememberRipple()
+                    content()
+                }
+
+                @Composable
+                override fun MeasuredContent() {
+                    ripple!!.rememberUpdatedInstance(interactionSource = interactionSource)
+                }
+            }
+        }
+    }
+
+    /**
+     * Composition cost of creating a second ripple instance, after one has already been created,
+     * discounting any first-ripple performance costs.
+     */
+    @Test
+    fun additionalRippleRememberUpdatedInstanceFirstComposition() {
+        benchmarkRule.benchmarkFirstCompose {
+            object : LayeredComposeTestCase() {
+                val interactionSource = MutableInteractionSource()
+                var ripple: Indication? = null
+
+                @Composable
+                override fun ContentWrappers(content: @Composable () -> Unit) {
+                    // Create a ripple from outside the measured content
+                    ripple = rememberRipple()
+                    // Create another ripple and call rememberUpdatedInstance()
+                    rememberRipple().rememberUpdatedInstance(interactionSource = interactionSource)
+                    content()
+                }
+
+                @Composable
+                override fun MeasuredContent() {
+                    ripple!!.rememberUpdatedInstance(interactionSource = interactionSource)
+                }
+            }
+        }
+    }
+
+    /**
+     * Cost of emitting a [PressInteraction] for the first time (so including any work done as part
+     * of collecting the interaction and creating a ripple) and then rendering a frame after that.
+     *
+     * [PressInteraction] tests the RippleDrawable codepath - other [Interaction]s use a simplified
+     * common-code StateLayer.
+     */
+    @Test
+    fun initialEmitPressInteraction() {
+        val press = PressInteraction.Press(Offset.Zero)
+
+        with(benchmarkRule) {
+            runBenchmarkFor({ RippleInteractionTestCase() }) {
+                measureRepeated {
+                    runWithTimingDisabled {
+                        doFramesUntilNoChangesMeasureLayoutOrDrawPending()
+                    }
+
+                    runBlocking { getTestCase().emitInteraction(press) }
+                    doFrame()
+
+                    // We explicitly tear down after each iteration so we incur costs for anything
+                    // cached at the view hierarchy level, in this case the RippleContainer.
+                    runWithTimingDisabled {
+                        disposeContent()
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Cost of emitting another [PressInteraction] and rendering a frame after we have already
+     * emitted one.
+     */
+    @Test
+    fun additionalEmitPressInteraction() {
+        val press1 = PressInteraction.Press(Offset.Zero)
+        val release1 = PressInteraction.Release(press1)
+        val press2 = PressInteraction.Press(Offset.Zero)
+
+        with(benchmarkRule) {
+            runBenchmarkFor({ RippleInteractionTestCase() }) {
+                measureRepeated {
+                    runWithTimingDisabled {
+                        doFramesUntilNoChangesMeasureLayoutOrDrawPending()
+                        runBlocking {
+                            getTestCase().emitInteraction(press1)
+                            // Account for RippleHostView#setRippleState logic that will delay
+                            // an exit that happens on the same frame as an enter which will cause
+                            // a callback to be posted, breaking synchronization in this case /
+                            // causing a more costly and rare codepath.
+                            delay(100)
+                            getTestCase().emitInteraction(release1)
+                        }
+                        doFramesUntilNoChangesMeasureLayoutOrDrawPending()
+                    }
+
+                    runBlocking { getTestCase().emitInteraction(press2) }
+                    doFrame()
+
+                    runWithTimingDisabled {
+                        disposeContent()
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Cost of emitting a [HoverInteraction] for the first time and then rendering a frame after
+     * that.
+     *
+     * [HoverInteraction] ends up being drawn by a common-code StateLayer, as with focus and drag -
+     * so there is no need to test those cases separately.
+     */
+    @Test
+    fun initialEmitHoverInteraction() {
+        val hover = HoverInteraction.Enter()
+
+        with(benchmarkRule) {
+            runBenchmarkFor({ RippleInteractionTestCase() }) {
+                measureRepeated {
+                    runWithTimingDisabled {
+                        doFramesUntilNoChangesMeasureLayoutOrDrawPending()
+                    }
+
+                    runBlocking { getTestCase().emitInteraction(hover) }
+                    doFrame()
+
+                    // We explicitly tear down after each iteration so we incur costs for anything
+                    // cached at the view hierarchy level. There shouldn't be anything cached in
+                    // this way for the hover case, but we do it to be consistent with the press
+                    // case.
+                    runWithTimingDisabled {
+                        disposeContent()
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Cost of emitting another [HoverInteraction] and rendering a frame after we have already
+     * emitted one.
+     */
+    @Test
+    fun additionalEmitHoverInteraction() {
+        val hover1 = HoverInteraction.Enter()
+        val unhover1 = HoverInteraction.Exit(hover1)
+        val hover2 = HoverInteraction.Enter()
+
+        with(benchmarkRule) {
+            runBenchmarkFor({ RippleInteractionTestCase() }) {
+                measureRepeated {
+                    runWithTimingDisabled {
+                        doFramesUntilNoChangesMeasureLayoutOrDrawPending()
+                        runBlocking {
+                            getTestCase().emitInteraction(hover1)
+                            getTestCase().emitInteraction(unhover1)
+                        }
+                        doFramesUntilNoChangesMeasureLayoutOrDrawPending()
+                    }
+
+                    runBlocking { getTestCase().emitInteraction(hover2) }
+                    doFrame()
+
+                    runWithTimingDisabled {
+                        disposeContent()
+                    }
+                }
+            }
+        }
+    }
+}
+
+/**
+ * Test case for a manually-drawn ripple (no [androidx.compose.foundation.indication]) that allows
+ * emitting [Interaction]s with [emitInteraction].
+ */
+private class RippleInteractionTestCase : LayeredComposeTestCase() {
+    private val interactionSource = MutableInteractionSource()
+
+    @Composable
+    override fun MeasuredContent() {
+        val instance = rememberRipple().rememberUpdatedInstance(interactionSource)
+
+        Box(
+            Modifier
+                .size(100.dp)
+                .drawWithContent {
+                    with(instance) {
+                        drawIndication()
+                    }
+                })
+    }
+
+    suspend fun emitInteraction(interaction: Interaction) {
+        interactionSource.emit(interaction)
+    }
+}
+
+/**
+ * [doFramesUntilNoChangesPending] but also accounts for pending measure or layout passes, and
+ * whether the view itself is dirty
+ */
+private fun ComposeBenchmarkScope<*>.doFramesUntilNoChangesMeasureLayoutOrDrawPending() {
+    val maxAmountOfFrames = 10
+    var framesDone = 0
+    while (framesDone < maxAmountOfFrames) {
+        doFrame()
+        framesDone++
+
+        fun hasPending(): Boolean {
+            var hasPending = hasPendingChanges()
+            val hostView = getHostView()
+            hasPending = hasPending || (hostView as ViewRootForTest).hasPendingMeasureOrLayout
+            hasPending = hasPending || hostView.isDirty
+            return hasPending
+        }
+
+        if (!(hasPending())) {
+            // We are stable!
+            return
+        }
+    }
+
+    // Still not stable
+    throw AssertionError("Changes are still pending after '$maxAmountOfFrames' frames.")
+}
diff --git a/compose/material/material-ripple/benchmark/src/main/AndroidManifest.xml b/compose/material/material-ripple/benchmark/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..6c631ea
--- /dev/null
+++ b/compose/material/material-ripple/benchmark/src/main/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+    <application/>
+</manifest>
diff --git a/compose/material/material-ripple/build.gradle b/compose/material/material-ripple/build.gradle
index 9e6fbe8..c2a484d 100644
--- a/compose/material/material-ripple/build.gradle
+++ b/compose/material/material-ripple/build.gradle
@@ -117,7 +117,6 @@
         if (desktopEnabled) {
             desktopTest {
                 dependsOn(jvmTest)
-                dependsOn(desktopMain)
                 dependencies {
                 }
             }
diff --git a/compose/material/material-ripple/src/androidMain/kotlin/androidx/compose/material/ripple/Ripple.android.kt b/compose/material/material-ripple/src/androidMain/kotlin/androidx/compose/material/ripple/Ripple.android.kt
index 928b4ff..5708558 100644
--- a/compose/material/material-ripple/src/androidMain/kotlin/androidx/compose/material/ripple/Ripple.android.kt
+++ b/compose/material/material-ripple/src/androidMain/kotlin/androidx/compose/material/ripple/Ripple.android.kt
@@ -70,26 +70,8 @@
             }
         }
 
-        // Create or get the RippleContainer attached to the nearest root Compose view
-
-        var rippleContainer: RippleContainer? = null
-
-        for (index in 0 until view.childCount) {
-            val child = view.getChildAt(index)
-            if (child is RippleContainer) {
-                rippleContainer = child
-                break
-            }
-        }
-
-        if (rippleContainer == null) {
-            rippleContainer = RippleContainer(view.context).apply {
-                view.addView(this)
-            }
-        }
-
-        return remember(interactionSource, this, rippleContainer) {
-            AndroidRippleIndicationInstance(bounded, radius, color, rippleAlpha, rippleContainer)
+        return remember(interactionSource, this, view) {
+            AndroidRippleIndicationInstance(bounded, radius, color, rippleAlpha, view)
         }
     }
 
@@ -129,9 +111,15 @@
     private val radius: Dp,
     private val color: State<Color>,
     private val rippleAlpha: State<RippleAlpha>,
-    private val rippleContainer: RippleContainer
+    private val view: ViewGroup
 ) : RippleIndicationInstance(bounded, rippleAlpha), RememberObserver {
     /**
+     * [RippleContainer] attached to the nearest [ViewGroup]: [view]. If it hasn't already been
+     * created by a another ripple, we will create it and attach it to the hierarchy.
+     */
+    private var rippleContainer: RippleContainer? = null
+
+    /**
      * Backing [RippleHostView] used to draw ripples for this [RippleIndicationInstance].
      * [mutableStateOf] as we want changes to this to invalidate drawing, and cause us to draw /
      * stop drawing a ripple.
@@ -207,7 +195,7 @@
     }
 
     override fun addRipple(interaction: PressInteraction.Press, scope: CoroutineScope) {
-        rippleHostView = with(rippleContainer) {
+        rippleHostView = with(getOrCreateRippleContainer()) {
             getRippleHostView().apply {
                 addRipple(
                     interaction = interaction,
@@ -237,7 +225,7 @@
     }
 
     private fun dispose() {
-        with(rippleContainer) {
+        rippleContainer?.run {
             disposeRippleIfNeeded()
         }
     }
@@ -249,4 +237,26 @@
     fun resetHostView() {
         rippleHostView = null
     }
+
+    private fun getOrCreateRippleContainer(): RippleContainer {
+        if (rippleContainer != null) return rippleContainer!!
+
+        // Find existing RippleContainer in the view hierarchy
+        for (index in 0 until view.childCount) {
+            val child = view.getChildAt(index)
+            if (child is RippleContainer) {
+                rippleContainer = child
+                break
+            }
+        }
+
+        // Create a new RippleContainer if needed
+        if (rippleContainer == null) {
+            rippleContainer = RippleContainer(view.context).apply {
+                view.addView(this)
+            }
+        }
+
+        return rippleContainer!!
+    }
 }
diff --git a/compose/material/material/api/current.txt b/compose/material/material/api/current.txt
index 5f99188..aa36fd8 100644
--- a/compose/material/material/api/current.txt
+++ b/compose/material/material/api/current.txt
@@ -82,7 +82,7 @@
     method public suspend Object? expand(kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public androidx.compose.material.BottomDrawerValue getCurrentValue();
     method public float getOffset();
-    method public float getProgress();
+    method @FloatRange(from=0.0, to=1.0) public float getProgress();
     method public androidx.compose.material.BottomDrawerValue getTargetValue();
     method public boolean isClosed();
     method public boolean isExpanded();
@@ -93,7 +93,7 @@
     property public final boolean isExpanded;
     property public final boolean isOpen;
     property public final float offset;
-    property @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi public final float progress;
+    property @SuppressCompatibility @FloatRange(from=0.0, to=1.0) @androidx.compose.material.ExperimentalMaterialApi public final float progress;
     property public final androidx.compose.material.BottomDrawerValue targetValue;
     field public static final androidx.compose.material.BottomDrawerState.Companion Companion;
   }
@@ -154,7 +154,7 @@
     method public suspend Object? expand(kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public androidx.compose.material.BottomSheetValue getCurrentValue();
     method @Deprecated public float getOffset();
-    method public float getProgress();
+    method @FloatRange(from=0.0, to=1.0) public float getProgress();
     method public androidx.compose.material.BottomSheetValue getTargetValue();
     method public boolean isCollapsed();
     method public boolean isExpanded();
@@ -163,7 +163,7 @@
     property public final boolean isCollapsed;
     property public final boolean isExpanded;
     property @Deprecated public final float offset;
-    property @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi public final float progress;
+    property @SuppressCompatibility @FloatRange(from=0.0, to=1.0) @androidx.compose.material.ExperimentalMaterialApi public final float progress;
     property public final androidx.compose.material.BottomSheetValue targetValue;
     field public static final androidx.compose.material.BottomSheetState.Companion Companion;
   }
@@ -550,14 +550,14 @@
     ctor @Deprecated public ModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional boolean isSkipHalfExpanded, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmStateChange);
     ctor @Deprecated public ModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmStateChange);
     method public androidx.compose.material.ModalBottomSheetValue getCurrentValue();
-    method public float getProgress();
+    method @FloatRange(from=0.0, to=1.0) public float getProgress();
     method public androidx.compose.material.ModalBottomSheetValue getTargetValue();
     method public suspend Object? hide(kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public boolean isVisible();
     method public suspend Object? show(kotlin.coroutines.Continuation<? super kotlin.Unit>);
     property public final androidx.compose.material.ModalBottomSheetValue currentValue;
     property public final boolean isVisible;
-    property @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi public final float progress;
+    property @SuppressCompatibility @FloatRange(from=0.0, to=1.0) @androidx.compose.material.ExperimentalMaterialApi public final float progress;
     property public final androidx.compose.material.ModalBottomSheetValue targetValue;
     field public static final androidx.compose.material.ModalBottomSheetState.Companion Companion;
   }
@@ -607,11 +607,11 @@
     method @Deprecated @androidx.compose.runtime.Composable public static void CircularProgressIndicator(optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth);
     method @androidx.compose.runtime.Composable public static void CircularProgressIndicator(optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth, optional long backgroundColor, optional int strokeCap);
     method @Deprecated @androidx.compose.runtime.Composable public static void CircularProgressIndicator(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth);
-    method @androidx.compose.runtime.Composable public static void CircularProgressIndicator(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth, optional long backgroundColor, optional int strokeCap);
+    method @androidx.compose.runtime.Composable public static void CircularProgressIndicator(@FloatRange(from=0.0, to=1.0) float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth, optional long backgroundColor, optional int strokeCap);
     method @Deprecated @androidx.compose.runtime.Composable public static void LinearProgressIndicator(optional androidx.compose.ui.Modifier modifier, optional long color, optional long backgroundColor);
     method @androidx.compose.runtime.Composable public static void LinearProgressIndicator(optional androidx.compose.ui.Modifier modifier, optional long color, optional long backgroundColor, optional int strokeCap);
     method @Deprecated @androidx.compose.runtime.Composable public static void LinearProgressIndicator(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional long backgroundColor);
-    method @androidx.compose.runtime.Composable public static void LinearProgressIndicator(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional long backgroundColor, optional int strokeCap);
+    method @androidx.compose.runtime.Composable public static void LinearProgressIndicator(@FloatRange(from=0.0, to=1.0) float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional long backgroundColor, optional int strokeCap);
   }
 
   @androidx.compose.runtime.Stable public interface RadioButtonColors {
@@ -685,8 +685,8 @@
   }
 
   public final class SliderKt {
-    method @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void RangeSlider(kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> value, kotlin.jvm.functions.Function1<? super kotlin.ranges.ClosedFloatingPointRange<java.lang.Float>,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material.SliderColors colors);
-    method @androidx.compose.runtime.Composable public static void Slider(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material.SliderColors colors);
+    method @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void RangeSlider(kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> value, kotlin.jvm.functions.Function1<? super kotlin.ranges.ClosedFloatingPointRange<java.lang.Float>,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional @IntRange(from=0L) int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material.SliderColors colors);
+    method @androidx.compose.runtime.Composable public static void Slider(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional @IntRange(from=0L) int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material.SliderColors colors);
   }
 
   public interface SnackbarData {
diff --git a/compose/material/material/api/restricted_current.txt b/compose/material/material/api/restricted_current.txt
index 5f99188..aa36fd8 100644
--- a/compose/material/material/api/restricted_current.txt
+++ b/compose/material/material/api/restricted_current.txt
@@ -82,7 +82,7 @@
     method public suspend Object? expand(kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public androidx.compose.material.BottomDrawerValue getCurrentValue();
     method public float getOffset();
-    method public float getProgress();
+    method @FloatRange(from=0.0, to=1.0) public float getProgress();
     method public androidx.compose.material.BottomDrawerValue getTargetValue();
     method public boolean isClosed();
     method public boolean isExpanded();
@@ -93,7 +93,7 @@
     property public final boolean isExpanded;
     property public final boolean isOpen;
     property public final float offset;
-    property @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi public final float progress;
+    property @SuppressCompatibility @FloatRange(from=0.0, to=1.0) @androidx.compose.material.ExperimentalMaterialApi public final float progress;
     property public final androidx.compose.material.BottomDrawerValue targetValue;
     field public static final androidx.compose.material.BottomDrawerState.Companion Companion;
   }
@@ -154,7 +154,7 @@
     method public suspend Object? expand(kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public androidx.compose.material.BottomSheetValue getCurrentValue();
     method @Deprecated public float getOffset();
-    method public float getProgress();
+    method @FloatRange(from=0.0, to=1.0) public float getProgress();
     method public androidx.compose.material.BottomSheetValue getTargetValue();
     method public boolean isCollapsed();
     method public boolean isExpanded();
@@ -163,7 +163,7 @@
     property public final boolean isCollapsed;
     property public final boolean isExpanded;
     property @Deprecated public final float offset;
-    property @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi public final float progress;
+    property @SuppressCompatibility @FloatRange(from=0.0, to=1.0) @androidx.compose.material.ExperimentalMaterialApi public final float progress;
     property public final androidx.compose.material.BottomSheetValue targetValue;
     field public static final androidx.compose.material.BottomSheetState.Companion Companion;
   }
@@ -550,14 +550,14 @@
     ctor @Deprecated public ModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional boolean isSkipHalfExpanded, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmStateChange);
     ctor @Deprecated public ModalBottomSheetState(androidx.compose.material.ModalBottomSheetValue initialValue, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material.ModalBottomSheetValue,java.lang.Boolean> confirmStateChange);
     method public androidx.compose.material.ModalBottomSheetValue getCurrentValue();
-    method public float getProgress();
+    method @FloatRange(from=0.0, to=1.0) public float getProgress();
     method public androidx.compose.material.ModalBottomSheetValue getTargetValue();
     method public suspend Object? hide(kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public boolean isVisible();
     method public suspend Object? show(kotlin.coroutines.Continuation<? super kotlin.Unit>);
     property public final androidx.compose.material.ModalBottomSheetValue currentValue;
     property public final boolean isVisible;
-    property @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi public final float progress;
+    property @SuppressCompatibility @FloatRange(from=0.0, to=1.0) @androidx.compose.material.ExperimentalMaterialApi public final float progress;
     property public final androidx.compose.material.ModalBottomSheetValue targetValue;
     field public static final androidx.compose.material.ModalBottomSheetState.Companion Companion;
   }
@@ -607,11 +607,11 @@
     method @Deprecated @androidx.compose.runtime.Composable public static void CircularProgressIndicator(optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth);
     method @androidx.compose.runtime.Composable public static void CircularProgressIndicator(optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth, optional long backgroundColor, optional int strokeCap);
     method @Deprecated @androidx.compose.runtime.Composable public static void CircularProgressIndicator(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth);
-    method @androidx.compose.runtime.Composable public static void CircularProgressIndicator(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth, optional long backgroundColor, optional int strokeCap);
+    method @androidx.compose.runtime.Composable public static void CircularProgressIndicator(@FloatRange(from=0.0, to=1.0) float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional float strokeWidth, optional long backgroundColor, optional int strokeCap);
     method @Deprecated @androidx.compose.runtime.Composable public static void LinearProgressIndicator(optional androidx.compose.ui.Modifier modifier, optional long color, optional long backgroundColor);
     method @androidx.compose.runtime.Composable public static void LinearProgressIndicator(optional androidx.compose.ui.Modifier modifier, optional long color, optional long backgroundColor, optional int strokeCap);
     method @Deprecated @androidx.compose.runtime.Composable public static void LinearProgressIndicator(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional long backgroundColor);
-    method @androidx.compose.runtime.Composable public static void LinearProgressIndicator(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional long backgroundColor, optional int strokeCap);
+    method @androidx.compose.runtime.Composable public static void LinearProgressIndicator(@FloatRange(from=0.0, to=1.0) float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional long backgroundColor, optional int strokeCap);
   }
 
   @androidx.compose.runtime.Stable public interface RadioButtonColors {
@@ -685,8 +685,8 @@
   }
 
   public final class SliderKt {
-    method @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void RangeSlider(kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> value, kotlin.jvm.functions.Function1<? super kotlin.ranges.ClosedFloatingPointRange<java.lang.Float>,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material.SliderColors colors);
-    method @androidx.compose.runtime.Composable public static void Slider(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material.SliderColors colors);
+    method @SuppressCompatibility @androidx.compose.material.ExperimentalMaterialApi @androidx.compose.runtime.Composable public static void RangeSlider(kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> value, kotlin.jvm.functions.Function1<? super kotlin.ranges.ClosedFloatingPointRange<java.lang.Float>,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional @IntRange(from=0L) int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material.SliderColors colors);
+    method @androidx.compose.runtime.Composable public static void Slider(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional @IntRange(from=0L) int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.material.SliderColors colors);
   }
 
   public interface SnackbarData {
diff --git a/compose/material/material/build.gradle b/compose/material/material/build.gradle
index 4fc4668..8da78ab39 100644
--- a/compose/material/material/build.gradle
+++ b/compose/material/material/build.gradle
@@ -140,7 +140,6 @@
         if (desktopEnabled) {
             desktopTest {
                 dependsOn(jvmTest)
-                dependsOn(desktopMain)
                 dependencies {
                     implementation(project(":compose:ui:ui-test-junit4"))
                     implementation(libs.truth)
@@ -153,6 +152,7 @@
 }
 
 dependencies {
+    lintChecks project(":compose:material:material-lint")
     lintPublish project(":compose:material:material-lint")
 }
 
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AnchoredDraggable.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AnchoredDraggable.kt
index f888873..3ec1356 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AnchoredDraggable.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/AnchoredDraggable.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.material
 
+import androidx.annotation.FloatRange
 import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.SpringSpec
 import androidx.compose.animation.core.animate
@@ -352,7 +353,7 @@
      * The fraction of the progress going from [currentValue] to [closestValue], within [0f..1f]
      * bounds, or 1f if the [AnchoredDraggableState] is in a settled state.
      */
-    /*@FloatRange(from = 0f, to = 1f)*/
+    @get:FloatRange(from = 0.0, to = 1.0)
     val progress: Float by derivedStateOf(structuralEqualityPolicy()) {
         val a = anchors.positionOf(currentValue)
         val b = anchors.positionOf(closestValue)
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomNavigation.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomNavigation.kt
index cad60b6..ee0e23d 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomNavigation.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomNavigation.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.material
 
+import androidx.annotation.FloatRange
 import androidx.compose.animation.core.FastOutSlowInEasing
 import androidx.compose.animation.core.TweenSpec
 import androidx.compose.animation.core.VectorizedAnimationSpec
@@ -254,7 +255,7 @@
 private fun BottomNavigationItemBaselineLayout(
     icon: @Composable () -> Unit,
     label: @Composable (() -> Unit)?,
-    /*@FloatRange(from = 0.0, to = 1.0)*/
+    @FloatRange(from = 0.0, to = 1.0)
     iconPositionAnimationProgress: Float
 ) {
     Layout(
@@ -332,7 +333,7 @@
     labelPlaceable: Placeable,
     iconPlaceable: Placeable,
     constraints: Constraints,
-    /*@FloatRange(from = 0.0, to = 1.0)*/
+    @FloatRange(from = 0.0, to = 1.0)
     iconPositionAnimationProgress: Float
 ): MeasureResult {
     val height = constraints.maxHeight
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomSheetScaffold.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomSheetScaffold.kt
index 1ad6f92..b4bbc9c 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomSheetScaffold.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/BottomSheetScaffold.kt
@@ -15,6 +15,7 @@
  */
 package androidx.compose.material
 
+import androidx.annotation.FloatRange
 import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.layout.Column
@@ -185,7 +186,7 @@
      * The fraction of the progress, within [0f..1f] bounds, or 1f if the [AnchoredDraggableState]
      * is in a settled state.
      */
-    /*@FloatRange(from = 0f, to = 1f)*/
+    @get:FloatRange(from = 0.0, to = 1.0)
     @ExperimentalMaterialApi
     val progress: Float
         get() = anchoredDraggableState.progress
@@ -436,11 +437,9 @@
     content: @Composable (PaddingValues) -> Unit
 ) {
     // b/278692145 Remove this once deprecated methods without density are removed
-    if (scaffoldState.bottomSheetState.density == null) {
-        val density = LocalDensity.current
-        SideEffect {
-            scaffoldState.bottomSheetState.density = density
-        }
+    val density = LocalDensity.current
+    SideEffect {
+        scaffoldState.bottomSheetState.density = density
     }
 
     val peekHeightPx = with(LocalDensity.current) { sheetPeekHeight.toPx() }
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ContentAlpha.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ContentAlpha.kt
index 930b61e..a3091b1 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ContentAlpha.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ContentAlpha.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.material
 
+import androidx.annotation.FloatRange
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.compositionLocalOf
 import androidx.compose.ui.graphics.luminance
@@ -69,9 +70,9 @@
      */
     @Composable
     private fun contentAlpha(
-        /*@FloatRange(from = 0.0, to = 1.0)*/
+        @FloatRange(from = 0.0, to = 1.0)
         highContrastAlpha: Float,
-        /*@FloatRange(from = 0.0, to = 1.0)*/
+        @FloatRange(from = 0.0, to = 1.0)
         lowContrastAlpha: Float
     ): Float {
         val contentColor = LocalContentColor.current
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt
index 8b5883f..bd51ba9 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Drawer.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.material
 
+import androidx.annotation.FloatRange
 import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.TweenSpec
 import androidx.compose.animation.core.animateFloatAsState
@@ -327,7 +328,7 @@
      * The fraction of the progress, within [0f..1f] bounds, or 1f if the [AnchoredDraggableState]
      * is in a settled state.
      */
-    /*@FloatRange(from = 0f, to = 1f)*/
+    @get:FloatRange(from = 0.0, to = 1.0)
     @ExperimentalMaterialApi
     val progress: Float
         get() = anchoredDraggableState.progress
@@ -647,11 +648,9 @@
     content: @Composable () -> Unit
 ) {
     // b/278692145 Remove this once deprecated methods without density are removed
-    if (drawerState.density == null) {
-        val density = LocalDensity.current
-        SideEffect {
-            drawerState.density = density
-        }
+    val density = LocalDensity.current
+    SideEffect {
+        drawerState.density = density
     }
     val scope = rememberCoroutineScope()
 
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt
index 081ca0b..a07039a 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ModalBottomSheet.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.material
 
+import androidx.annotation.FloatRange
 import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.TweenSpec
 import androidx.compose.animation.core.animateFloatAsState
@@ -226,7 +227,7 @@
      * The fraction of the progress, within [0f..1f] bounds, or 1f if the [AnchoredDraggableState]
      * is in a settled state.
      */
-    /*@FloatRange(from = 0f, to = 1f)*/
+    @get:FloatRange(from = 0.0, to = 1.0)
     @ExperimentalMaterialApi
     val progress: Float
         get() = anchoredDraggableState.progress
@@ -563,11 +564,9 @@
     content: @Composable () -> Unit
 ) {
     // b/278692145 Remove this once deprecated methods without density are removed
-    if (sheetState.density == null) {
-        val density = LocalDensity.current
-        SideEffect {
-            sheetState.density = density
-        }
+    val density = LocalDensity.current
+    SideEffect {
+        sheetState.density = density
     }
     val scope = rememberCoroutineScope()
     val orientation = Orientation.Vertical
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/NavigationRail.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/NavigationRail.kt
index 78907a7..e2b37e2 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/NavigationRail.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/NavigationRail.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.material
 
+import androidx.annotation.FloatRange
 import androidx.compose.animation.core.FastOutSlowInEasing
 import androidx.compose.animation.core.TweenSpec
 import androidx.compose.animation.core.VectorizedAnimationSpec
@@ -259,7 +260,7 @@
 private fun NavigationRailItemBaselineLayout(
     icon: @Composable () -> Unit,
     label: @Composable (() -> Unit)?,
-    /*@FloatRange(from = 0.0, to = 1.0)*/
+    @FloatRange(from = 0.0, to = 1.0)
     iconPositionAnimationProgress: Float
 ) {
     Layout(
@@ -335,7 +336,7 @@
     labelPlaceable: Placeable,
     iconPlaceable: Placeable,
     constraints: Constraints,
-    /*@FloatRange(from = 0.0, to = 1.0)*/
+    @FloatRange(from = 0.0, to = 1.0)
     iconPositionAnimationProgress: Float
 ): MeasureResult {
     val baseline = labelPlaceable[LastBaseline]
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ProgressIndicator.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ProgressIndicator.kt
index 3139c65..9a30437 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ProgressIndicator.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ProgressIndicator.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.material
 
+import androidx.annotation.FloatRange
 import androidx.compose.animation.core.CubicBezierEasing
 import androidx.compose.animation.core.LinearEasing
 import androidx.compose.animation.core.Spring
@@ -71,7 +72,7 @@
  */
 @Composable
 fun LinearProgressIndicator(
-    /*@FloatRange(from = 0.0, to = 1.0)*/
+    @FloatRange(from = 0.0, to = 1.0)
     progress: Float,
     modifier: Modifier = Modifier,
     color: Color = MaterialTheme.colors.primary,
@@ -284,7 +285,7 @@
  */
 @Composable
 fun CircularProgressIndicator(
-    /*@FloatRange(from = 0.0, to = 1.0)*/
+    @FloatRange(from = 0.0, to = 1.0)
     progress: Float,
     modifier: Modifier = Modifier,
     color: Color = MaterialTheme.colors.primary,
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt
index 4c3a706..aa3bd62 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Slider.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.material
 
+import androidx.annotation.IntRange
 import androidx.compose.animation.core.Animatable
 import androidx.compose.animation.core.TweenSpec
 import androidx.compose.foundation.Canvas
@@ -149,7 +150,7 @@
     modifier: Modifier = Modifier,
     enabled: Boolean = true,
     valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
-    /*@IntRange(from = 0)*/
+    @IntRange(from = 0)
     steps: Int = 0,
     onValueChangeFinished: (() -> Unit)? = null,
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
@@ -294,7 +295,7 @@
     modifier: Modifier = Modifier,
     enabled: Boolean = true,
     valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
-    /*@IntRange(from = 0)*/
+    @IntRange(from = 0)
     steps: Int = 0,
     onValueChangeFinished: (() -> Unit)? = null,
     colors: SliderColors = SliderDefaults.colors()
diff --git a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/DatePickerBenchmark.kt b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/DatePickerBenchmark.kt
index d047879..e5e5664 100644
--- a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/DatePickerBenchmark.kt
+++ b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/DatePickerBenchmark.kt
@@ -53,50 +53,32 @@
 
     @Test
     fun datePicker_measure() {
-        benchmarkRule.benchmarkFirstMeasure(
-            caseFactory = datePickerTestCaseFactory,
-            allowPendingChanges = true
-        )
+        benchmarkRule.benchmarkMeasureUntilStable(datePickerTestCaseFactory)
     }
 
     @Test
     fun dateInput_measure() {
-        benchmarkRule.benchmarkFirstMeasure(
-            caseFactory = dateInputTestCaseFactory,
-            allowPendingChanges = true
-        )
+        benchmarkRule.benchmarkMeasureUntilStable(dateInputTestCaseFactory)
     }
 
     @Test
     fun datePicker_layout() {
-        benchmarkRule.benchmarkFirstLayout(
-            caseFactory = datePickerTestCaseFactory,
-            allowPendingChanges = true
-        )
+        benchmarkRule.benchmarkLayoutUntilStable(datePickerTestCaseFactory)
     }
 
     @Test
     fun dateInput_layout() {
-        benchmarkRule.benchmarkFirstLayout(
-            caseFactory = dateInputTestCaseFactory,
-            allowPendingChanges = true
-        )
+        benchmarkRule.benchmarkLayoutUntilStable(dateInputTestCaseFactory)
     }
 
     @Test
     fun datePicker_draw() {
-        benchmarkRule.benchmarkFirstDraw(
-            caseFactory = datePickerTestCaseFactory,
-            allowPendingChanges = true
-        )
+        benchmarkRule.benchmarkDrawUntilStable(datePickerTestCaseFactory)
     }
 
     @Test
     fun dateInput_draw() {
-        benchmarkRule.benchmarkFirstDraw(
-            caseFactory = dateInputTestCaseFactory,
-            allowPendingChanges = true
-        )
+        benchmarkRule.benchmarkDrawUntilStable(dateInputTestCaseFactory)
     }
 }
 
diff --git a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/DateRangePickerBenchmark.kt b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/DateRangePickerBenchmark.kt
index d8a46d8..b9f97a3 100644
--- a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/DateRangePickerBenchmark.kt
+++ b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/DateRangePickerBenchmark.kt
@@ -61,10 +61,7 @@
 
     @Test
     fun dateRangeInput_measure() {
-        benchmarkRule.benchmarkFirstMeasure(
-            caseFactory = dateRangeInputTestCaseFactory,
-            allowPendingChanges = true
-        )
+        benchmarkRule.benchmarkMeasureUntilStable(dateRangeInputTestCaseFactory)
     }
 
     @Test
@@ -74,10 +71,7 @@
 
     @Test
     fun dateRangeInput_layout() {
-        benchmarkRule.benchmarkFirstLayout(
-            caseFactory = dateRangeInputTestCaseFactory,
-            allowPendingChanges = true
-        )
+        benchmarkRule.benchmarkLayoutUntilStable(dateRangeInputTestCaseFactory)
     }
 
     @Test
@@ -87,10 +81,7 @@
 
     @Test
     fun dateRangeInput_draw() {
-        benchmarkRule.benchmarkFirstDraw(
-            caseFactory = dateRangeInputTestCaseFactory,
-            allowPendingChanges = true
-        )
+        benchmarkRule.benchmarkDrawUntilStable(dateRangeInputTestCaseFactory)
     }
 }
 
diff --git a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/ExposedDropdownMenuBenchmark.kt b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/ExposedDropdownMenuBenchmark.kt
new file mode 100644
index 0000000..fc6d8bd
--- /dev/null
+++ b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/ExposedDropdownMenuBenchmark.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.benchmark
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.size
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExposedDropdownMenuBox
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.testutils.LayeredComposeTestCase
+import androidx.compose.testutils.ToggleableTestCase
+import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
+import androidx.compose.testutils.benchmark.benchmarkFirstCompose
+import androidx.compose.testutils.benchmark.toggleStateBenchmarkComposeMeasureLayout
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.test.filters.LargeTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@LargeTest
+@RunWith(Parameterized::class)
+class ExposedDropdownMenuBenchmark(private val expanded: Boolean) {
+    @get:Rule
+    val benchmarkRule = ComposeBenchmarkRule()
+
+    @Test
+    fun edm_first_compose() {
+        benchmarkRule.benchmarkFirstCompose { ExposedDropdownMenuTestCase(expanded) }
+    }
+
+    @Test
+    fun edm_measure() {
+        benchmarkRule.benchmarkMeasureUntilStable({
+            ExposedDropdownMenuTestCase(expanded)
+        })
+    }
+
+    @Test
+    fun edm_layout() {
+        benchmarkRule.benchmarkLayoutUntilStable({
+            ExposedDropdownMenuTestCase(expanded)
+        })
+    }
+
+    @Test
+    fun edm_draw() {
+        benchmarkRule.benchmarkDrawUntilStable({
+            ExposedDropdownMenuTestCase(expanded)
+        })
+    }
+
+    @Test
+    fun edm_textFieldAnchor_repositioned() {
+        benchmarkRule.toggleStateBenchmarkComposeMeasureLayout({
+            ExposedDropdownMenuTestCase(expanded)
+        })
+    }
+
+    companion object {
+        @Parameterized.Parameters(name = "expanded = {0}")
+        @JvmStatic
+        fun parameters() = arrayOf(true, false)
+    }
+}
+
+internal class ExposedDropdownMenuTestCase(
+    private val expanded: Boolean
+) : LayeredComposeTestCase(), ToggleableTestCase {
+    private lateinit var state: MutableState<Dp>
+
+    @OptIn(ExperimentalMaterial3Api::class)
+    @Composable
+    override fun MeasuredContent() {
+        val spacerHeight = remember { mutableStateOf(100.dp) }
+        state = spacerHeight
+
+        Column(Modifier.fillMaxSize()) {
+            Spacer(Modifier.height(state.value))
+
+            ExposedDropdownMenuBox(
+                expanded = expanded,
+                onExpandedChange = {},
+            ) {
+                Spacer(modifier = Modifier.size(100.dp).menuAnchor())
+                ExposedDropdownMenu(
+                    expanded = expanded,
+                    onDismissRequest = {},
+                    content = { Spacer(modifier = Modifier.height(50.dp).fillMaxWidth()) },
+                )
+            }
+        }
+    }
+
+    @Composable
+    override fun ContentWrappers(content: @Composable () -> Unit) {
+        MaterialTheme {
+            content()
+        }
+    }
+
+    override fun toggleState() {
+        state.value = if (state.value == 100.dp) 200.dp else 100.dp
+    }
+}
diff --git a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/MaterialBenchmarkExtensions.kt b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/MaterialBenchmarkExtensions.kt
index 5eaa7df..5a50c15 100644
--- a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/MaterialBenchmarkExtensions.kt
+++ b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/MaterialBenchmarkExtensions.kt
@@ -28,101 +28,126 @@
 import androidx.compose.testutils.doFramesUntilNoChangesPending
 import org.junit.Assert
 
-/**
- * Measures the time of the first draw right after the given test case is added to an already
- * existing hierarchy.
- *
- * @param caseFactory a factory for [LayeredComposeTestCase]
- * @param allowPendingChanges in case `true`, the benchmark will allow pending changes after the
- * first draw. Otherwise, an [assertNoPendingChanges] will be called.
- */
-internal fun ComposeBenchmarkRule.benchmarkFirstDraw(
+internal fun ComposeBenchmarkRule.benchmarkDrawUntilStable(
     caseFactory: () -> LayeredComposeTestCase,
-    allowPendingChanges: Boolean
+    maxSteps: Int = 10,
 ) {
     runBenchmarkFor(LayeredCaseAdapter.of(caseFactory)) {
         measureRepeated {
             runWithTimingDisabled {
-                doFramesUntilNoChangesPending()
+                doFramesUntilNoChangesPending(maxSteps)
                 // Add the content to benchmark
                 getTestCase().addMeasuredContent()
-                recomposeUntilNoChangesPending()
-                requestLayout()
+            }
+
+            var loopCount = 0
+            while (hasPendingChanges()) {
+                loopCount++
+                runWithTimingDisabled {
+                    recomposeUntilNoChangesPending(maxSteps)
+                    requestLayout()
+                    measure()
+                    layout()
+                    drawPrepare()
+                }
+
+                draw()
+
+                runWithTimingDisabled {
+                    drawFinish()
+                }
+            }
+
+            if (loopCount == 1) {
+                throw AssertionError("Use benchmarkFirstDraw instead")
+            }
+
+            runWithTimingDisabled {
+                assertNoPendingChanges()
+                disposeContent()
+            }
+        }
+    }
+}
+
+internal fun ComposeBenchmarkRule.benchmarkMeasureUntilStable(
+    caseFactory: () -> LayeredComposeTestCase,
+    maxSteps: Int = MaxSteps,
+) {
+    runBenchmarkFor(LayeredCaseAdapter.of(caseFactory)) {
+        measureRepeated {
+            runWithTimingDisabled {
+                doFramesUntilNoChangesPending(maxSteps)
+                // Add the content to benchmark
+                getTestCase().addMeasuredContent()
+            }
+
+            var loopCount = 0
+            while (hasPendingChanges()) {
+                loopCount++
+                runWithTimingDisabled {
+                    recomposeUntilNoChangesPending(maxSteps)
+                    requestLayout()
+                }
+
                 measure()
+
+                runWithTimingDisabled {
+                    layout()
+                    drawPrepare()
+                    draw()
+                    drawFinish()
+                }
+            }
+
+            if (loopCount == 1) {
+                throw AssertionError("Use benchmarkFirstMeasure instead")
+            }
+
+            runWithTimingDisabled {
+                assertNoPendingChanges()
+                disposeContent()
+            }
+        }
+    }
+}
+
+internal fun ComposeBenchmarkRule.benchmarkLayoutUntilStable(
+    caseFactory: () -> LayeredComposeTestCase,
+    maxSteps: Int = MaxSteps,
+) {
+    runBenchmarkFor(LayeredCaseAdapter.of(caseFactory)) {
+        measureRepeated {
+            runWithTimingDisabled {
+                doFramesUntilNoChangesPending(maxSteps)
+                // Add the content to benchmark
+                getTestCase().addMeasuredContent()
+            }
+
+            var loopCount = 0
+            while (hasPendingChanges()) {
+                loopCount++
+                runWithTimingDisabled {
+                    recomposeUntilNoChangesPending(maxSteps)
+                    requestLayout()
+                    measure()
+                }
+
                 layout()
-                drawPrepare()
+
+                runWithTimingDisabled {
+                    drawPrepare()
+                    draw()
+                    drawFinish()
+                }
             }
 
-            draw()
-
-            runWithTimingDisabled {
-                drawFinish()
-                if (!allowPendingChanges) assertNoPendingChanges()
-                disposeContent()
-            }
-        }
-    }
-}
-
-/**
- * Measures the time of the first measure right after the given test case is added to an already
- * existing hierarchy.
- *
- * @param caseFactory a factory for [LayeredComposeTestCase]
- * @param allowPendingChanges in case `true`, the benchmark will allow pending changes after the
- * first measure. Otherwise, an [assertNoPendingChanges] will be called.
- */
-internal fun ComposeBenchmarkRule.benchmarkFirstMeasure(
-    caseFactory: () -> LayeredComposeTestCase,
-    allowPendingChanges: Boolean
-) {
-    runBenchmarkFor(LayeredCaseAdapter.of(caseFactory)) {
-        measureRepeated {
-            runWithTimingDisabled {
-                doFramesUntilNoChangesPending()
-                // Add the content to benchmark
-                getTestCase().addMeasuredContent()
-                recomposeUntilNoChangesPending()
-                requestLayout()
+            if (loopCount == 1) {
+                throw AssertionError("Use benchmarkFirstLayout instead")
             }
 
-            measure()
-
             runWithTimingDisabled {
-                if (!allowPendingChanges) assertNoPendingChanges()
-                disposeContent()
-            }
-        }
-    }
-}
-
-/**
- * Measures the time of the first layout right after the given test case is added to an already
- * existing hierarchy.
- *
- * @param caseFactory a factory for [LayeredComposeTestCase]
- * @param allowPendingChanges in case `true`, the benchmark will allow pending changes after the
- * first layout. Otherwise, an [assertNoPendingChanges] will be called.
- */
-internal fun ComposeBenchmarkRule.benchmarkFirstLayout(
-    caseFactory: () -> LayeredComposeTestCase,
-    allowPendingChanges: Boolean
-) {
-    runBenchmarkFor(LayeredCaseAdapter.of(caseFactory)) {
-        measureRepeated {
-            runWithTimingDisabled {
-                doFramesUntilNoChangesPending()
-                // Add the content to benchmark
-                getTestCase().addMeasuredContent()
-                recomposeUntilNoChangesPending()
-                requestLayout()
-                measure()
-            }
-
-            layout()
-
-            runWithTimingDisabled {
-                if (!allowPendingChanges) assertNoPendingChanges()
+                assertNoPendingChanges()
                 disposeContent()
             }
         }
@@ -154,4 +179,4 @@
     }
 }
 
-private const val MaxAmountOfRecompositions = 10
+private const val MaxSteps = 10
diff --git a/compose/material3/material3-adaptive/api/current.txt b/compose/material3/material3-adaptive/api/current.txt
index 08260f2..bf5b32a 100644
--- a/compose/material3/material3-adaptive/api/current.txt
+++ b/compose/material3/material3-adaptive/api/current.txt
@@ -11,19 +11,21 @@
     property public final androidx.compose.material3.adaptive.AdaptStrategy Hide;
   }
 
-  @androidx.compose.runtime.Immutable public final class AdaptiveLayoutDirective {
-    ctor public AdaptiveLayoutDirective(int maxHorizontalPartitions, androidx.compose.material3.adaptive.GutterSizes gutterSizes, optional int maxVerticalPartitions);
+  @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Immutable public final class AdaptiveLayoutDirective {
+    ctor public AdaptiveLayoutDirective(int maxHorizontalPartitions, androidx.compose.material3.adaptive.GutterSizes gutterSizes, optional int maxVerticalPartitions, optional java.util.List<androidx.compose.ui.geometry.Rect> excludedBounds);
+    method public java.util.List<androidx.compose.ui.geometry.Rect> getExcludedBounds();
     method public androidx.compose.material3.adaptive.GutterSizes getGutterSizes();
     method public int getMaxHorizontalPartitions();
     method public int getMaxVerticalPartitions();
+    property public final java.util.List<androidx.compose.ui.geometry.Rect> excludedBounds;
     property public final androidx.compose.material3.adaptive.GutterSizes gutterSizes;
     property public final int maxHorizontalPartitions;
     property public final int maxVerticalPartitions;
   }
 
   public final class AdaptiveLayoutDirectiveKt {
-    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static androidx.compose.material3.adaptive.AdaptiveLayoutDirective calculateDenseAdaptiveLayoutDirective(androidx.compose.material3.adaptive.WindowAdaptiveInfo windowAdaptiveInfo);
-    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static androidx.compose.material3.adaptive.AdaptiveLayoutDirective calculateStandardAdaptiveLayoutDirective(androidx.compose.material3.adaptive.WindowAdaptiveInfo windowAdaptiveInfo);
+    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static androidx.compose.material3.adaptive.AdaptiveLayoutDirective calculateDenseAdaptiveLayoutDirective(androidx.compose.material3.adaptive.WindowAdaptiveInfo windowAdaptiveInfo, optional int hingePolicy);
+    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static androidx.compose.material3.adaptive.AdaptiveLayoutDirective calculateStandardAdaptiveLayoutDirective(androidx.compose.material3.adaptive.WindowAdaptiveInfo windowAdaptiveInfo, optional int hingePolicy);
   }
 
   public final class AndroidPosture_androidKt {
@@ -39,7 +41,7 @@
   @SuppressCompatibility @kotlin.RequiresOptIn(message="This material3-adaptive API is experimental and is likely to change or to be" + "removed in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalMaterial3AdaptiveApi {
   }
 
-  @androidx.compose.runtime.Immutable public final class GutterSizes {
+  @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Immutable public final class GutterSizes {
     ctor public GutterSizes(float outerVertical, float innerVertical, optional float outerHorizontal, optional float innerHorizontal);
     method public float getInnerHorizontal();
     method public float getInnerVertical();
@@ -51,6 +53,49 @@
     property public final float outerVertical;
   }
 
+  @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class HingePolicy {
+    field public static final androidx.compose.material3.adaptive.HingePolicy.Companion Companion;
+  }
+
+  public static final class HingePolicy.Companion {
+    method public int getAlwaysAvoid();
+    method public int getAvoidOccluding();
+    method public int getAvoidSeparating();
+    method public int getNeverAvoid();
+    property public final int AlwaysAvoid;
+    property public final int AvoidOccluding;
+    property public final int AvoidSeparating;
+    property public final int NeverAvoid;
+  }
+
+  @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class ListDetailPaneScaffoldDefaults {
+    method public androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies(optional androidx.compose.material3.adaptive.AdaptStrategy detailPaneAdaptStrategy, optional androidx.compose.material3.adaptive.AdaptStrategy listPaneAdaptStrategy, optional androidx.compose.material3.adaptive.AdaptStrategy extraPaneAdaptStrategy);
+    field public static final androidx.compose.material3.adaptive.ListDetailPaneScaffoldDefaults INSTANCE;
+  }
+
+  public final class ListDetailPaneScaffoldKt {
+    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ListDetailPaneScaffold(androidx.compose.material3.adaptive.ListDetailPaneScaffoldState layoutState, kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit> listPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit>? extraPane, kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit> detailPane);
+    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.ListDetailPaneScaffoldState rememberListDetailPaneScaffoldState(optional androidx.compose.material3.adaptive.AdaptiveLayoutDirective layoutDirectives, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole initialFocus);
+  }
+
+  @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public enum ListDetailPaneScaffoldRole {
+    method public static androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole[] values();
+    enum_constant public static final androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole Detail;
+    enum_constant public static final androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole Extra;
+    enum_constant public static final androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole List;
+  }
+
+  @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Stable public interface ListDetailPaneScaffoldState {
+    method public boolean canNavigateBack(optional boolean layoutValueMustChange);
+    method public androidx.compose.material3.adaptive.AdaptiveLayoutDirective getLayoutDirective();
+    method public androidx.compose.material3.adaptive.ThreePaneScaffoldValue getLayoutValue();
+    method public boolean navigateBack(optional boolean popUntilLayoutValueChange);
+    method public void navigateTo(androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole pane);
+    property public abstract androidx.compose.material3.adaptive.AdaptiveLayoutDirective layoutDirective;
+    property public abstract androidx.compose.material3.adaptive.ThreePaneScaffoldValue layoutValue;
+  }
+
   @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @kotlin.jvm.JvmInline public final value class PaneAdaptedValue {
     field public static final androidx.compose.material3.adaptive.PaneAdaptedValue.Companion Companion;
   }
@@ -67,13 +112,19 @@
   }
 
   @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Immutable public final class Posture {
-    ctor public Posture(optional boolean hasVerticalHinge, optional boolean isTabletop, optional boolean hasSeparatingHinge);
+    ctor public Posture(optional boolean hasVerticalHinge, optional boolean isTabletop, optional boolean hasSeparatingHinge, optional java.util.List<androidx.compose.ui.geometry.Rect> separatingHingeBounds, optional java.util.List<androidx.compose.ui.geometry.Rect> occludingHingeBounds, optional java.util.List<androidx.compose.ui.geometry.Rect> allHingeBounds);
+    method public java.util.List<androidx.compose.ui.geometry.Rect> getAllHingeBounds();
     method public boolean getHasSeparatingHinge();
     method public boolean getHasVerticalHinge();
+    method public java.util.List<androidx.compose.ui.geometry.Rect> getOccludingHingeBounds();
+    method public java.util.List<androidx.compose.ui.geometry.Rect> getSeparatingHingeBounds();
     method public boolean isTabletop();
+    property public final java.util.List<androidx.compose.ui.geometry.Rect> allHingeBounds;
     property public final boolean hasSeparatingHinge;
     property public final boolean hasVerticalHinge;
     property public final boolean isTabletop;
+    property public final java.util.List<androidx.compose.ui.geometry.Rect> occludingHingeBounds;
+    property public final java.util.List<androidx.compose.ui.geometry.Rect> separatingHingeBounds;
   }
 
   @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class ThreePaneScaffoldAdaptStrategies {
@@ -98,6 +149,10 @@
     field public static final androidx.compose.material3.adaptive.ThreePaneScaffoldDefaults INSTANCE;
   }
 
+  public final class ThreePaneScaffoldKt {
+    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ThreePaneScaffold(androidx.compose.ui.Modifier modifier, androidx.compose.material3.adaptive.AdaptiveLayoutDirective layoutDirective, androidx.compose.material3.adaptive.ThreePaneScaffoldValue scaffoldValue, androidx.compose.material3.adaptive.ThreePaneScaffoldArrangement arrangement, kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit> secondaryPane, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit>? tertiaryPane, kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit> primaryPane);
+  }
+
   @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public enum ThreePaneScaffoldRole {
     method public static androidx.compose.material3.adaptive.ThreePaneScaffoldRole valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
     method public static androidx.compose.material3.adaptive.ThreePaneScaffoldRole[] values();
@@ -106,6 +161,9 @@
     enum_constant public static final androidx.compose.material3.adaptive.ThreePaneScaffoldRole Tertiary;
   }
 
+  public interface ThreePaneScaffoldScope extends androidx.compose.material3.adaptive.PaneScaffoldScope {
+  }
+
   @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Immutable public final class ThreePaneScaffoldValue {
     ctor public ThreePaneScaffoldValue(String primary, String secondary, String tertiary);
     method public operator String get(androidx.compose.material3.adaptive.ThreePaneScaffoldRole role);
diff --git a/compose/material3/material3-adaptive/api/restricted_current.txt b/compose/material3/material3-adaptive/api/restricted_current.txt
index 08260f2..bf5b32a 100644
--- a/compose/material3/material3-adaptive/api/restricted_current.txt
+++ b/compose/material3/material3-adaptive/api/restricted_current.txt
@@ -11,19 +11,21 @@
     property public final androidx.compose.material3.adaptive.AdaptStrategy Hide;
   }
 
-  @androidx.compose.runtime.Immutable public final class AdaptiveLayoutDirective {
-    ctor public AdaptiveLayoutDirective(int maxHorizontalPartitions, androidx.compose.material3.adaptive.GutterSizes gutterSizes, optional int maxVerticalPartitions);
+  @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Immutable public final class AdaptiveLayoutDirective {
+    ctor public AdaptiveLayoutDirective(int maxHorizontalPartitions, androidx.compose.material3.adaptive.GutterSizes gutterSizes, optional int maxVerticalPartitions, optional java.util.List<androidx.compose.ui.geometry.Rect> excludedBounds);
+    method public java.util.List<androidx.compose.ui.geometry.Rect> getExcludedBounds();
     method public androidx.compose.material3.adaptive.GutterSizes getGutterSizes();
     method public int getMaxHorizontalPartitions();
     method public int getMaxVerticalPartitions();
+    property public final java.util.List<androidx.compose.ui.geometry.Rect> excludedBounds;
     property public final androidx.compose.material3.adaptive.GutterSizes gutterSizes;
     property public final int maxHorizontalPartitions;
     property public final int maxVerticalPartitions;
   }
 
   public final class AdaptiveLayoutDirectiveKt {
-    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static androidx.compose.material3.adaptive.AdaptiveLayoutDirective calculateDenseAdaptiveLayoutDirective(androidx.compose.material3.adaptive.WindowAdaptiveInfo windowAdaptiveInfo);
-    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static androidx.compose.material3.adaptive.AdaptiveLayoutDirective calculateStandardAdaptiveLayoutDirective(androidx.compose.material3.adaptive.WindowAdaptiveInfo windowAdaptiveInfo);
+    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static androidx.compose.material3.adaptive.AdaptiveLayoutDirective calculateDenseAdaptiveLayoutDirective(androidx.compose.material3.adaptive.WindowAdaptiveInfo windowAdaptiveInfo, optional int hingePolicy);
+    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static androidx.compose.material3.adaptive.AdaptiveLayoutDirective calculateStandardAdaptiveLayoutDirective(androidx.compose.material3.adaptive.WindowAdaptiveInfo windowAdaptiveInfo, optional int hingePolicy);
   }
 
   public final class AndroidPosture_androidKt {
@@ -39,7 +41,7 @@
   @SuppressCompatibility @kotlin.RequiresOptIn(message="This material3-adaptive API is experimental and is likely to change or to be" + "removed in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalMaterial3AdaptiveApi {
   }
 
-  @androidx.compose.runtime.Immutable public final class GutterSizes {
+  @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Immutable public final class GutterSizes {
     ctor public GutterSizes(float outerVertical, float innerVertical, optional float outerHorizontal, optional float innerHorizontal);
     method public float getInnerHorizontal();
     method public float getInnerVertical();
@@ -51,6 +53,49 @@
     property public final float outerVertical;
   }
 
+  @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class HingePolicy {
+    field public static final androidx.compose.material3.adaptive.HingePolicy.Companion Companion;
+  }
+
+  public static final class HingePolicy.Companion {
+    method public int getAlwaysAvoid();
+    method public int getAvoidOccluding();
+    method public int getAvoidSeparating();
+    method public int getNeverAvoid();
+    property public final int AlwaysAvoid;
+    property public final int AvoidOccluding;
+    property public final int AvoidSeparating;
+    property public final int NeverAvoid;
+  }
+
+  @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class ListDetailPaneScaffoldDefaults {
+    method public androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies(optional androidx.compose.material3.adaptive.AdaptStrategy detailPaneAdaptStrategy, optional androidx.compose.material3.adaptive.AdaptStrategy listPaneAdaptStrategy, optional androidx.compose.material3.adaptive.AdaptStrategy extraPaneAdaptStrategy);
+    field public static final androidx.compose.material3.adaptive.ListDetailPaneScaffoldDefaults INSTANCE;
+  }
+
+  public final class ListDetailPaneScaffoldKt {
+    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ListDetailPaneScaffold(androidx.compose.material3.adaptive.ListDetailPaneScaffoldState layoutState, kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit> listPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit>? extraPane, kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit> detailPane);
+    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.ListDetailPaneScaffoldState rememberListDetailPaneScaffoldState(optional androidx.compose.material3.adaptive.AdaptiveLayoutDirective layoutDirectives, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole initialFocus);
+  }
+
+  @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public enum ListDetailPaneScaffoldRole {
+    method public static androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole[] values();
+    enum_constant public static final androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole Detail;
+    enum_constant public static final androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole Extra;
+    enum_constant public static final androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole List;
+  }
+
+  @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Stable public interface ListDetailPaneScaffoldState {
+    method public boolean canNavigateBack(optional boolean layoutValueMustChange);
+    method public androidx.compose.material3.adaptive.AdaptiveLayoutDirective getLayoutDirective();
+    method public androidx.compose.material3.adaptive.ThreePaneScaffoldValue getLayoutValue();
+    method public boolean navigateBack(optional boolean popUntilLayoutValueChange);
+    method public void navigateTo(androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole pane);
+    property public abstract androidx.compose.material3.adaptive.AdaptiveLayoutDirective layoutDirective;
+    property public abstract androidx.compose.material3.adaptive.ThreePaneScaffoldValue layoutValue;
+  }
+
   @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @kotlin.jvm.JvmInline public final value class PaneAdaptedValue {
     field public static final androidx.compose.material3.adaptive.PaneAdaptedValue.Companion Companion;
   }
@@ -67,13 +112,19 @@
   }
 
   @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Immutable public final class Posture {
-    ctor public Posture(optional boolean hasVerticalHinge, optional boolean isTabletop, optional boolean hasSeparatingHinge);
+    ctor public Posture(optional boolean hasVerticalHinge, optional boolean isTabletop, optional boolean hasSeparatingHinge, optional java.util.List<androidx.compose.ui.geometry.Rect> separatingHingeBounds, optional java.util.List<androidx.compose.ui.geometry.Rect> occludingHingeBounds, optional java.util.List<androidx.compose.ui.geometry.Rect> allHingeBounds);
+    method public java.util.List<androidx.compose.ui.geometry.Rect> getAllHingeBounds();
     method public boolean getHasSeparatingHinge();
     method public boolean getHasVerticalHinge();
+    method public java.util.List<androidx.compose.ui.geometry.Rect> getOccludingHingeBounds();
+    method public java.util.List<androidx.compose.ui.geometry.Rect> getSeparatingHingeBounds();
     method public boolean isTabletop();
+    property public final java.util.List<androidx.compose.ui.geometry.Rect> allHingeBounds;
     property public final boolean hasSeparatingHinge;
     property public final boolean hasVerticalHinge;
     property public final boolean isTabletop;
+    property public final java.util.List<androidx.compose.ui.geometry.Rect> occludingHingeBounds;
+    property public final java.util.List<androidx.compose.ui.geometry.Rect> separatingHingeBounds;
   }
 
   @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class ThreePaneScaffoldAdaptStrategies {
@@ -98,6 +149,10 @@
     field public static final androidx.compose.material3.adaptive.ThreePaneScaffoldDefaults INSTANCE;
   }
 
+  public final class ThreePaneScaffoldKt {
+    method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ThreePaneScaffold(androidx.compose.ui.Modifier modifier, androidx.compose.material3.adaptive.AdaptiveLayoutDirective layoutDirective, androidx.compose.material3.adaptive.ThreePaneScaffoldValue scaffoldValue, androidx.compose.material3.adaptive.ThreePaneScaffoldArrangement arrangement, kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit> secondaryPane, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit>? tertiaryPane, kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit> primaryPane);
+  }
+
   @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public enum ThreePaneScaffoldRole {
     method public static androidx.compose.material3.adaptive.ThreePaneScaffoldRole valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
     method public static androidx.compose.material3.adaptive.ThreePaneScaffoldRole[] values();
@@ -106,6 +161,9 @@
     enum_constant public static final androidx.compose.material3.adaptive.ThreePaneScaffoldRole Tertiary;
   }
 
+  public interface ThreePaneScaffoldScope extends androidx.compose.material3.adaptive.PaneScaffoldScope {
+  }
+
   @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Immutable public final class ThreePaneScaffoldValue {
     ctor public ThreePaneScaffoldValue(String primary, String secondary, String tertiary);
     method public operator String get(androidx.compose.material3.adaptive.ThreePaneScaffoldRole role);
diff --git a/compose/material3/material3-adaptive/build.gradle b/compose/material3/material3-adaptive/build.gradle
index 52bb592..5c97bb2 100644
--- a/compose/material3/material3-adaptive/build.gradle
+++ b/compose/material3/material3-adaptive/build.gradle
@@ -42,6 +42,7 @@
                 implementation(project(":compose:foundation:foundation-layout"))
                 implementation(project(":compose:material3:material3"))
                 implementation(project(":compose:material3:material3-window-size-class"))
+                implementation(project(":compose:ui:ui-util"))
             }
         }
 
@@ -107,7 +108,14 @@
     name = "Material Adaptive"
     mavenVersion = LibraryVersions.COMPOSE_MATERIAL3_ADAPTIVE
     type = LibraryType.PUBLISHED_LIBRARY
-    publish = Publish.NONE
+    publish = Publish.SNAPSHOT_ONLY
     inceptionYear = "2023"
     description = "Compose Material Design Adaptive Library"
 }
+
+// Screenshot tests related setup
+android {
+    sourceSets.androidTest.assets.srcDirs +=
+            project.rootDir.absolutePath + "/../../golden/compose/material3/material3-adaptive"
+    namespace "androidx.compose.material3.adaptive"
+}
diff --git a/compose/material3/material3-adaptive/src/androidAndroidTest/kotlin/androidx/compose/material3/adaptive/CalculatePostureTest.kt b/compose/material3/material3-adaptive/src/androidAndroidTest/kotlin/androidx/compose/material3/adaptive/CalculatePostureTest.kt
index 63a9792..a0e3f4d 100644
--- a/compose/material3/material3-adaptive/src/androidAndroidTest/kotlin/androidx/compose/material3/adaptive/CalculatePostureTest.kt
+++ b/compose/material3/material3-adaptive/src/androidAndroidTest/kotlin/androidx/compose/material3/adaptive/CalculatePostureTest.kt
@@ -17,6 +17,7 @@
 package androidx.compose.material3.adaptive
 
 import android.graphics.Rect
+import androidx.compose.ui.graphics.toComposeRect
 import androidx.window.layout.FoldingFeature
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
@@ -161,6 +162,97 @@
 
         assertThat(posture.isTabletop).isFalse()
     }
+
+    @Test
+    fun test_calculatePosture_separatingBounds() {
+        val mockHingeBounds1 = Rect(1, 1, 2, 2)
+        val mockHingeBounds2 = Rect(2, 2, 3, 3)
+        val mockHingeBounds3 = Rect(3, 3, 4, 4)
+        val posture = calculatePosture(
+            listOf(
+                MockFoldingFeature(
+                    isSeparating = false,
+                    occlusionType = FoldingFeature.OcclusionType.FULL,
+                    bounds = mockHingeBounds1
+                ),
+                MockFoldingFeature(
+                    isSeparating = true,
+                    occlusionType = FoldingFeature.OcclusionType.FULL,
+                    bounds = mockHingeBounds2
+                ),
+                MockFoldingFeature(
+                    isSeparating = true,
+                    occlusionType = FoldingFeature.OcclusionType.NONE,
+                    bounds = mockHingeBounds3
+                ),
+            )
+        )
+
+        assertThat(posture.separatingHingeBounds.size).isEqualTo(2)
+        assertThat(posture.separatingHingeBounds[0]).isEqualTo(mockHingeBounds2.toComposeRect())
+        assertThat(posture.separatingHingeBounds[1]).isEqualTo(mockHingeBounds3.toComposeRect())
+    }
+
+    @Test
+    fun test_calculatePosture_occludingBounds() {
+        val mockHingeBounds1 = Rect(1, 1, 2, 2)
+        val mockHingeBounds2 = Rect(2, 2, 3, 3)
+        val mockHingeBounds3 = Rect(3, 3, 4, 4)
+        val posture = calculatePosture(
+            listOf(
+                MockFoldingFeature(
+                    isSeparating = false,
+                    occlusionType = FoldingFeature.OcclusionType.FULL,
+                    bounds = mockHingeBounds1
+                ),
+                MockFoldingFeature(
+                    isSeparating = true,
+                    occlusionType = FoldingFeature.OcclusionType.FULL,
+                    bounds = mockHingeBounds2
+                ),
+                MockFoldingFeature(
+                    isSeparating = true,
+                    occlusionType = FoldingFeature.OcclusionType.NONE,
+                    bounds = mockHingeBounds3
+                ),
+            )
+        )
+
+        assertThat(posture.occludingHingeBounds.size).isEqualTo(2)
+        assertThat(posture.occludingHingeBounds[0]).isEqualTo(mockHingeBounds1.toComposeRect())
+        assertThat(posture.occludingHingeBounds[1]).isEqualTo(mockHingeBounds2.toComposeRect())
+    }
+
+    @Test
+    fun test_calculatePosture_allBounds() {
+        val mockHingeBounds1 = Rect(1, 1, 2, 2)
+        val mockHingeBounds2 = Rect(2, 2, 3, 3)
+        val mockHingeBounds3 = Rect(3, 3, 4, 4)
+        val posture = calculatePosture(
+            listOf(
+                MockFoldingFeature(
+                    isSeparating = false,
+                    occlusionType = FoldingFeature.OcclusionType.FULL,
+                    bounds = mockHingeBounds1
+                ),
+                MockFoldingFeature(
+                    isSeparating = true,
+                    occlusionType = FoldingFeature.OcclusionType.FULL,
+                    bounds = mockHingeBounds2
+                ),
+                MockFoldingFeature(
+                    isSeparating = true,
+                    occlusionType = FoldingFeature.OcclusionType.NONE,
+                    bounds = mockHingeBounds3
+                ),
+            )
+        )
+
+        assertThat(posture.allHingeBounds.size).isEqualTo(3)
+        assertThat(posture.allHingeBounds[0]).isEqualTo(mockHingeBounds1.toComposeRect())
+        assertThat(posture.allHingeBounds[1]).isEqualTo(mockHingeBounds2.toComposeRect())
+        assertThat(posture.allHingeBounds[2]).isEqualTo(mockHingeBounds3.toComposeRect())
+    }
 }
 
 internal class MockFoldingFeature(
diff --git a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence1.java b/compose/material3/material3-adaptive/src/androidAndroidTest/kotlin/androidx/compose/material3/adaptive/GoldenCommon.kt
similarity index 75%
copy from lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence1.java
copy to compose/material3/material3-adaptive/src/androidAndroidTest/kotlin/androidx/compose/material3/adaptive/GoldenCommon.kt
index ae9c85f..a693e3a 100644
--- a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence1.java
+++ b/compose/material3/material3-adaptive/src/androidAndroidTest/kotlin/androidx/compose/material3/adaptive/GoldenCommon.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,10 +14,6 @@
  * limitations under the License.
  */
 
-package androidx.lifecycle.observers;
+package androidx.compose.material3.adaptive
 
-public class DerivedSequence1 extends Base {
-
-    public void something() {
-    }
-}
+internal const val GOLDEN_MATERIAL3_ADAPTIVE = "compose/material3/material3-adaptive"
diff --git a/compose/material3/material3-adaptive/src/androidAndroidTest/kotlin/androidx/compose/material3/adaptive/ListDetailPaneScaffoldStateTest.kt b/compose/material3/material3-adaptive/src/androidAndroidTest/kotlin/androidx/compose/material3/adaptive/ListDetailPaneScaffoldStateTest.kt
new file mode 100644
index 0000000..ad6c75f
--- /dev/null
+++ b/compose/material3/material3-adaptive/src/androidAndroidTest/kotlin/androidx/compose/material3/adaptive/ListDetailPaneScaffoldStateTest.kt
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.adaptive
+
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalMaterial3AdaptiveApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ListDetailPaneScaffoldStateTest {
+    @get:Rule
+    val composeRule = createComposeRule()
+
+    @Test
+    fun singlePaneLayout_navigateTo_makeFocusPaneExpanded() {
+        lateinit var layoutState: ListDetailPaneScaffoldState
+
+        composeRule.setContent {
+            layoutState = rememberListDetailPaneScaffoldState(
+                layoutDirectives = MockSinglePaneLayoutDirective
+            )
+        }
+
+        composeRule.runOnIdle {
+            assertThat(layoutState.layoutValue.secondary).isEqualTo(PaneAdaptedValue.Hidden)
+            layoutState.navigateTo(ListDetailPaneScaffoldRole.List)
+        }
+
+        composeRule.runOnIdle {
+            assertThat(layoutState.layoutValue.secondary).isEqualTo(PaneAdaptedValue.Expanded)
+        }
+    }
+
+    @Test
+    fun dualPaneLayout_navigateTo_keepFocusPaneExpanded() {
+        lateinit var layoutState: ListDetailPaneScaffoldState
+
+        composeRule.setContent {
+            layoutState = rememberListDetailPaneScaffoldState(
+                layoutDirectives = MockDualPaneLayoutDirective
+            )
+        }
+
+        composeRule.runOnIdle {
+            assertThat(layoutState.layoutValue.secondary).isEqualTo(PaneAdaptedValue.Expanded)
+            layoutState.navigateTo(ListDetailPaneScaffoldRole.List)
+        }
+
+        composeRule.runOnIdle {
+            assertThat(layoutState.layoutValue.secondary).isEqualTo(PaneAdaptedValue.Expanded)
+        }
+    }
+
+    @Test
+    fun singlePaneLayout_navigateBack_makeFocusPaneHidden() {
+        lateinit var layoutState: ListDetailPaneScaffoldState
+
+        composeRule.setContent {
+            layoutState = rememberListDetailPaneScaffoldState(
+                layoutDirectives = MockSinglePaneLayoutDirective
+            )
+            layoutState.navigateTo(ListDetailPaneScaffoldRole.List)
+        }
+
+        composeRule.runOnIdle {
+            assertThat(layoutState.layoutValue.secondary).isEqualTo(PaneAdaptedValue.Expanded)
+            assertThat(layoutState.canNavigateBack()).isTrue()
+            layoutState.navigateBack()
+        }
+
+        composeRule.runOnIdle {
+            assertThat(layoutState.layoutValue.secondary).isEqualTo(PaneAdaptedValue.Hidden)
+        }
+    }
+
+    @Test
+    fun dualPaneLayout_enforceLayoutValueChange_cannotNavigateBack() {
+        lateinit var layoutState: ListDetailPaneScaffoldState
+
+        composeRule.setContent {
+            layoutState = rememberListDetailPaneScaffoldState(
+                layoutDirectives = MockDualPaneLayoutDirective
+            )
+            layoutState.navigateTo(ListDetailPaneScaffoldRole.List)
+        }
+
+        composeRule.runOnIdle {
+            assertThat(layoutState.canNavigateBack()).isFalse()
+        }
+    }
+
+    @Test
+    fun dualPaneLayout_notEnforceLayoutValueChange_canNavigateBack() {
+        lateinit var layoutState: ListDetailPaneScaffoldState
+
+        composeRule.setContent {
+            layoutState = rememberListDetailPaneScaffoldState(
+                layoutDirectives = MockDualPaneLayoutDirective
+            )
+            layoutState.navigateTo(ListDetailPaneScaffoldRole.List)
+        }
+
+        composeRule.runOnIdle {
+            assertThat(layoutState.layoutValue.secondary).isEqualTo(PaneAdaptedValue.Expanded)
+            assertThat(layoutState.canNavigateBack(false)).isTrue()
+            layoutState.navigateBack(false)
+        }
+
+        composeRule.runOnIdle {
+            assertThat(layoutState.layoutValue.secondary).isEqualTo(PaneAdaptedValue.Expanded)
+        }
+    }
+
+    @Test
+    fun singlePaneToDualPaneLayout_enforceLayoutValueChange_cannotNavigateBack() {
+        lateinit var layoutState: ListDetailPaneScaffoldState
+        val mockCurrentLayoutDirective = mutableStateOf(MockSinglePaneLayoutDirective)
+
+        composeRule.setContent {
+            layoutState = rememberListDetailPaneScaffoldState(
+                layoutDirectives = mockCurrentLayoutDirective.value
+            )
+            layoutState.navigateTo(ListDetailPaneScaffoldRole.List)
+        }
+
+        composeRule.runOnIdle {
+            assertThat(layoutState.layoutValue.secondary).isEqualTo(PaneAdaptedValue.Expanded)
+            // Switches to dual pane
+            mockCurrentLayoutDirective.value = MockDualPaneLayoutDirective
+        }
+
+        composeRule.runOnIdle {
+            assertThat(layoutState.canNavigateBack()).isFalse()
+        }
+    }
+}
+
+@OptIn(ExperimentalMaterial3AdaptiveApi::class)
+private val MockSinglePaneLayoutDirective = AdaptiveLayoutDirective(
+    maxHorizontalPartitions = 1,
+    gutterSizes = GutterSizes(0.dp, 0.dp)
+)
+
+@OptIn(ExperimentalMaterial3AdaptiveApi::class)
+private val MockDualPaneLayoutDirective = AdaptiveLayoutDirective(
+    maxHorizontalPartitions = 2,
+    gutterSizes = GutterSizes(16.dp, 16.dp)
+)
diff --git a/compose/material3/material3-adaptive/src/androidAndroidTest/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldScreenshotTest.kt b/compose/material3/material3-adaptive/src/androidAndroidTest/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldScreenshotTest.kt
new file mode 100644
index 0000000..7ff97c2
--- /dev/null
+++ b/compose/material3/material3-adaptive/src/androidAndroidTest/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldScreenshotTest.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.adaptive
+
+import android.os.Build
+import androidx.compose.testutils.assertAgainstGolden
+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(ExperimentalMaterial3AdaptiveApi::class)
+class ThreePaneScaffoldScreenshotTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    @get:Rule
+    val screenshotRule = AndroidXScreenshotTestRule(GOLDEN_MATERIAL3_ADAPTIVE)
+
+    @Test
+    fun threePaneScaffold_listDetailArrangement_standard() {
+        rule.setContent {
+            val layoutDirective = calculateStandardAdaptiveLayoutDirective(
+                calculateWindowAdaptiveInfo()
+            )
+            val scaffoldValue = calculateThreePaneScaffoldValue(
+                layoutDirective.maxHorizontalPartitions
+            )
+            SampleThreePaneScaffold(
+                layoutDirective,
+                scaffoldValue,
+                ThreePaneScaffoldDefaults.ListDetailLayoutArrangement
+            )
+        }
+
+        rule.onNodeWithTag(ThreePaneScaffoldTestTag)
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "threePaneScaffold_listDetail_standard")
+    }
+
+    @Test
+    fun threePaneScaffold_listDetailArrangement_dense() {
+        rule.setContent {
+            val layoutDirective = calculateDenseAdaptiveLayoutDirective(
+                calculateWindowAdaptiveInfo()
+            )
+            val scaffoldValue = calculateThreePaneScaffoldValue(
+                layoutDirective.maxHorizontalPartitions
+            )
+            SampleThreePaneScaffold(
+                layoutDirective,
+                scaffoldValue,
+                ThreePaneScaffoldDefaults.ListDetailLayoutArrangement
+            )
+        }
+
+        rule.onNodeWithTag(ThreePaneScaffoldTestTag)
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, "threePaneScaffold_listDetail_dense")
+    }
+}
diff --git a/compose/material3/material3-adaptive/src/androidAndroidTest/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldTest.kt b/compose/material3/material3-adaptive/src/androidAndroidTest/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldTest.kt
new file mode 100644
index 0000000..2242806
--- /dev/null
+++ b/compose/material3/material3-adaptive/src/androidAndroidTest/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldTest.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.adaptive
+
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalMaterial3AdaptiveApi::class)
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class ThreePaneScaffoldTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun threePaneScaffold_allPanesHidden_noVisiblePanes() {
+         val testScaffoldValue = ThreePaneScaffoldValue(
+             PaneAdaptedValue.Hidden,
+             PaneAdaptedValue.Hidden,
+             PaneAdaptedValue.Hidden
+         )
+         rule.setContent {
+             SampleThreePaneScaffold(scaffoldValue = testScaffoldValue)
+         }
+
+         rule.onNodeWithTag("PrimaryPane").assertDoesNotExist()
+         rule.onNodeWithTag("SecondaryPane").assertDoesNotExist()
+         rule.onNodeWithTag("TertiaryPane").assertDoesNotExist()
+    }
+
+    @Test
+    fun threePaneScaffold_oneExpandedPane_onlyExpandedPanesAreVisible() {
+        val testScaffoldValue = ThreePaneScaffoldValue(
+            PaneAdaptedValue.Expanded,
+            PaneAdaptedValue.Hidden,
+            PaneAdaptedValue.Hidden
+        )
+        rule.setContent {
+            SampleThreePaneScaffold(scaffoldValue = testScaffoldValue)
+        }
+
+        rule.onNodeWithTag("PrimaryPane").assertExists()
+        rule.onNodeWithTag("SecondaryPane").assertDoesNotExist()
+        rule.onNodeWithTag("TertiaryPane").assertDoesNotExist()
+    }
+
+    @Test
+    fun threePaneScaffold_twoExpandedPanes_onlyExpandedPanesAreVisible() {
+        val testScaffoldValue = ThreePaneScaffoldValue(
+            PaneAdaptedValue.Hidden,
+            PaneAdaptedValue.Expanded,
+            PaneAdaptedValue.Expanded
+        )
+        rule.setContent {
+            SampleThreePaneScaffold(scaffoldValue = testScaffoldValue)
+        }
+
+        rule.onNodeWithTag("PrimaryPane").assertDoesNotExist()
+        rule.onNodeWithTag("SecondaryPane").assertExists()
+        rule.onNodeWithTag("TertiaryPane").assertExists()
+    }
+
+    @Test
+    fun threePaneScaffold_threeExpandedPanes_onlyExpandedPanesAreVisible() {
+        val testScaffoldValue = ThreePaneScaffoldValue(
+            PaneAdaptedValue.Expanded,
+            PaneAdaptedValue.Expanded,
+            PaneAdaptedValue.Expanded
+        )
+        rule.setContent {
+            SampleThreePaneScaffold(scaffoldValue = testScaffoldValue)
+        }
+
+        rule.onNodeWithTag("PrimaryPane").assertExists()
+        rule.onNodeWithTag("SecondaryPane").assertExists()
+        rule.onNodeWithTag("TertiaryPane").assertExists()
+    }
+}
+
+@OptIn(ExperimentalMaterial3AdaptiveApi::class)
+private val MockLayoutDirective = AdaptiveLayoutDirective(
+    maxHorizontalPartitions = 1,
+    gutterSizes = GutterSizes(0.dp, 0.dp)
+)
+
+internal const val ThreePaneScaffoldTestTag = "SampleThreePaneScaffold"
+
+@OptIn(ExperimentalMaterial3AdaptiveApi::class)
+@Composable
+private fun SampleThreePaneScaffold(scaffoldValue: ThreePaneScaffoldValue) {
+    SampleThreePaneScaffold(
+        MockLayoutDirective,
+        scaffoldValue,
+        ThreePaneScaffoldDefaults.ListDetailLayoutArrangement
+    )
+}
+
+@OptIn(ExperimentalMaterial3AdaptiveApi::class)
+@Composable
+internal fun SampleThreePaneScaffold(
+    layoutDirective: AdaptiveLayoutDirective,
+    scaffoldValue: ThreePaneScaffoldValue,
+    arrangement: ThreePaneScaffoldArrangement
+) {
+    ThreePaneScaffold(
+        modifier = Modifier.fillMaxSize().testTag(ThreePaneScaffoldTestTag),
+        layoutDirective = layoutDirective,
+        scaffoldValue = scaffoldValue,
+        arrangement = arrangement,
+        secondaryPane = {
+            Surface(
+                modifier = Modifier.testTag(tag = "SecondaryPane"),
+                color = MaterialTheme.colorScheme.secondary
+            ) {}
+        },
+        tertiaryPane = {
+            Surface(
+                modifier = Modifier.testTag(tag = "TertiaryPane"),
+                color = MaterialTheme.colorScheme.tertiary
+            ) {}
+        }
+    ) {
+        Surface(
+            modifier = Modifier.testTag(tag = "PrimaryPane"),
+            color = MaterialTheme.colorScheme.primary
+        ) {}
+    }
+}
diff --git a/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/AndroidPosture.android.kt b/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/AndroidPosture.android.kt
index cad4404..4a461e26 100644
--- a/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/AndroidPosture.android.kt
+++ b/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/AndroidPosture.android.kt
@@ -16,6 +16,8 @@
 
 package androidx.compose.material3.adaptive
 
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.graphics.toComposeRect
 import androidx.window.layout.FoldingFeature
 
 /**
@@ -27,6 +29,9 @@
     var hasVerticalHinge = false
     var isTableTop = false
     var hasSeparatingHinge = false
+    val separatingHingeBounds = mutableListOf<Rect>()
+    val occludingHingeBounds = mutableListOf<Rect>()
+    val allHingeBounds = mutableListOf<Rect>()
     foldingFeatures.forEach {
         if (it.orientation == FoldingFeature.Orientation.VERTICAL) {
             hasVerticalHinge = true
@@ -38,6 +43,21 @@
             it.state == FoldingFeature.State.HALF_OPENED) {
             isTableTop = true
         }
+        val hingeBounds = it.bounds.toComposeRect()
+        allHingeBounds.add(hingeBounds)
+        if (it.isSeparating) {
+            separatingHingeBounds.add(hingeBounds)
+        }
+        if (it.occlusionType == FoldingFeature.OcclusionType.FULL) {
+            occludingHingeBounds.add(hingeBounds)
+        }
     }
-    return Posture(hasVerticalHinge, isTableTop, hasSeparatingHinge)
+    return Posture(
+        hasVerticalHinge,
+        isTableTop,
+        hasSeparatingHinge,
+        separatingHingeBounds,
+        occludingHingeBounds,
+        allHingeBounds
+    )
 }
diff --git a/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/ListDetailPaneScaffold.kt b/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/ListDetailPaneScaffold.kt
new file mode 100644
index 0000000..57d3e56
--- /dev/null
+++ b/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/ListDetailPaneScaffold.kt
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.material3.adaptive
+
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.Modifier
+
+/**
+ * A Material opinionated implementation of [ThreePaneScaffold] that will display the provided three
+ * panes in a canonical list-detail layout.
+ *
+ * @param layoutState the state of the scaffold, which will decide the current layout directive
+ *        and scaffold layout value, and perform navigation within the scaffold.
+ * @param listPane the list pane of the scaffold. See [ListDetailPaneScaffoldRole.List].
+ * @param modifier [Modifier] of the scaffold layout.
+ * @param extraPane the list pane of the scaffold. See [ListDetailPaneScaffoldRole.Extra].
+ * @param detailPane the list pane of the scaffold. See [ListDetailPaneScaffoldRole.Detail].
+ */
+@ExperimentalMaterial3AdaptiveApi
+@Composable
+fun ListDetailPaneScaffold(
+    layoutState: ListDetailPaneScaffoldState,
+    listPane: @Composable ThreePaneScaffoldScope.(PaneAdaptedValue) -> Unit,
+    modifier: Modifier = Modifier,
+    extraPane: (@Composable ThreePaneScaffoldScope.(PaneAdaptedValue) -> Unit)? = null,
+    detailPane: @Composable ThreePaneScaffoldScope.(PaneAdaptedValue) -> Unit
+) {
+    ThreePaneScaffold(
+        modifier = modifier.fillMaxSize(),
+        layoutDirective = layoutState.layoutDirective,
+        scaffoldValue = layoutState.layoutValue,
+        arrangement = ThreePaneScaffoldDefaults.ListDetailLayoutArrangement,
+        secondaryPane = listPane,
+        tertiaryPane = extraPane,
+        primaryPane = detailPane
+    )
+}
+
+/**
+ * Provides default values of [ListDetailPaneScaffold].
+ */
+@ExperimentalMaterial3AdaptiveApi
+object ListDetailPaneScaffoldDefaults {
+    /**
+     * Creates a default [ThreePaneScaffoldAdaptStrategies] for [ListDetailPaneScaffold].
+     *
+     * @param detailPaneAdaptStrategy the adapt strategy of the primary pane
+     * @param listPaneAdaptStrategy the adapt strategy of the secondary pane
+     * @param extraPaneAdaptStrategy the adapt strategy of the tertiary pane
+     */
+    fun adaptStrategies(
+        detailPaneAdaptStrategy: AdaptStrategy = AdaptStrategy.Hide,
+        listPaneAdaptStrategy: AdaptStrategy = AdaptStrategy.Hide,
+        extraPaneAdaptStrategy: AdaptStrategy = AdaptStrategy.Hide,
+    ): ThreePaneScaffoldAdaptStrategies =
+        ThreePaneScaffoldAdaptStrategies(
+            detailPaneAdaptStrategy,
+            listPaneAdaptStrategy,
+            extraPaneAdaptStrategy
+        )
+}
+
+/**
+ * The state of [ListDetailPaneScaffold]. It provides the layout directive and value state that will
+ * be updated directly. It also provides functions to perform navigation.
+ *
+ * Use [rememberListDetailPaneScaffoldState] to get a remembered default instance of this interface,
+ * which works independently from any navigation frameworks. Developers can also integrate with
+ * other navigation frameworks by implementing this interface.
+ *
+ * @property layoutDirective the current layout directives that the associated
+ *           [ListDetailPaneScaffold] needs to follow. It's supposed to be automatically updated
+ *           when the window configuration changes.
+ * @property layoutValue the current layout value of the associated [ListDetailPaneScaffold], which
+ *           represents unique layout states of the scaffold.
+ */
+@ExperimentalMaterial3AdaptiveApi
+@Stable
+interface ListDetailPaneScaffoldState {
+    val layoutDirective: AdaptiveLayoutDirective
+    val layoutValue: ThreePaneScaffoldValue
+
+    /**
+     * Navigates to a new focus.
+     */
+    fun navigateTo(pane: ListDetailPaneScaffoldRole)
+
+    /**
+     * Returns `true` if there is a previous focus to navigate back to.
+     *
+     * @param layoutValueMustChange `true` if the navigation operation should only be performed when
+     *        there are actual layout value changes.
+     */
+    fun canNavigateBack(layoutValueMustChange: Boolean = true): Boolean
+
+    /**
+     * Navigates to the previous focus.
+     *
+     * @param popUntilLayoutValueChange `true` if the backstack should be popped until the layout
+     *        value changes.
+     */
+    fun navigateBack(popUntilLayoutValueChange: Boolean = true): Boolean
+}
+
+@OptIn(ExperimentalMaterial3AdaptiveApi::class)
+private class DefaultListDetailPaneScaffoldState(
+    override val layoutDirective: AdaptiveLayoutDirective,
+    val adaptStrategies: ThreePaneScaffoldAdaptStrategies,
+    val focusHistory: MutableList<ListDetailPaneScaffoldRole>
+) : ListDetailPaneScaffoldState {
+    val currentFocus: ListDetailPaneScaffoldRole? get() = focusHistory.lastOrNull()
+    val currentValueState: MutableState<ThreePaneScaffoldValue> =
+        mutableStateOf(calculateCurrentScaffoldValue())
+
+    override val layoutValue: ThreePaneScaffoldValue
+        get() = currentValueState.value
+
+    override fun navigateTo(pane: ListDetailPaneScaffoldRole) {
+        focusHistory.add(pane)
+        currentValueState.value = calculateCurrentScaffoldValue()
+    }
+
+    override fun canNavigateBack(layoutValueMustChange: Boolean): Boolean =
+        getPreviousFocusIndex(layoutValueMustChange) >= 0
+
+    override fun navigateBack(popUntilLayoutValueChange: Boolean): Boolean {
+        val previousFocusIndex = getPreviousFocusIndex(popUntilLayoutValueChange)
+        if (previousFocusIndex < 0) {
+            focusHistory.clear()
+            return false
+        }
+        val targetSize = previousFocusIndex + 1
+        while (focusHistory.size > targetSize) {
+            focusHistory.removeLast()
+        }
+        currentValueState.value = calculateCurrentScaffoldValue()
+        return true
+    }
+
+    private fun getPreviousFocusIndex(withLayoutValueChange: Boolean): Int {
+        if (focusHistory.size <= 1) {
+            // No previous focus
+            return -1
+        }
+        if (!withLayoutValueChange) {
+            return focusHistory.lastIndex - 1
+        }
+        for (previousFocusIndex in focusHistory.lastIndex - 1 downTo 0) {
+            val newValue = calculateScaffoldValue(focusHistory[previousFocusIndex])
+            if (newValue != currentValueState.value) {
+                return previousFocusIndex
+            }
+        }
+        return -1
+    }
+
+    private fun calculateScaffoldValue(focus: ListDetailPaneScaffoldRole?): ThreePaneScaffoldValue =
+        calculateThreePaneScaffoldValue(
+            layoutDirective.maxHorizontalPartitions,
+            adaptStrategies,
+            focus?.threePaneScaffoldRole
+        )
+
+    private fun calculateCurrentScaffoldValue(): ThreePaneScaffoldValue =
+        calculateScaffoldValue(currentFocus)
+}
+
+/**
+ * Returns a remembered default implementation of [ListDetailPaneScaffoldState], which will
+ * be updated automatically when the input values change. The default state is supposed to be
+ * used independently from any navigation frameworks and it will address the navigation purely
+ * inside the [ListDetailPaneScaffold].
+ *
+ * @param layoutDirectives the current layout directives to follow. The default value will be
+ *        Calculated with [calculateStandardAdaptiveLayoutDirective] using [WindowAdaptiveInfo]
+ *        retrieved from the current context.
+ * @param adaptStrategies adaptation strategies of each pane.
+ * @param initialFocus the initial focus of the scaffold, by default it will be the detail pane.
+ */
+@ExperimentalMaterial3AdaptiveApi
+@Composable
+fun rememberListDetailPaneScaffoldState(
+    layoutDirectives: AdaptiveLayoutDirective =
+        calculateStandardAdaptiveLayoutDirective(calculateWindowAdaptiveInfo()),
+    adaptStrategies: ThreePaneScaffoldAdaptStrategies =
+        ListDetailPaneScaffoldDefaults.adaptStrategies(),
+    initialFocus: ListDetailPaneScaffoldRole = ListDetailPaneScaffoldRole.Detail
+): ListDetailPaneScaffoldState {
+    val focusHistory = rememberSaveable { mutableListOf(initialFocus) }
+    return remember(layoutDirectives, adaptStrategies) {
+        DefaultListDetailPaneScaffoldState(layoutDirectives, adaptStrategies, focusHistory)
+    }
+}
+
+/**
+ * The set of the available pane roles of [ListDetailPaneScaffold].
+ */
+@ExperimentalMaterial3AdaptiveApi
+enum class ListDetailPaneScaffoldRole(internal val threePaneScaffoldRole: ThreePaneScaffoldRole) {
+    /**
+     * The list pane of [ListDetailPaneScaffold]. It is mapped to [ThreePaneScaffoldRole.Secondary].
+     */
+    List(ThreePaneScaffoldRole.Secondary),
+    /**
+     * The detail pane of [ListDetailPaneScaffold]. It is mapped to [ThreePaneScaffoldRole.Primary].
+     */
+    Detail(ThreePaneScaffoldRole.Primary),
+    /**
+     * The extra pane of [ListDetailPaneScaffold]. It is mapped to [ThreePaneScaffoldRole.Tertiary].
+     */
+    Extra(ThreePaneScaffoldRole.Tertiary)
+}
diff --git a/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/AdaptiveLayoutDirective.kt b/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/AdaptiveLayoutDirective.kt
index 9ed5c8f..9fc0544 100644
--- a/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/AdaptiveLayoutDirective.kt
+++ b/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/AdaptiveLayoutDirective.kt
@@ -18,6 +18,7 @@
 
 import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
 import androidx.compose.runtime.Immutable
+import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 
@@ -31,12 +32,14 @@
  *
  * @param windowAdaptiveInfo [WindowAdaptiveInfo] that collects useful information in making
  *                           layout adaptation decisions like [WindowSizeClass].
+ * @param hingePolicy [HingePolicy] that decides how layouts are supposed to address hinges.
  * @return an [AdaptiveLayoutDirective] to be used to decide adaptive layout states.
  */
 // TODO(b/285144647): Add more details regarding the use scenarios of this function.
 @ExperimentalMaterial3AdaptiveApi
 fun calculateStandardAdaptiveLayoutDirective(
-    windowAdaptiveInfo: WindowAdaptiveInfo
+    windowAdaptiveInfo: WindowAdaptiveInfo,
+    hingePolicy: HingePolicy = HingePolicy.AvoidSeparating
 ): AdaptiveLayoutDirective {
     val maxHorizontalPartitions: Int
     val gutterOuterVertical: Dp
@@ -75,7 +78,8 @@
         GutterSizes(
             gutterOuterVertical, gutterInnerVertical, innerHorizontal = gutterInnerHorizontal
         ),
-        maxVerticalPartitions
+        maxVerticalPartitions,
+        getExcludedBounds(windowAdaptiveInfo.posture, hingePolicy)
     )
 }
 
@@ -89,12 +93,14 @@
  *
  * @param windowAdaptiveInfo [WindowAdaptiveInfo] that collects useful information in making
  *                           layout adaptation decisions like [WindowSizeClass].
+ * @param hingePolicy [HingePolicy] that decides how layouts are supposed to address hinges.
  * @return an [AdaptiveLayoutDirective] to be used to decide adaptive layout states.
  */
 // TODO(b/285144647): Add more details regarding the use scenarios of this function.
 @ExperimentalMaterial3AdaptiveApi
 fun calculateDenseAdaptiveLayoutDirective(
-    windowAdaptiveInfo: WindowAdaptiveInfo
+    windowAdaptiveInfo: WindowAdaptiveInfo,
+    hingePolicy: HingePolicy = HingePolicy.AvoidSeparating
 ): AdaptiveLayoutDirective {
     val maxHorizontalPartitions: Int
     val gutterOuterVertical: Dp
@@ -133,25 +139,41 @@
         GutterSizes(
             gutterOuterVertical, gutterInnerVertical, innerHorizontal = gutterInnerHorizontal
         ),
-        maxVerticalPartitions
+        maxVerticalPartitions,
+        getExcludedBounds(windowAdaptiveInfo.posture, hingePolicy)
     )
 }
 
+@OptIn(ExperimentalMaterial3AdaptiveApi::class)
+private fun getExcludedBounds(posture: Posture, hingePolicy: HingePolicy): List<Rect> {
+    return when (hingePolicy) {
+        HingePolicy.AvoidSeparating -> posture.separatingHingeBounds
+        HingePolicy.AvoidOccluding -> posture.occludingHingeBounds
+        HingePolicy.AlwaysAvoid -> posture.allHingeBounds
+        else -> emptyList()
+    }
+}
+
 /**
  * Top-level directives about how an adaptive layout should be arranged and spaced, like how many
  * partitions the layout can be split into and what should be the gutter size.
+ *
+ * @constructor create an instance of [AdaptiveLayoutDirective]
+ * @param maxHorizontalPartitions the max number of partitions along the horizontal axis the layout
+ *        can be split into.
+ * @param gutterSizes the gutter sizes between panes the layout should preserve.
+ * @param maxVerticalPartitions the max number of partitions along the vertical axis the layout can
+ *        be split into.
+ * @param excludedBounds the bounds of all areas in the window that the layout needs to avoid
+ *        displaying anything upon it. Usually these bounds represent where physical hinges are.
  */
+@ExperimentalMaterial3AdaptiveApi
 @Immutable
 class AdaptiveLayoutDirective(
-    /** How many partitions along the horizontal axis the respective layout can be split into. */
     val maxHorizontalPartitions: Int,
-    /** The gutter size of the respective layout should preserve. */
     val gutterSizes: GutterSizes,
-    /**
-     * How many partitions along the vertical axis the respective layout can be split into.
-     * The default value is 1.
-     */
-    val maxVerticalPartitions: Int = 1
+    val maxVerticalPartitions: Int = 1,
+    val excludedBounds: List<Rect> = emptyList()
 ) {
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
@@ -175,26 +197,23 @@
  * between panes ([innerVertical] and [innerHorizontal]) and paddings of the layout itself
  * ([outerVertical] and [outerHorizontal]). Usually we will expect larger gutter sizes to be set
  * when the layout is larger and more panes are shown in the layout.
+ *
+ * @constructor create an instance of [GutterSizes]
+ * @param outerVertical Size of the outer vertical gutters. It's similar to left/right paddings of
+ *        a normal layout.
+ * @param innerVertical Size of the inner vertical gutters. It's similar to left/right margins of
+ *        the layout's children.
+ * @param outerHorizontal Size of the outer horizontal gutters. It's similar to top/bottom paddings
+ *        of a normal layout.
+ * @param innerHorizontal Size of the inner horizontal gutters. It's similar to top/bottom margins
+ *        of the layout's children.
  */
+@ExperimentalMaterial3AdaptiveApi
 @Immutable
 class GutterSizes(
-    /**
-     * Size of the outer vertical gutters. It's similar to left/right paddings of a normal layout.
-     */
     val outerVertical: Dp,
-    /**
-     * Size of the inner vertical gutters. It's similar to left/right margins of the layout's
-     * children.
-     */
     val innerVertical: Dp,
-    /**
-     * Size of the outer horizontal gutters. It's similar to top/bottom paddings of a normal layout.
-     */
     val outerHorizontal: Dp = outerVertical,
-    /**
-     * Size of the inner horizontal gutters. It's similar to top/bottom margins of the layout's
-     * children.
-     */
     val innerHorizontal: Dp = innerVertical
 ) {
     override fun equals(other: Any?): Boolean {
@@ -215,3 +234,35 @@
         return result
     }
 }
+
+/** Policies that indicate how hinges are supposed to be addressed in an adaptive layout. */
+@Immutable
[email protected]
+value class HingePolicy private constructor(private val value: Int) {
+    override fun toString(): String {
+        return "HingePolicy." + when (this) {
+            AlwaysAvoid -> "AlwaysAvoid"
+            AvoidSeparating -> "AvoidOccludingAndSeparating"
+            AvoidOccluding -> "AvoidOccludingOnly"
+            NeverAvoid -> "NeverAvoid"
+            else -> ""
+        }
+    }
+
+    companion object {
+        /** When rendering content in a layout, always avoid where hinges are. */
+        val AlwaysAvoid = HingePolicy(0)
+        /**
+         * When rendering content in a layout, avoid hinges that are separating. Note that an
+         * occluding hinge is supposed to be separating as well but not vice versa.
+         */
+        val AvoidSeparating = HingePolicy(1)
+        /**
+         * When rendering content in a layout, avoid hinges that are occluding. Note that an
+         * occluding hinge is supposed to be separating as well but not vice versa.
+         */
+        val AvoidOccluding = HingePolicy(2)
+        /** When rendering content in a layout, never avoid any hinges, separating or not. */
+        val NeverAvoid = HingePolicy(3)
+    }
+}
diff --git a/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/PaneScaffold.kt b/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/PaneScaffold.kt
index 4e1658b..6832d50 100644
--- a/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/PaneScaffold.kt
+++ b/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/PaneScaffold.kt
@@ -38,7 +38,7 @@
 }
 
 @OptIn(ExperimentalMaterial3AdaptiveApi::class)
-private object PaneScaffoldScopeInstance : PaneScaffoldScope {
+internal abstract class PaneScaffoldScopeImpl : PaneScaffoldScope {
     override fun Modifier.preferredWidth(width: Dp): Modifier {
         require(width == Dp.Unspecified || width > 0.dp) { "invalid width" }
         return this.then(PreferredWidthElement(width))
@@ -83,6 +83,6 @@
         }
 }
 
-private data class PaneScaffoldParentData(
-    var preferredWidth: Float = Float.NaN,
+internal data class PaneScaffoldParentData(
+    var preferredWidth: Float? = null,
 )
diff --git a/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/Posture.kt b/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/Posture.kt
index 1e7c87e..1eb8189 100644
--- a/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/Posture.kt
+++ b/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/Posture.kt
@@ -17,6 +17,7 @@
 package androidx.compose.material3.adaptive
 
 import androidx.compose.runtime.Immutable
+import androidx.compose.ui.geometry.Rect
 
 /**
  * Posture info that can help make layout adaptation decisions. For example when
@@ -24,33 +25,34 @@
  * the hinge area. We suggest to use [calculatePosture] to retrieve instances of this class in
  * applications, unless you have a strong need of customization that cannot be fulfilled by the
  * default implementation.
+ *
+ * @constructor create an instance of [Posture]
+ * @param hasVerticalHinge `true` if at least one vertical hinge is present in the middle of
+ *        the current window. When this is `true`, it means the current window is separated into
+ *        multiple partitions along the horizontal axis and developers may consider separating
+ *        the layout into multiple partitions accordingly.
+ * @param isTabletop `true` if the current window is considered as in the table top mode, i.e. there
+ *        is one half-opened horizontal hinge in the middle of the current window. When this is
+ *        `true` it usually means it's hard for users to interact with the window area around
+ *        the hinge and developers may consider separating the layout along the hinge and show
+ *        software keyboard or other controls in the bottom half of the window.
+ * @param hasSeparatingHinge `true` if at least one hinge present in the current window is
+ *        separating, i.e., content cannot be displayed on the hinge area. When this is `true`
+ *        developer may want to avoid showing anything around the hinge area because the content
+ *        will be cut or not visible.
+ * @param separatingHingeBounds the list of hinge bounds that are separating.
+ * @param occludingHingeBounds the list of hinge bounds that are occluding.
+ * @param allHingeBounds the list of all hinge bounds.
  */
 @ExperimentalMaterial3AdaptiveApi
 @Immutable
 class Posture(
-    /**
-     * `true` if at least one vertical hinge is present in the middle of the current window. When
-     * this is `true`, it means the current window is separated into multiple partitions along the
-     * horizontal axis and developers may consider separating the layout into multiple partitions
-     * accordingly.
-     */
     val hasVerticalHinge: Boolean = false,
-
-    /**
-     * `true` if the current window is considered as in the table top mode, i.e. there is
-     * one half-opened horizontal hinge in the middle of the current window. When this is `true` it
-     * usually means it's hard for users to interact with the window area around the hinge and
-     * developers may consider separating the layout along the hinge and show software keyboard or
-     * other controls in the bottom half of the window.
-     */
     val isTabletop: Boolean = false,
-
-    /**
-     * `true` if at least one hinge present in the current window is separating, i.e., content
-     * cannot be displayed on the hinge area. When this is `true` developer may want to avoid
-     * showing anything around the hinge area because the content will be cut or not visible.
-     */
-    val hasSeparatingHinge: Boolean = false
+    val hasSeparatingHinge: Boolean = false,
+    val separatingHingeBounds: List<Rect> = emptyList(),
+    val occludingHingeBounds: List<Rect> = emptyList(),
+    val allHingeBounds: List<Rect> = emptyList()
 ) {
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
@@ -58,6 +60,9 @@
         if (hasVerticalHinge != other.hasVerticalHinge) return false
         if (isTabletop != other.isTabletop) return false
         if (hasSeparatingHinge != other.hasSeparatingHinge) return false
+        if (separatingHingeBounds != other.separatingHingeBounds) return false
+        if (occludingHingeBounds != other.occludingHingeBounds) return false
+        if (allHingeBounds != other.allHingeBounds) return false
         return true
     }
 
@@ -65,6 +70,9 @@
         var result = hasVerticalHinge.hashCode()
         result = 31 * result + isTabletop.hashCode()
         result = 31 * result + hasSeparatingHinge.hashCode()
+        result = 31 * result + separatingHingeBounds.hashCode()
+        result = 31 * result + occludingHingeBounds.hashCode()
+        result = 31 * result + allHingeBounds.hashCode()
         return result
     }
 }
diff --git a/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffold.kt b/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffold.kt
index 3ef3860..a75073e 100644
--- a/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffold.kt
+++ b/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffold.kt
@@ -17,12 +17,280 @@
 package androidx.compose.material3.adaptive
 
 import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.Placeable
+import androidx.compose.ui.layout.boundsInWindow
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.IntRect
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.roundToIntRect
+import androidx.compose.ui.util.fastForEach
+import kotlin.math.max
+import kotlin.math.min
 
+/**
+ * A pane scaffold composable that can display up to three panes according to the instructions
+ * provided by [ThreePaneScaffoldValue] in the order that [ThreePaneScaffoldArrangement] specifies,
+ * and allocate margins and spacers according to [AdaptiveLayoutDirective].
+ *
+ * [ThreePaneScaffold] is the base composable functions of adaptive programming. Developers can
+ * freely pipeline the relevant adaptive signals and use them as input of the scaffold function
+ * to render the final adaptive layout.
+ *
+ * It's recommended to use [ThreePaneScaffold] with [calculateStandardAdaptiveLayoutDirective],
+ * [calculateThreePaneScaffoldValue] to follow the Material design guidelines on adaptive
+ * programming.
+ *
+ * @param modifier The modifier to be applied to the layout.
+ * @param layoutDirective The top-level directives about how the scaffold should arrange its panes.
+ * @param scaffoldValue The current adapted value of the scaffold.
+ * @param arrangement The arrangement of the panes in the scaffold.
+ * @param secondaryPane The content of the secondary pane that has a priority lower then the primary
+ *                      pane but higher than the tertiary pane.
+ * @param tertiaryPane The content of the tertiary pane that has the lowest priority.
+ * @param primaryPane The content of the primary pane that has the highest priority.
+ */
+@ExperimentalMaterial3AdaptiveApi
 @Composable
-internal fun ThreePaneScaffold() {
-    // TODO(conradchen): implement the actual func
+fun ThreePaneScaffold(
+    modifier: Modifier,
+    layoutDirective: AdaptiveLayoutDirective,
+    scaffoldValue: ThreePaneScaffoldValue,
+    arrangement: ThreePaneScaffoldArrangement,
+    secondaryPane: @Composable ThreePaneScaffoldScope.(PaneAdaptedValue) -> Unit,
+    tertiaryPane: (@Composable ThreePaneScaffoldScope.(PaneAdaptedValue) -> Unit)? = null,
+    primaryPane: @Composable ThreePaneScaffoldScope.(PaneAdaptedValue) -> Unit
+) {
+    val contents = listOf<@Composable () -> Unit>(
+        { PaneWrapper(scaffoldValue[ThreePaneScaffoldRole.Primary], primaryPane) },
+        { PaneWrapper(scaffoldValue[ThreePaneScaffoldRole.Secondary], secondaryPane) },
+        { PaneWrapper(scaffoldValue[ThreePaneScaffoldRole.Tertiary], tertiaryPane) },
+    )
+
+    Layout(
+        contents = contents,
+        modifier = modifier,
+    ) { (primaryMeasurables, secondaryMeasurables, tertiaryMeasurables), constraints ->
+        layout(constraints.maxWidth, constraints.maxHeight) {
+            if (coordinates == null) {
+                return@layout
+            }
+            val paneMeasurables = buildList {
+                arrangement.forEach { role ->
+                    when (role) {
+                        ThreePaneScaffoldRole.Primary -> {
+                            createPaneMeasurableIfNeeded(
+                                primaryMeasurables,
+                                ThreePaneScaffoldDefaults.PrimaryPanePriority,
+                                ThreePaneScaffoldDefaults.PrimaryPanePreferredWidth.roundToPx()
+                            )
+                        }
+                        ThreePaneScaffoldRole.Secondary -> {
+                            createPaneMeasurableIfNeeded(
+                                secondaryMeasurables,
+                                ThreePaneScaffoldDefaults.SecondaryPanePriority,
+                                ThreePaneScaffoldDefaults.SecondaryPanePreferredWidth.roundToPx()
+                            )
+                        }
+                        ThreePaneScaffoldRole.Tertiary -> {
+                            createPaneMeasurableIfNeeded(
+                                tertiaryMeasurables,
+                                ThreePaneScaffoldDefaults.TertiaryPanePriority,
+                                ThreePaneScaffoldDefaults.TertiaryPanePreferredWidth.roundToPx()
+                            )
+                        }
+                    }
+                }
+            }
+
+            val outerVerticalGutterSize = layoutDirective.gutterSizes.outerVertical.roundToPx()
+            val innerVerticalGutterSize = layoutDirective.gutterSizes.innerVertical.roundToPx()
+            val outerHorizontalGutterSize = layoutDirective.gutterSizes.outerHorizontal.roundToPx()
+
+            if (layoutDirective.excludedBounds.isNotEmpty()) {
+                val layoutBounds = coordinates!!.boundsInWindow()
+                val layoutPhysicalPartitions = mutableListOf<Rect>()
+                var actualLeft = layoutBounds.left + outerVerticalGutterSize
+                var actualRight = layoutBounds.right - outerVerticalGutterSize
+                // Assume hinge bounds are sorted from left to right, non-overlapped.
+                layoutDirective.excludedBounds.fastForEach { hingeBound ->
+                    if (hingeBound.left <= actualLeft) {
+                        // The hinge is at the left of the layout, adjust the left edge of
+                        // the current partition to the actual displayable bounds.
+                        actualLeft = max(actualLeft, hingeBound.right)
+                    } else if (hingeBound.right >= actualRight) {
+                        // The hinge is right at the right of the layout and there's no more room
+                        // for more partitions, adjust the right edge of the current partition to
+                        // the actual displayable bounds.
+                        actualRight = min(hingeBound.left, actualRight)
+                        return@fastForEach
+                    } else {
+                        // The hinge is inside the layout, add the current partition to the list and
+                        // move the left edge of the next partition to the right of the hinge.
+                        layoutPhysicalPartitions.add(
+                            Rect(actualLeft, layoutBounds.top, hingeBound.left, layoutBounds.bottom)
+                        )
+                        actualLeft +=
+                            max(hingeBound.right, hingeBound.left + innerVerticalGutterSize)
+                    }
+                }
+                if (actualLeft < actualRight) {
+                    // The last partition
+                    layoutPhysicalPartitions.add(
+                        Rect(actualLeft, layoutBounds.top, actualRight, layoutBounds.bottom)
+                    )
+                }
+                if (layoutPhysicalPartitions.size == 1) {
+                    measureAndPlacePanes(
+                        layoutPhysicalPartitions[0],
+                        innerVerticalGutterSize,
+                        paneMeasurables
+                    )
+                } else if (layoutPhysicalPartitions.size < paneMeasurables.size) {
+                    // Note that the only possible situation is we have only two physical partitions
+                    // but three expanded panes to show. In this case fit two panes in the larger
+                    // partition.
+                    if (layoutPhysicalPartitions[0].width > layoutPhysicalPartitions[1].width) {
+                        measureAndPlacePanes(
+                            layoutPhysicalPartitions[0],
+                            innerVerticalGutterSize,
+                            paneMeasurables.subList(0, 2)
+                        )
+                        measureAndPlacePane(layoutPhysicalPartitions[1], paneMeasurables[2])
+                    } else {
+                        measureAndPlacePane(layoutPhysicalPartitions[0], paneMeasurables[0])
+                        measureAndPlacePanes(
+                            layoutPhysicalPartitions[1],
+                            innerVerticalGutterSize,
+                            paneMeasurables.subList(1, 3)
+                        )
+                    }
+                } else {
+                    // Layout each pane in a physical partition
+                    paneMeasurables.forEachIndexed { index, paneMeasurable ->
+                        measureAndPlacePane(layoutPhysicalPartitions[index], paneMeasurable)
+                    }
+                }
+            } else {
+                measureAndPlacePanesWithLocalBounds(
+                    IntRect(
+                        outerVerticalGutterSize,
+                        outerHorizontalGutterSize,
+                        constraints.maxWidth - outerVerticalGutterSize,
+                        constraints.maxHeight - outerHorizontalGutterSize),
+                    innerVerticalGutterSize,
+                    paneMeasurables
+                )
+            }
+        }
+    }
 }
 
+@ExperimentalMaterial3AdaptiveApi
+@Composable
+private fun PaneWrapper(
+    adaptedValue: PaneAdaptedValue,
+    pane: (@Composable ThreePaneScaffoldScope.(PaneAdaptedValue) -> Unit)?
+) {
+    if (adaptedValue != PaneAdaptedValue.Hidden) {
+        pane?.invoke(ThreePaneScaffoldScopeImpl, adaptedValue)
+    }
+}
+
+private fun MutableList<PaneMeasurable>.createPaneMeasurableIfNeeded(
+    measurables: List<Measurable>,
+    priority: Int,
+    defaultPreferredWidth: Int
+) {
+    if (measurables.isNotEmpty()) {
+        add(PaneMeasurable(measurables[0], priority, defaultPreferredWidth))
+    }
+}
+private fun Placeable.PlacementScope.measureAndPlacePane(
+    partitionBounds: Rect,
+    measurable: PaneMeasurable
+) {
+    val localBounds = getLocalBounds(partitionBounds)
+    measurable.measuredWidth = localBounds.width
+    measurable.apply {
+        measure(Constraints.fixed(measuredWidth, localBounds.height))
+            .place(localBounds.left, localBounds.top)
+    }
+}
+
+private fun Placeable.PlacementScope.measureAndPlacePanes(
+    partitionBounds: Rect,
+    spacerSize: Int,
+    measurables: List<PaneMeasurable>
+) {
+    measureAndPlacePanesWithLocalBounds(
+        getLocalBounds(partitionBounds),
+        spacerSize,
+        measurables
+    )
+}
+
+private fun Placeable.PlacementScope.measureAndPlacePanesWithLocalBounds(
+    partitionBounds: IntRect,
+    spacerSize: Int,
+    measurables: List<PaneMeasurable>
+) {
+    if (measurables.isEmpty()) {
+        return
+    }
+    val allocatableWidth = partitionBounds.width - (measurables.size - 1) * spacerSize
+    val totalPreferredWidth = measurables.sumOf { it.measuredWidth }
+    if (allocatableWidth > totalPreferredWidth) {
+        // Allocate the remaining space to the pane with the highest priority.
+        measurables.maxBy {
+            it.priority
+        }.measuredWidth += allocatableWidth - totalPreferredWidth
+    } else if (allocatableWidth < totalPreferredWidth) {
+        // Scale down all panes to fit in the available space.
+        val scale = allocatableWidth.toFloat() / totalPreferredWidth
+        measurables.forEach {
+            it.measuredWidth = (it.measuredWidth * scale).toInt()
+        }
+    }
+    var positionX = partitionBounds.left
+    measurables.forEach {
+        it.measure(Constraints.fixed(it.measuredWidth, partitionBounds.height))
+            .place(positionX, partitionBounds.top)
+        positionX += it.measuredWidth + spacerSize
+    }
+}
+
+private fun Placeable.PlacementScope.getLocalBounds(bounds: Rect): IntRect {
+    return bounds.translate(coordinates!!.localToWindow(Offset.Zero)).roundToIntRect()
+}
+
+private class PaneMeasurable(
+    val measurable: Measurable,
+    val priority: Int,
+    defaultPreferredWidth: Int
+) : Measurable by measurable {
+    private val data = ((parentData as? PaneScaffoldParentData) ?: PaneScaffoldParentData())
+
+    // TODO(conradchen): Handle the case of a low priority pane with no preferred with
+    var measuredWidth = when (data.preferredWidth) {
+        null -> defaultPreferredWidth
+        Float.NaN -> 0
+        else -> data.preferredWidth!!.toInt()
+    }
+}
+
+/**
+ * Scope for the panes of [ThreePaneScaffold].
+ */
+@OptIn(ExperimentalMaterial3AdaptiveApi::class)
+interface ThreePaneScaffoldScope : PaneScaffoldScope
+
+private object ThreePaneScaffoldScopeImpl : ThreePaneScaffoldScope, PaneScaffoldScopeImpl()
+
 /**
  * Provides default values of [ThreePaneScaffold] and the calculation functions of
  * [ThreePaneScaffoldValue].
@@ -39,6 +307,16 @@
         ThreePaneScaffoldRole.Tertiary
     )
 
+    // TODO(conradchen): confirm with designers before we make these values public
+    internal val PrimaryPanePreferredWidth = 360.dp
+    internal val SecondaryPanePreferredWidth = 360.dp
+    internal val TertiaryPanePreferredWidth = 360.dp
+
+    // TODO(conradchen): consider declaring a value class for priority
+    internal const val PrimaryPanePriority = 10
+    internal const val SecondaryPanePriority = 5
+    internal const val TertiaryPanePriority = 1
+
     /**
      * Creates a default [ThreePaneScaffoldAdaptStrategies].
      *
diff --git a/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldAdaptStrategies.kt b/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldAdaptStrategies.kt
index 7b3360c..b0f43b1 100644
--- a/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldAdaptStrategies.kt
+++ b/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldAdaptStrategies.kt
@@ -21,6 +21,7 @@
  * [ThreePaneScaffold] should be adapted. It should be used as an input parameter of
  * [calculateThreePaneScaffoldValue] to decide the [ThreePaneScaffoldValue].
  *
+ * @constructor create an instance of [ThreePaneScaffoldAdaptStrategies]
  * @param primaryPaneAdaptStrategy [AdaptStrategy] of the primary pane of [ThreePaneScaffold]
  * @param secondaryPaneAdaptStrategy [AdaptStrategy] of the secondary pane of [ThreePaneScaffold]
  * @param tertiaryPaneAdaptStrategy [AdaptStrategy] of the tertiary pane of [ThreePaneScaffold]
diff --git a/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldArrangement.kt b/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldArrangement.kt
index eb42e5e..51e8642 100644
--- a/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldArrangement.kt
+++ b/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldArrangement.kt
@@ -23,6 +23,7 @@
  * [firstPane], [secondPane] and [thirdPane] have to be different, otherwise
  * [IllegalArgumentException] will be thrown.
  *
+ * @constructor create an instance of [ThreePaneScaffoldArrangement]
  * @param firstPane The first pane from the start of the [ThreePaneScaffold]
  * @param secondPane The second pane from the start of the [ThreePaneScaffold]
  * @param thirdPane The third pane from the start of the [ThreePaneScaffold]
@@ -58,6 +59,22 @@
     }
 }
 
+@ExperimentalMaterial3AdaptiveApi
+internal inline fun ThreePaneScaffoldArrangement.forEach(action: (ThreePaneScaffoldRole) -> Unit) {
+    action(firstPane)
+    action(secondPane)
+    action(thirdPane)
+}
+
+@ExperimentalMaterial3AdaptiveApi
+internal inline fun ThreePaneScaffoldArrangement.forEachIndexed(
+    action: (Int, ThreePaneScaffoldRole) -> Unit
+) {
+    action(0, firstPane)
+    action(1, secondPane)
+    action(2, thirdPane)
+}
+
 /**
  * The set of the available pane roles of [ThreePaneScaffold].
  */
diff --git a/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldValue.kt b/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldValue.kt
index c979682..66b6f06 100644
--- a/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldValue.kt
+++ b/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldValue.kt
@@ -79,6 +79,7 @@
  * For a Material-opinionated layout, it's suggested to use [calculateThreePaneScaffoldValue] to
  * calculate the current scaffold value.
  *
+ * @constructor create an instance of [ThreePaneScaffoldValue]
  * @param primary [PaneAdaptedValue] of the primary pane of [ThreePaneScaffold]
  * @param secondary [PaneAdaptedValue] of the secondary pane of [ThreePaneScaffold]
  * @param tertiary [PaneAdaptedValue] of the tertiary pane of [ThreePaneScaffold]
diff --git a/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/WindowAdaptiveInfo.kt b/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/WindowAdaptiveInfo.kt
index 57f24cb..cc76985 100644
--- a/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/WindowAdaptiveInfo.kt
+++ b/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/WindowAdaptiveInfo.kt
@@ -22,13 +22,15 @@
 /**
  * This class collects window info that affects adaptation decisions. An adaptive layout is supposed
  * to use the info from this class to decide how the layout is supposed to be adapted.
+ *
+ * @constructor create an instance of [WindowAdaptiveInfo]
+ * @param windowSizeClass [WindowSizeClass] of the current window.
+ * @param posture [Posture] of the current window.
  */
 @ExperimentalMaterial3AdaptiveApi
 @Immutable
 class WindowAdaptiveInfo(
-    /** [WindowSizeClass] of the current window. */
     val windowSizeClass: WindowSizeClass,
-    /** [Posture] of the current window. */
     val posture: Posture
 ) {
     override fun equals(other: Any?): Boolean {
diff --git a/compose/material3/material3-adaptive/src/test/kotlin/androidx/compose/material3/adaptive/AdaptiveLayoutDirectiveTest.kt b/compose/material3/material3-adaptive/src/test/kotlin/androidx/compose/material3/adaptive/AdaptiveLayoutDirectiveTest.kt
index 6ae0180..63a9db7 100644
--- a/compose/material3/material3-adaptive/src/test/kotlin/androidx/compose/material3/adaptive/AdaptiveLayoutDirectiveTest.kt
+++ b/compose/material3/material3-adaptive/src/test/kotlin/androidx/compose/material3/adaptive/AdaptiveLayoutDirectiveTest.kt
@@ -18,6 +18,7 @@
 
 import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
 import androidx.compose.material3.windowsizeclass.WindowSizeClass
+import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.dp
 import com.google.common.truth.Truth.assertThat
@@ -163,4 +164,154 @@
         assertThat(layoutDirective.gutterSizes.outerHorizontal).isEqualTo(24.dp)
         assertThat(layoutDirective.gutterSizes.innerHorizontal).isEqualTo(24.dp)
     }
+
+    @Test
+    fun test_calculateStandardAdaptiveLayoutDirective_alwaysAvoidHinge() {
+        val occludingHingeBounds = listOf(
+            Rect(0F, 0F, 1F, 1F),
+            Rect(1F, 1F, 2F, 2F),
+        )
+        val allHingeBounds = listOf(
+            Rect(0F, 0F, 1F, 1F),
+            Rect(1F, 1F, 2F, 2F),
+            Rect(2F, 2F, 3F, 3F)
+        )
+        val layoutDirective = calculateStandardAdaptiveLayoutDirective(
+            WindowAdaptiveInfo(
+                WindowSizeClass.calculateFromSize(DpSize(700.dp, 800.dp)),
+                Posture(
+                    allHingeBounds = allHingeBounds,
+                    occludingHingeBounds = occludingHingeBounds
+                )
+            ),
+            HingePolicy.AlwaysAvoid
+        )
+
+        assertThat(layoutDirective.excludedBounds).isEqualTo(allHingeBounds)
+    }
+
+    @Test
+    fun test_calculateStandardAdaptiveLayoutDirective_avoidOccludingHinge() {
+        val occludingHingeBounds = listOf(
+            Rect(0F, 0F, 1F, 1F),
+            Rect(1F, 1F, 2F, 2F),
+        )
+        val allHingeBounds = listOf(
+            Rect(0F, 0F, 1F, 1F),
+            Rect(1F, 1F, 2F, 2F),
+            Rect(2F, 2F, 3F, 3F)
+        )
+        val layoutDirective = calculateStandardAdaptiveLayoutDirective(
+            WindowAdaptiveInfo(
+                WindowSizeClass.calculateFromSize(DpSize(700.dp, 800.dp)),
+                Posture(
+                    allHingeBounds = allHingeBounds,
+                    occludingHingeBounds = occludingHingeBounds
+                )
+            ),
+            HingePolicy.AvoidOccluding
+        )
+
+        assertThat(layoutDirective.excludedBounds).isEqualTo(occludingHingeBounds)
+    }
+
+    @Test
+    fun test_calculateStandardAdaptiveLayoutDirective_neverAvoidHinge() {
+        val occludingHingeBounds = listOf(
+            Rect(0F, 0F, 1F, 1F),
+            Rect(1F, 1F, 2F, 2F),
+        )
+        val allHingeBounds = listOf(
+            Rect(0F, 0F, 1F, 1F),
+            Rect(1F, 1F, 2F, 2F),
+            Rect(2F, 2F, 3F, 3F)
+        )
+        val layoutDirective = calculateStandardAdaptiveLayoutDirective(
+            WindowAdaptiveInfo(
+                WindowSizeClass.calculateFromSize(DpSize(700.dp, 800.dp)),
+                Posture(
+                    allHingeBounds = allHingeBounds,
+                    occludingHingeBounds = occludingHingeBounds
+                )
+            ),
+            HingePolicy.NeverAvoid
+        )
+
+        assertThat(layoutDirective.excludedBounds).isEmpty()
+    }
+
+    @Test
+    fun test_calculateDenseAdaptiveLayoutDirective_alwaysAvoidHinge() {
+        val occludingHingeBounds = listOf(
+            Rect(0F, 0F, 1F, 1F),
+            Rect(1F, 1F, 2F, 2F),
+        )
+        val allHingeBounds = listOf(
+            Rect(0F, 0F, 1F, 1F),
+            Rect(1F, 1F, 2F, 2F),
+            Rect(2F, 2F, 3F, 3F)
+        )
+        val layoutDirective = calculateDenseAdaptiveLayoutDirective(
+            WindowAdaptiveInfo(
+                WindowSizeClass.calculateFromSize(DpSize(700.dp, 800.dp)),
+                Posture(
+                    allHingeBounds = allHingeBounds,
+                    occludingHingeBounds = occludingHingeBounds
+                )
+            ),
+            HingePolicy.AlwaysAvoid
+        )
+
+        assertThat(layoutDirective.excludedBounds).isEqualTo(allHingeBounds)
+    }
+
+    @Test
+    fun test_calculateDenseAdaptiveLayoutDirective_avoidOccludingHinge() {
+        val occludingHingeBounds = listOf(
+            Rect(0F, 0F, 1F, 1F),
+            Rect(1F, 1F, 2F, 2F),
+        )
+        val allHingeBounds = listOf(
+            Rect(0F, 0F, 1F, 1F),
+            Rect(1F, 1F, 2F, 2F),
+            Rect(2F, 2F, 3F, 3F)
+        )
+        val layoutDirective = calculateDenseAdaptiveLayoutDirective(
+            WindowAdaptiveInfo(
+                WindowSizeClass.calculateFromSize(DpSize(700.dp, 800.dp)),
+                Posture(
+                    allHingeBounds = allHingeBounds,
+                    occludingHingeBounds = occludingHingeBounds
+                )
+            ),
+            HingePolicy.AvoidOccluding
+        )
+
+        assertThat(layoutDirective.excludedBounds).isEqualTo(occludingHingeBounds)
+    }
+
+    @Test
+    fun test_calculateDenseAdaptiveLayoutDirective_neverAvoidHinge() {
+        val occludingHingeBounds = listOf(
+            Rect(0F, 0F, 1F, 1F),
+            Rect(1F, 1F, 2F, 2F),
+        )
+        val allHingeBounds = listOf(
+            Rect(0F, 0F, 1F, 1F),
+            Rect(1F, 1F, 2F, 2F),
+            Rect(2F, 2F, 3F, 3F)
+        )
+        val layoutDirective = calculateDenseAdaptiveLayoutDirective(
+            WindowAdaptiveInfo(
+                WindowSizeClass.calculateFromSize(DpSize(700.dp, 800.dp)),
+                Posture(
+                    allHingeBounds = allHingeBounds,
+                    occludingHingeBounds = occludingHingeBounds
+                )
+            ),
+            HingePolicy.NeverAvoid
+        )
+
+        assertThat(layoutDirective.excludedBounds).isEmpty()
+    }
 }
diff --git a/compose/material3/material3-lint/src/main/java/androidx/compose/material3/lint/MaterialImportDetector.kt b/compose/material3/material3-lint/src/main/java/androidx/compose/material3/lint/MaterialImportDetector.kt
index d53535b..33c73a5 100644
--- a/compose/material3/material3-lint/src/main/java/androidx/compose/material3/lint/MaterialImportDetector.kt
+++ b/compose/material3/material3-lint/src/main/java/androidx/compose/material3/lint/MaterialImportDetector.kt
@@ -45,17 +45,22 @@
             val reference = node.importReference ?: return
             val importString = reference.asSourceString()
 
-            // Ignore non-material imports
-            if (!importString.contains(MaterialPackage)) return
-            // Ignore explicitly allowed imports
-            if (AllowlistedSubpackages.any { importString.contains(it) }) return
+            if (
+                // Wildcard reference - so the import string is exactly androidx.compose.material
+                importString == MaterialPackage ||
+                // The prefix is androidx.compose.material - ignore material3* and other prefixes
+                importString.contains("$MaterialPackage.")
+            ) {
+                // Ignore explicitly allowed imports
+                if (AllowlistedSubpackages.any { importString.contains(it) }) return
 
-            context.report(
-                UsingMaterialAndMaterial3Libraries,
-                reference,
-                context.getLocation(reference),
-                "Using a material import while also using the material3 library"
-            )
+                context.report(
+                    UsingMaterialAndMaterial3Libraries,
+                    reference,
+                    context.getLocation(reference),
+                    "Using a material import while also using the material3 library"
+                )
+            }
         }
     }
 
diff --git a/compose/material3/material3-lint/src/test/java/androidx/compose/material3/lint/MaterialImportDetectorTest.kt b/compose/material3/material3-lint/src/test/java/androidx/compose/material3/lint/MaterialImportDetectorTest.kt
index 969eebf..065d628 100644
--- a/compose/material3/material3-lint/src/test/java/androidx/compose/material3/lint/MaterialImportDetectorTest.kt
+++ b/compose/material3/material3-lint/src/test/java/androidx/compose/material3/lint/MaterialImportDetectorTest.kt
@@ -38,7 +38,7 @@
     override fun getIssues(): MutableList<Issue> =
         mutableListOf(MaterialImportDetector.UsingMaterialAndMaterial3Libraries)
 
-    private val ButtonStub = bytecodeStub(
+    private val MaterialButtonStub = bytecodeStub(
         filename = "Button.kt",
         filepath = "androidx/compose/material",
         checksum = 0x94880e7a,
@@ -65,6 +65,34 @@
         """
     )
 
+    private val Material3ButtonStub = bytecodeStub(
+        filename = "Button.kt",
+        filepath = "androidx/compose/material3",
+        checksum = 0x8bce80e4,
+        """
+            package androidx.compose.material3
+
+            fun Button() {}
+        """,
+"""
+        META-INF/main.kotlin_module:
+        H4sIAAAAAAAA/2NgYGBmYGBgBGIOBijgUuWSTMxLKcrPTKnQS87PLcgvTtXL
+        TSxJLcpMzBHicCotKcnP8y7hMuNSxalMr6A0J6coNa0otThDiDcAyAmCcID6
+        dLgUcOsryiwoyEkV4ggC00DValxSOFUbI7mGl4s5LT9fiC0ktbjEu0SJQYsB
+        ANpB3LXcAAAA
+        """,
+        """
+        androidx/compose/material3/ButtonKt.class:
+        H4sIAAAAAAAA/yVOTU/CQBB9s4UC9YMifpWrF71YUG+e1MSkETVRw4XTQjdm
+        ge6adks48pe8ejCc/VHGXTuHN2/ee5OZn9+vbwBXiAgnXKW5lukqnursQxci
+        zrgRueSLy/i2NEarB9MAEcIZX/J4wdV7/DyZialVPYJfZQje6dmI0BnOtVlI
+        FT8Kw1Nu+DWBZUvPXiMHTQcg0NwRZs2VdKxvWTogdDdrP9isAxaynh9u1j3W
+        J2ddkNtqVcfO54ZQu9OpILSHUomnMpuI/I1PFlYJXnWZT8W9dEP0UiojMzGS
+        hbTujVLacCO1KjAAQw3VOxHq8G0/sFOEqujz3zq06CzYMMOR+xr7OLZ9YNWG
+        XWyO4SVoJQgSbGE7wQ52E7QRjkEFOtgbgxWoF+j+AX2HBh54AQAA
+        """
+    )
+
     private val RippleStub = bytecodeStub(
         filename = "Ripple.kt",
         filepath = "androidx/compose/material/ripple",
@@ -152,13 +180,14 @@
         )
 
     @Test
-    fun imports() {
+    fun material_imports() {
         lint().files(
             kotlin(
                 """
                 package foo
 
                 import androidx.compose.material.Button
+                import androidx.compose.material3.Button
                 import androidx.compose.material.ripple.rememberRipple
                 import androidx.compose.material.icons.Icons
                 import androidx.compose.material.pullrefresh.pullRefresh
@@ -166,7 +195,8 @@
                 fun test() {}
             """
             ),
-            ButtonStub,
+            MaterialButtonStub,
+            Material3ButtonStub,
             RippleStub,
             IconsStub,
             PullRefreshStub
@@ -183,13 +213,14 @@
     }
 
     @Test
-    fun wildcardImports() {
+    fun material_wildcardImports() {
         lint().files(
             kotlin(
                 """
                 package foo
 
                 import androidx.compose.material.*
+                import androidx.compose.material3.*
                 import androidx.compose.material.ripple.*
                 import androidx.compose.material.icons.*
                 import androidx.compose.material.pullrefresh.*
@@ -197,7 +228,8 @@
                 fun test() {}
             """
             ),
-            ButtonStub,
+            MaterialButtonStub,
+            Material3ButtonStub,
             RippleStub,
             IconsStub,
             PullRefreshStub
diff --git a/compose/material3/material3-window-size-class/build.gradle b/compose/material3/material3-window-size-class/build.gradle
index 4d4f79c..76cbc23 100644
--- a/compose/material3/material3-window-size-class/build.gradle
+++ b/compose/material3/material3-window-size-class/build.gradle
@@ -114,7 +114,6 @@
         if (desktopEnabled) {
             desktopTest {
                 dependsOn(jvmTest)
-                dependsOn(desktopMain)
                 dependencies {
 
                 }
diff --git a/compose/material3/material3/api/current.txt b/compose/material3/material3/api/current.txt
index 3f6cc31..310ebce 100644
--- a/compose/material3/material3/api/current.txt
+++ b/compose/material3/material3/api/current.txt
@@ -225,7 +225,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class CheckboxColors {
-    ctor public CheckboxColors(long checkedCheckmarkColor, long uncheckedCheckmarkColor, long checkedBoxColor, long uncheckedBoxColor, long disabledCheckedBoxColor, long disabledUncheckedBoxColor, long disabledIndeterminateBoxColor, long checkedBorderColor, long uncheckedBorderColor, long disabledBorderColor, long disabledIndeterminateBorderColor);
+    ctor public CheckboxColors(long checkedCheckmarkColor, long uncheckedCheckmarkColor, long checkedBoxColor, long uncheckedBoxColor, long disabledCheckedBoxColor, long disabledUncheckedBoxColor, long disabledIndeterminateBoxColor, long checkedBorderColor, long uncheckedBorderColor, long disabledBorderColor, long disabledUncheckedBorderColor, long disabledIndeterminateBorderColor);
     method public long getCheckedBorderColor();
     method public long getCheckedBoxColor();
     method public long getCheckedCheckmarkColor();
@@ -233,6 +233,7 @@
     method public long getDisabledCheckedBoxColor();
     method public long getDisabledIndeterminateBorderColor();
     method public long getDisabledIndeterminateBoxColor();
+    method public long getDisabledUncheckedBorderColor();
     method public long getDisabledUncheckedBoxColor();
     method public long getUncheckedBorderColor();
     method public long getUncheckedBoxColor();
@@ -244,6 +245,7 @@
     property public final long disabledCheckedBoxColor;
     property public final long disabledIndeterminateBorderColor;
     property public final long disabledIndeterminateBoxColor;
+    property public final long disabledUncheckedBorderColor;
     property public final long disabledUncheckedBoxColor;
     property public final long uncheckedBorderColor;
     property public final long uncheckedBoxColor;
@@ -530,7 +532,7 @@
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public final class DismissState {
-    ctor public DismissState(androidx.compose.material3.DismissValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.DismissValue,java.lang.Boolean> confirmValueChange, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super java.lang.Float,java.lang.Float> positionalThreshold);
+    ctor @Deprecated public DismissState(androidx.compose.material3.DismissValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.DismissValue,java.lang.Boolean> confirmValueChange, kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float> positionalThreshold);
     method public suspend Object? dismiss(androidx.compose.material3.DismissDirection direction, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public androidx.compose.material3.DismissValue getCurrentValue();
     method public androidx.compose.material3.DismissDirection? getDismissDirection();
@@ -548,7 +550,8 @@
   }
 
   public static final class DismissState.Companion {
-    method public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.DismissState,androidx.compose.material3.DismissValue> Saver(kotlin.jvm.functions.Function1<? super androidx.compose.material3.DismissValue,java.lang.Boolean> confirmValueChange, kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super java.lang.Float,java.lang.Float> positionalThreshold);
+    method @Deprecated public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.DismissState,androidx.compose.material3.DismissValue> Saver(kotlin.jvm.functions.Function1<? super androidx.compose.material3.DismissValue,java.lang.Boolean> confirmValueChange, kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float> positionalThreshold);
+    method public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.DismissState,androidx.compose.material3.DismissValue> Saver(kotlin.jvm.functions.Function1<? super androidx.compose.material3.DismissValue,java.lang.Boolean> confirmValueChange, kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float> positionalThreshold, androidx.compose.ui.unit.Density density);
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public enum DismissValue {
@@ -907,6 +910,9 @@
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static androidx.compose.material3.SheetState rememberModalBottomSheetState(optional boolean skipPartiallyExpanded, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange);
   }
 
+  @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public interface MultiChoiceSegmentedButtonRowScope extends androidx.compose.foundation.layout.RowScope {
+  }
+
   public final class NavigationBarDefaults {
     method @androidx.compose.runtime.Composable public long getContainerColor();
     method public float getElevation();
@@ -1091,7 +1097,7 @@
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class RangeSliderState {
-    ctor public RangeSliderState(optional float initialActiveRangeStart, optional float initialActiveRangeEnd, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.FloatRange,kotlin.Unit>? initialOnValueChange, optional int steps, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished);
+    ctor public RangeSliderState(optional float initialActiveRangeStart, optional float initialActiveRangeEnd, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.FloatRange,kotlin.Unit>? initialOnValueChange, optional @IntRange(from=0L) int steps, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished);
     method public float getActiveRangeEnd();
     method public float getActiveRangeStart();
     method public kotlin.jvm.functions.Function0<kotlin.Unit>? getOnValueChangeFinished();
@@ -1179,35 +1185,37 @@
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Immutable public final class SegmentedButtonColors {
-    ctor public SegmentedButtonColors(long checkedContainerColor, long checkedContentColor, long checkedBorderColor, long uncheckedContainerColor, long uncheckedContentColor, long uncheckedBorderColor, long disabledCheckedContainerColor, long disabledCheckedContentColor, long disabledCheckedBorderColor, long disabledUncheckedContainerColor, long disabledUncheckedContentColor, long disabledUncheckedBorderColor);
-    method public long getCheckedBorderColor();
-    method public long getCheckedContainerColor();
-    method public long getCheckedContentColor();
-    method public long getDisabledCheckedBorderColor();
-    method public long getDisabledCheckedContainerColor();
-    method public long getDisabledCheckedContentColor();
-    method public long getDisabledUncheckedBorderColor();
-    method public long getDisabledUncheckedContainerColor();
-    method public long getDisabledUncheckedContentColor();
-    method public long getUncheckedBorderColor();
-    method public long getUncheckedContainerColor();
-    method public long getUncheckedContentColor();
-    property public final long checkedBorderColor;
-    property public final long checkedContainerColor;
-    property public final long checkedContentColor;
-    property public final long disabledCheckedBorderColor;
-    property public final long disabledCheckedContainerColor;
-    property public final long disabledCheckedContentColor;
-    property public final long disabledUncheckedBorderColor;
-    property public final long disabledUncheckedContainerColor;
-    property public final long disabledUncheckedContentColor;
-    property public final long uncheckedBorderColor;
-    property public final long uncheckedContainerColor;
-    property public final long uncheckedContentColor;
+    ctor public SegmentedButtonColors(long activeContainerColor, long activeContentColor, long activeBorderColor, long inactiveContainerColor, long inactiveContentColor, long inactiveBorderColor, long disabledActiveContainerColor, long disabledActiveContentColor, long disabledActiveBorderColor, long disabledInactiveContainerColor, long disabledInactiveContentColor, long disabledInactiveBorderColor);
+    method public long getActiveBorderColor();
+    method public long getActiveContainerColor();
+    method public long getActiveContentColor();
+    method public long getDisabledActiveBorderColor();
+    method public long getDisabledActiveContainerColor();
+    method public long getDisabledActiveContentColor();
+    method public long getDisabledInactiveBorderColor();
+    method public long getDisabledInactiveContainerColor();
+    method public long getDisabledInactiveContentColor();
+    method public long getInactiveBorderColor();
+    method public long getInactiveContainerColor();
+    method public long getInactiveContentColor();
+    property public final long activeBorderColor;
+    property public final long activeContainerColor;
+    property public final long activeContentColor;
+    property public final long disabledActiveBorderColor;
+    property public final long disabledActiveContainerColor;
+    property public final long disabledActiveContentColor;
+    property public final long disabledInactiveBorderColor;
+    property public final long disabledInactiveContainerColor;
+    property public final long disabledInactiveContentColor;
+    property public final long inactiveBorderColor;
+    property public final long inactiveContainerColor;
+    property public final long inactiveContentColor;
   }
 
-  @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public final class SegmentedButtonDefaults {
-    method @androidx.compose.runtime.Composable public androidx.compose.material3.SegmentedButtonColors colors(optional long checkedContainerColor, optional long checkedContentColor, optional long checkedBorderColor, optional long uncheckedContainerColor, optional long uncheckedContentColor, optional long uncheckedBorderColor, optional long disabledCheckedContainerColor, optional long disabledCheckedContentColor, optional long disabledCheckedBorderColor, optional long disabledUncheckedContainerColor, optional long disabledUncheckedContentColor, optional long disabledUncheckedBorderColor);
+  @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class SegmentedButtonDefaults {
+    method @androidx.compose.runtime.Composable public void ActiveIcon();
+    method @androidx.compose.runtime.Composable public void SegmentedButtonIcon(boolean active, optional kotlin.jvm.functions.Function0<kotlin.Unit> activeContent, optional kotlin.jvm.functions.Function0<kotlin.Unit>? inactiveContent);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.SegmentedButtonColors colors(optional long activeContainerColor, optional long activeContentColor, optional long activeBorderColor, optional long inactiveContainerColor, optional long inactiveContentColor, optional long inactiveBorderColor, optional long disabledActiveContainerColor, optional long disabledActiveContentColor, optional long disabledActiveBorderColor, optional long disabledInactiveContainerColor, optional long disabledInactiveContentColor, optional long disabledInactiveBorderColor);
     method public androidx.compose.material3.SegmentedButtonBorder getBorder();
     method public float getIconSize();
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.foundation.shape.CornerBasedShape getShape();
@@ -1219,8 +1227,10 @@
   }
 
   public final class SegmentedButtonKt {
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void SegmentedButton(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.ui.graphics.Shape shape, optional androidx.compose.material3.SegmentedButtonColors colors, optional androidx.compose.material3.SegmentedButtonBorder border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void SegmentedButtonRow(optional androidx.compose.ui.Modifier modifier, optional float space, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void MultiChoiceSegmentedButtonRow(optional androidx.compose.ui.Modifier modifier, optional float space, kotlin.jvm.functions.Function1<? super androidx.compose.material3.MultiChoiceSegmentedButtonRowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void SegmentedButton(androidx.compose.material3.MultiChoiceSegmentedButtonRowScope, 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.ui.graphics.Shape shape, optional androidx.compose.material3.SegmentedButtonColors colors, optional androidx.compose.material3.SegmentedButtonBorder border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional kotlin.jvm.functions.Function0<kotlin.Unit> icon, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void SegmentedButton(androidx.compose.material3.SingleChoiceSegmentedButtonRowScope, boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.SegmentedButtonColors colors, optional androidx.compose.material3.SegmentedButtonBorder border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional kotlin.jvm.functions.Function0<kotlin.Unit> icon, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void SingleChoiceSegmentedButtonRow(optional androidx.compose.ui.Modifier modifier, optional float space, kotlin.jvm.functions.Function1<? super androidx.compose.material3.SingleChoiceSegmentedButtonRowScope,kotlin.Unit> content);
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Immutable public final class SelectableChipBorder {
@@ -1279,8 +1289,12 @@
     property public final androidx.compose.foundation.shape.CornerBasedShape small;
   }
 
+  public final class SheetDefaultsKt {
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public static androidx.compose.material3.SheetState SheetState(boolean skipPartiallyExpanded, androidx.compose.ui.unit.Density density, optional androidx.compose.material3.SheetValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange, optional boolean skipHiddenState);
+  }
+
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class SheetState {
-    ctor public SheetState(boolean skipPartiallyExpanded, optional androidx.compose.material3.SheetValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange, optional boolean skipHiddenState);
+    ctor @Deprecated public SheetState(boolean skipPartiallyExpanded, optional androidx.compose.material3.SheetValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange, optional boolean skipHiddenState);
     method public suspend Object? expand(kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public androidx.compose.material3.SheetValue getCurrentValue();
     method public boolean getHasExpandedState();
@@ -1300,7 +1314,8 @@
   }
 
   public static final class SheetState.Companion {
-    method public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.SheetState,androidx.compose.material3.SheetValue> Saver(boolean skipPartiallyExpanded, kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange);
+    method @Deprecated public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.SheetState,androidx.compose.material3.SheetValue> Saver(boolean skipPartiallyExpanded, kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange);
+    method public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.SheetState,androidx.compose.material3.SheetValue> Saver(boolean skipPartiallyExpanded, kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange, androidx.compose.ui.unit.Density density);
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public enum SheetValue {
@@ -1311,6 +1326,9 @@
     enum_constant public static final androidx.compose.material3.SheetValue PartiallyExpanded;
   }
 
+  @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public interface SingleChoiceSegmentedButtonRowScope extends androidx.compose.foundation.layout.RowScope {
+  }
+
   @androidx.compose.runtime.Immutable public final class SliderColors {
     ctor public SliderColors(long thumbColor, long activeTrackColor, long activeTickColor, long inactiveTrackColor, long inactiveTickColor, long disabledThumbColor, long disabledActiveTrackColor, long disabledActiveTickColor, long disabledInactiveTrackColor, long disabledInactiveTickColor);
     method public long getActiveTickColor();
@@ -1346,11 +1364,11 @@
 
   public final class SliderKt {
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void RangeSlider(androidx.compose.material3.RangeSliderState state, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource startInteractionSource, optional androidx.compose.foundation.interaction.MutableInteractionSource endInteractionSource, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.RangeSliderState,kotlin.Unit> startThumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.RangeSliderState,kotlin.Unit> endThumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.RangeSliderState,kotlin.Unit> track);
-    method @androidx.compose.runtime.Composable public static void RangeSlider(kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> value, kotlin.jvm.functions.Function1<? super kotlin.ranges.ClosedFloatingPointRange<java.lang.Float>,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void RangeSlider(long value, kotlin.jvm.functions.Function1<? super androidx.compose.material3.FloatRange,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource startInteractionSource, optional androidx.compose.foundation.interaction.MutableInteractionSource endInteractionSource, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.RangeSliderState,kotlin.Unit> startThumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.RangeSliderState,kotlin.Unit> endThumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.RangeSliderState,kotlin.Unit> track, optional int steps);
+    method @androidx.compose.runtime.Composable public static void RangeSlider(kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> value, kotlin.jvm.functions.Function1<? super kotlin.ranges.ClosedFloatingPointRange<java.lang.Float>,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional @IntRange(from=0L) int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void RangeSlider(long value, kotlin.jvm.functions.Function1<? super androidx.compose.material3.FloatRange,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource startInteractionSource, optional androidx.compose.foundation.interaction.MutableInteractionSource endInteractionSource, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.RangeSliderState,kotlin.Unit> startThumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.RangeSliderState,kotlin.Unit> endThumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.RangeSliderState,kotlin.Unit> track, optional @IntRange(from=0L) int steps);
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void Slider(androidx.compose.material3.SliderState state, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderState,kotlin.Unit> thumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderState,kotlin.Unit> track);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void Slider(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional int steps, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderState,kotlin.Unit> thumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderState,kotlin.Unit> track, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange);
-    method @androidx.compose.runtime.Composable public static void Slider(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void Slider(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional @IntRange(from=0L) int steps, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderState,kotlin.Unit> thumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderState,kotlin.Unit> track, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange);
+    method @androidx.compose.runtime.Composable public static void Slider(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional @IntRange(from=0L) int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
   }
 
   @androidx.compose.runtime.Stable public final class SliderPositions {
@@ -1362,7 +1380,7 @@
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class SliderState {
-    ctor public SliderState(optional float initialValue, optional kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit>? initialOnValueChange, optional int steps, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished);
+    ctor public SliderState(optional float initialValue, optional kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit>? initialOnValueChange, optional @IntRange(from=0L) int steps, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished);
     method public kotlin.jvm.functions.Function0<kotlin.Unit>? getOnValueChangeFinished();
     method public int getSteps();
     method public float getValue();
@@ -1466,14 +1484,15 @@
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public final class SwipeToDismissDefaults {
-    method public kotlin.jvm.functions.Function2<androidx.compose.ui.unit.Density,java.lang.Float,java.lang.Float> getFixedPositionalThreshold();
-    property public final kotlin.jvm.functions.Function2<androidx.compose.ui.unit.Density,java.lang.Float,java.lang.Float> FixedPositionalThreshold;
+    method @androidx.compose.runtime.Composable public kotlin.jvm.functions.Function1<java.lang.Float,java.lang.Float> getFixedPositionalThreshold();
+    property @androidx.compose.runtime.Composable public final kotlin.jvm.functions.Function1<java.lang.Float,java.lang.Float> fixedPositionalThreshold;
     field public static final androidx.compose.material3.SwipeToDismissDefaults INSTANCE;
   }
 
   public final class SwipeToDismissKt {
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public static androidx.compose.material3.DismissState DismissState(androidx.compose.material3.DismissValue initialValue, androidx.compose.ui.unit.Density density, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.DismissValue,java.lang.Boolean> confirmValueChange, kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float> positionalThreshold);
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void SwipeToDismiss(androidx.compose.material3.DismissState state, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> background, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> dismissContent, optional androidx.compose.ui.Modifier modifier, optional java.util.Set<? extends androidx.compose.material3.DismissDirection> directions);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static androidx.compose.material3.DismissState rememberDismissState(optional androidx.compose.material3.DismissValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.DismissValue,java.lang.Boolean> confirmValueChange, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super java.lang.Float,java.lang.Float> positionalThreshold);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static androidx.compose.material3.DismissState rememberDismissState(optional androidx.compose.material3.DismissValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.DismissValue,java.lang.Boolean> confirmValueChange, optional kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float> positionalThreshold);
   }
 
   @androidx.compose.runtime.Immutable public final class SwitchColors {
diff --git a/compose/material3/material3/api/restricted_current.txt b/compose/material3/material3/api/restricted_current.txt
index 3f6cc31..310ebce 100644
--- a/compose/material3/material3/api/restricted_current.txt
+++ b/compose/material3/material3/api/restricted_current.txt
@@ -225,7 +225,7 @@
   }
 
   @androidx.compose.runtime.Immutable public final class CheckboxColors {
-    ctor public CheckboxColors(long checkedCheckmarkColor, long uncheckedCheckmarkColor, long checkedBoxColor, long uncheckedBoxColor, long disabledCheckedBoxColor, long disabledUncheckedBoxColor, long disabledIndeterminateBoxColor, long checkedBorderColor, long uncheckedBorderColor, long disabledBorderColor, long disabledIndeterminateBorderColor);
+    ctor public CheckboxColors(long checkedCheckmarkColor, long uncheckedCheckmarkColor, long checkedBoxColor, long uncheckedBoxColor, long disabledCheckedBoxColor, long disabledUncheckedBoxColor, long disabledIndeterminateBoxColor, long checkedBorderColor, long uncheckedBorderColor, long disabledBorderColor, long disabledUncheckedBorderColor, long disabledIndeterminateBorderColor);
     method public long getCheckedBorderColor();
     method public long getCheckedBoxColor();
     method public long getCheckedCheckmarkColor();
@@ -233,6 +233,7 @@
     method public long getDisabledCheckedBoxColor();
     method public long getDisabledIndeterminateBorderColor();
     method public long getDisabledIndeterminateBoxColor();
+    method public long getDisabledUncheckedBorderColor();
     method public long getDisabledUncheckedBoxColor();
     method public long getUncheckedBorderColor();
     method public long getUncheckedBoxColor();
@@ -244,6 +245,7 @@
     property public final long disabledCheckedBoxColor;
     property public final long disabledIndeterminateBorderColor;
     property public final long disabledIndeterminateBoxColor;
+    property public final long disabledUncheckedBorderColor;
     property public final long disabledUncheckedBoxColor;
     property public final long uncheckedBorderColor;
     property public final long uncheckedBoxColor;
@@ -530,7 +532,7 @@
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public final class DismissState {
-    ctor public DismissState(androidx.compose.material3.DismissValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.DismissValue,java.lang.Boolean> confirmValueChange, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super java.lang.Float,java.lang.Float> positionalThreshold);
+    ctor @Deprecated public DismissState(androidx.compose.material3.DismissValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.DismissValue,java.lang.Boolean> confirmValueChange, kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float> positionalThreshold);
     method public suspend Object? dismiss(androidx.compose.material3.DismissDirection direction, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public androidx.compose.material3.DismissValue getCurrentValue();
     method public androidx.compose.material3.DismissDirection? getDismissDirection();
@@ -548,7 +550,8 @@
   }
 
   public static final class DismissState.Companion {
-    method public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.DismissState,androidx.compose.material3.DismissValue> Saver(kotlin.jvm.functions.Function1<? super androidx.compose.material3.DismissValue,java.lang.Boolean> confirmValueChange, kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super java.lang.Float,java.lang.Float> positionalThreshold);
+    method @Deprecated public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.DismissState,androidx.compose.material3.DismissValue> Saver(kotlin.jvm.functions.Function1<? super androidx.compose.material3.DismissValue,java.lang.Boolean> confirmValueChange, kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float> positionalThreshold);
+    method public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.DismissState,androidx.compose.material3.DismissValue> Saver(kotlin.jvm.functions.Function1<? super androidx.compose.material3.DismissValue,java.lang.Boolean> confirmValueChange, kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float> positionalThreshold, androidx.compose.ui.unit.Density density);
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public enum DismissValue {
@@ -907,6 +910,9 @@
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static androidx.compose.material3.SheetState rememberModalBottomSheetState(optional boolean skipPartiallyExpanded, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange);
   }
 
+  @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public interface MultiChoiceSegmentedButtonRowScope extends androidx.compose.foundation.layout.RowScope {
+  }
+
   public final class NavigationBarDefaults {
     method @androidx.compose.runtime.Composable public long getContainerColor();
     method public float getElevation();
@@ -1091,7 +1097,7 @@
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class RangeSliderState {
-    ctor public RangeSliderState(optional float initialActiveRangeStart, optional float initialActiveRangeEnd, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.FloatRange,kotlin.Unit>? initialOnValueChange, optional int steps, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished);
+    ctor public RangeSliderState(optional float initialActiveRangeStart, optional float initialActiveRangeEnd, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.FloatRange,kotlin.Unit>? initialOnValueChange, optional @IntRange(from=0L) int steps, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished);
     method public float getActiveRangeEnd();
     method public float getActiveRangeStart();
     method public kotlin.jvm.functions.Function0<kotlin.Unit>? getOnValueChangeFinished();
@@ -1179,35 +1185,37 @@
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Immutable public final class SegmentedButtonColors {
-    ctor public SegmentedButtonColors(long checkedContainerColor, long checkedContentColor, long checkedBorderColor, long uncheckedContainerColor, long uncheckedContentColor, long uncheckedBorderColor, long disabledCheckedContainerColor, long disabledCheckedContentColor, long disabledCheckedBorderColor, long disabledUncheckedContainerColor, long disabledUncheckedContentColor, long disabledUncheckedBorderColor);
-    method public long getCheckedBorderColor();
-    method public long getCheckedContainerColor();
-    method public long getCheckedContentColor();
-    method public long getDisabledCheckedBorderColor();
-    method public long getDisabledCheckedContainerColor();
-    method public long getDisabledCheckedContentColor();
-    method public long getDisabledUncheckedBorderColor();
-    method public long getDisabledUncheckedContainerColor();
-    method public long getDisabledUncheckedContentColor();
-    method public long getUncheckedBorderColor();
-    method public long getUncheckedContainerColor();
-    method public long getUncheckedContentColor();
-    property public final long checkedBorderColor;
-    property public final long checkedContainerColor;
-    property public final long checkedContentColor;
-    property public final long disabledCheckedBorderColor;
-    property public final long disabledCheckedContainerColor;
-    property public final long disabledCheckedContentColor;
-    property public final long disabledUncheckedBorderColor;
-    property public final long disabledUncheckedContainerColor;
-    property public final long disabledUncheckedContentColor;
-    property public final long uncheckedBorderColor;
-    property public final long uncheckedContainerColor;
-    property public final long uncheckedContentColor;
+    ctor public SegmentedButtonColors(long activeContainerColor, long activeContentColor, long activeBorderColor, long inactiveContainerColor, long inactiveContentColor, long inactiveBorderColor, long disabledActiveContainerColor, long disabledActiveContentColor, long disabledActiveBorderColor, long disabledInactiveContainerColor, long disabledInactiveContentColor, long disabledInactiveBorderColor);
+    method public long getActiveBorderColor();
+    method public long getActiveContainerColor();
+    method public long getActiveContentColor();
+    method public long getDisabledActiveBorderColor();
+    method public long getDisabledActiveContainerColor();
+    method public long getDisabledActiveContentColor();
+    method public long getDisabledInactiveBorderColor();
+    method public long getDisabledInactiveContainerColor();
+    method public long getDisabledInactiveContentColor();
+    method public long getInactiveBorderColor();
+    method public long getInactiveContainerColor();
+    method public long getInactiveContentColor();
+    property public final long activeBorderColor;
+    property public final long activeContainerColor;
+    property public final long activeContentColor;
+    property public final long disabledActiveBorderColor;
+    property public final long disabledActiveContainerColor;
+    property public final long disabledActiveContentColor;
+    property public final long disabledInactiveBorderColor;
+    property public final long disabledInactiveContainerColor;
+    property public final long disabledInactiveContentColor;
+    property public final long inactiveBorderColor;
+    property public final long inactiveContainerColor;
+    property public final long inactiveContentColor;
   }
 
-  @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public final class SegmentedButtonDefaults {
-    method @androidx.compose.runtime.Composable public androidx.compose.material3.SegmentedButtonColors colors(optional long checkedContainerColor, optional long checkedContentColor, optional long checkedBorderColor, optional long uncheckedContainerColor, optional long uncheckedContentColor, optional long uncheckedBorderColor, optional long disabledCheckedContainerColor, optional long disabledCheckedContentColor, optional long disabledCheckedBorderColor, optional long disabledUncheckedContainerColor, optional long disabledUncheckedContentColor, optional long disabledUncheckedBorderColor);
+  @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class SegmentedButtonDefaults {
+    method @androidx.compose.runtime.Composable public void ActiveIcon();
+    method @androidx.compose.runtime.Composable public void SegmentedButtonIcon(boolean active, optional kotlin.jvm.functions.Function0<kotlin.Unit> activeContent, optional kotlin.jvm.functions.Function0<kotlin.Unit>? inactiveContent);
+    method @androidx.compose.runtime.Composable public androidx.compose.material3.SegmentedButtonColors colors(optional long activeContainerColor, optional long activeContentColor, optional long activeBorderColor, optional long inactiveContainerColor, optional long inactiveContentColor, optional long inactiveBorderColor, optional long disabledActiveContainerColor, optional long disabledActiveContentColor, optional long disabledActiveBorderColor, optional long disabledInactiveContainerColor, optional long disabledInactiveContentColor, optional long disabledInactiveBorderColor);
     method public androidx.compose.material3.SegmentedButtonBorder getBorder();
     method public float getIconSize();
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.compose.foundation.shape.CornerBasedShape getShape();
@@ -1219,8 +1227,10 @@
   }
 
   public final class SegmentedButtonKt {
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void SegmentedButton(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.ui.graphics.Shape shape, optional androidx.compose.material3.SegmentedButtonColors colors, optional androidx.compose.material3.SegmentedButtonBorder border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void SegmentedButtonRow(optional androidx.compose.ui.Modifier modifier, optional float space, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void MultiChoiceSegmentedButtonRow(optional androidx.compose.ui.Modifier modifier, optional float space, kotlin.jvm.functions.Function1<? super androidx.compose.material3.MultiChoiceSegmentedButtonRowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void SegmentedButton(androidx.compose.material3.MultiChoiceSegmentedButtonRowScope, 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.ui.graphics.Shape shape, optional androidx.compose.material3.SegmentedButtonColors colors, optional androidx.compose.material3.SegmentedButtonBorder border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional kotlin.jvm.functions.Function0<kotlin.Unit> icon, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void SegmentedButton(androidx.compose.material3.SingleChoiceSegmentedButtonRowScope, boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.SegmentedButtonColors colors, optional androidx.compose.material3.SegmentedButtonBorder border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional kotlin.jvm.functions.Function0<kotlin.Unit> icon, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void SingleChoiceSegmentedButtonRow(optional androidx.compose.ui.Modifier modifier, optional float space, kotlin.jvm.functions.Function1<? super androidx.compose.material3.SingleChoiceSegmentedButtonRowScope,kotlin.Unit> content);
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Immutable public final class SelectableChipBorder {
@@ -1279,8 +1289,12 @@
     property public final androidx.compose.foundation.shape.CornerBasedShape small;
   }
 
+  public final class SheetDefaultsKt {
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public static androidx.compose.material3.SheetState SheetState(boolean skipPartiallyExpanded, androidx.compose.ui.unit.Density density, optional androidx.compose.material3.SheetValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange, optional boolean skipHiddenState);
+  }
+
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class SheetState {
-    ctor public SheetState(boolean skipPartiallyExpanded, optional androidx.compose.material3.SheetValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange, optional boolean skipHiddenState);
+    ctor @Deprecated public SheetState(boolean skipPartiallyExpanded, optional androidx.compose.material3.SheetValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange, optional boolean skipHiddenState);
     method public suspend Object? expand(kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public androidx.compose.material3.SheetValue getCurrentValue();
     method public boolean getHasExpandedState();
@@ -1300,7 +1314,8 @@
   }
 
   public static final class SheetState.Companion {
-    method public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.SheetState,androidx.compose.material3.SheetValue> Saver(boolean skipPartiallyExpanded, kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange);
+    method @Deprecated public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.SheetState,androidx.compose.material3.SheetValue> Saver(boolean skipPartiallyExpanded, kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange);
+    method public androidx.compose.runtime.saveable.Saver<androidx.compose.material3.SheetState,androidx.compose.material3.SheetValue> Saver(boolean skipPartiallyExpanded, kotlin.jvm.functions.Function1<? super androidx.compose.material3.SheetValue,java.lang.Boolean> confirmValueChange, androidx.compose.ui.unit.Density density);
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public enum SheetValue {
@@ -1311,6 +1326,9 @@
     enum_constant public static final androidx.compose.material3.SheetValue PartiallyExpanded;
   }
 
+  @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public interface SingleChoiceSegmentedButtonRowScope extends androidx.compose.foundation.layout.RowScope {
+  }
+
   @androidx.compose.runtime.Immutable public final class SliderColors {
     ctor public SliderColors(long thumbColor, long activeTrackColor, long activeTickColor, long inactiveTrackColor, long inactiveTickColor, long disabledThumbColor, long disabledActiveTrackColor, long disabledActiveTickColor, long disabledInactiveTrackColor, long disabledInactiveTickColor);
     method public long getActiveTickColor();
@@ -1346,11 +1364,11 @@
 
   public final class SliderKt {
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void RangeSlider(androidx.compose.material3.RangeSliderState state, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource startInteractionSource, optional androidx.compose.foundation.interaction.MutableInteractionSource endInteractionSource, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.RangeSliderState,kotlin.Unit> startThumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.RangeSliderState,kotlin.Unit> endThumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.RangeSliderState,kotlin.Unit> track);
-    method @androidx.compose.runtime.Composable public static void RangeSlider(kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> value, kotlin.jvm.functions.Function1<? super kotlin.ranges.ClosedFloatingPointRange<java.lang.Float>,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void RangeSlider(long value, kotlin.jvm.functions.Function1<? super androidx.compose.material3.FloatRange,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource startInteractionSource, optional androidx.compose.foundation.interaction.MutableInteractionSource endInteractionSource, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.RangeSliderState,kotlin.Unit> startThumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.RangeSliderState,kotlin.Unit> endThumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.RangeSliderState,kotlin.Unit> track, optional int steps);
+    method @androidx.compose.runtime.Composable public static void RangeSlider(kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> value, kotlin.jvm.functions.Function1<? super kotlin.ranges.ClosedFloatingPointRange<java.lang.Float>,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional @IntRange(from=0L) int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void RangeSlider(long value, kotlin.jvm.functions.Function1<? super androidx.compose.material3.FloatRange,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource startInteractionSource, optional androidx.compose.foundation.interaction.MutableInteractionSource endInteractionSource, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.RangeSliderState,kotlin.Unit> startThumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.RangeSliderState,kotlin.Unit> endThumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.RangeSliderState,kotlin.Unit> track, optional @IntRange(from=0L) int steps);
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void Slider(androidx.compose.material3.SliderState state, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderState,kotlin.Unit> thumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderState,kotlin.Unit> track);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void Slider(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional int steps, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderState,kotlin.Unit> thumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderState,kotlin.Unit> track, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange);
-    method @androidx.compose.runtime.Composable public static void Slider(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void Slider(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional @IntRange(from=0L) int steps, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderState,kotlin.Unit> thumb, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.SliderState,kotlin.Unit> track, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange);
+    method @androidx.compose.runtime.Composable public static void Slider(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional @IntRange(from=0L) int steps, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished, optional androidx.compose.material3.SliderColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
   }
 
   @androidx.compose.runtime.Stable public final class SliderPositions {
@@ -1362,7 +1380,7 @@
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class SliderState {
-    ctor public SliderState(optional float initialValue, optional kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit>? initialOnValueChange, optional int steps, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished);
+    ctor public SliderState(optional float initialValue, optional kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit>? initialOnValueChange, optional @IntRange(from=0L) int steps, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onValueChangeFinished);
     method public kotlin.jvm.functions.Function0<kotlin.Unit>? getOnValueChangeFinished();
     method public int getSteps();
     method public float getValue();
@@ -1466,14 +1484,15 @@
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public final class SwipeToDismissDefaults {
-    method public kotlin.jvm.functions.Function2<androidx.compose.ui.unit.Density,java.lang.Float,java.lang.Float> getFixedPositionalThreshold();
-    property public final kotlin.jvm.functions.Function2<androidx.compose.ui.unit.Density,java.lang.Float,java.lang.Float> FixedPositionalThreshold;
+    method @androidx.compose.runtime.Composable public kotlin.jvm.functions.Function1<java.lang.Float,java.lang.Float> getFixedPositionalThreshold();
+    property @androidx.compose.runtime.Composable public final kotlin.jvm.functions.Function1<java.lang.Float,java.lang.Float> fixedPositionalThreshold;
     field public static final androidx.compose.material3.SwipeToDismissDefaults INSTANCE;
   }
 
   public final class SwipeToDismissKt {
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public static androidx.compose.material3.DismissState DismissState(androidx.compose.material3.DismissValue initialValue, androidx.compose.ui.unit.Density density, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.DismissValue,java.lang.Boolean> confirmValueChange, kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float> positionalThreshold);
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void SwipeToDismiss(androidx.compose.material3.DismissState state, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> background, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> dismissContent, optional androidx.compose.ui.Modifier modifier, optional java.util.Set<? extends androidx.compose.material3.DismissDirection> directions);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static androidx.compose.material3.DismissState rememberDismissState(optional androidx.compose.material3.DismissValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.DismissValue,java.lang.Boolean> confirmValueChange, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.unit.Density,? super java.lang.Float,java.lang.Float> positionalThreshold);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static androidx.compose.material3.DismissState rememberDismissState(optional androidx.compose.material3.DismissValue initialValue, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.DismissValue,java.lang.Boolean> confirmValueChange, optional kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Float> positionalThreshold);
   }
 
   @androidx.compose.runtime.Immutable public final class SwitchColors {
diff --git a/compose/material3/material3/build.gradle b/compose/material3/material3/build.gradle
index e9f6e1c..fe8552b 100644
--- a/compose/material3/material3/build.gradle
+++ b/compose/material3/material3/build.gradle
@@ -42,6 +42,7 @@
                 implementation(project(":compose:animation:animation-core"))
 
                 api(project(":compose:foundation:foundation"))
+                api(project(":compose:foundation:foundation-layout"))
                 api(project(":compose:material:material-icons-core"))
                 api(project(":compose:material:material-ripple"))
                 api(project(":compose:runtime:runtime"))
@@ -49,7 +50,6 @@
                 api(project(":compose:ui:ui-text"))
 
                 implementation(project(":compose:ui:ui-util"))
-                implementation(project(":compose:foundation:foundation-layout"))
             }
         }
 
@@ -72,8 +72,9 @@
                     api(project(":compose:runtime:runtime"))
                     api(project(":compose:ui:ui"))
                     api(project(":compose:ui:ui-text"))
+                    api(project(":compose:foundation:foundation-layout"))
+
                     implementation(project(":compose:animation:animation"))
-                    implementation(project(":compose:foundation:foundation-layout"))
                     implementation(project(":compose:ui:ui-util"))
                 }
             }
@@ -145,7 +146,6 @@
         if (desktopEnabled) {
             desktopTest {
                 dependsOn(jvmTest)
-                dependsOn(desktopMain)
                 dependencies {
                     implementation(project(":compose:ui:ui-test-junit4"))
                     implementation(libs.truth)
@@ -158,6 +158,7 @@
 }
 
 dependencies {
+    lintChecks project(":compose:material3:material3-lint")
     lintPublish project(":compose:material3:material3-lint")
 }
 
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 445789d..3fba476 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
@@ -120,6 +120,7 @@
 import androidx.compose.material3.samples.ScrollingTextTabs
 import androidx.compose.material3.samples.SearchBarSample
 import androidx.compose.material3.samples.SecondaryTabs
+import androidx.compose.material3.samples.SegmentedButtonMultiSelectSample
 import androidx.compose.material3.samples.SegmentedButtonSingleSelectSample
 import androidx.compose.material3.samples.SimpleBottomAppBar
 import androidx.compose.material3.samples.SimpleBottomSheetScaffoldSample
@@ -800,6 +801,13 @@
     ) {
         SegmentedButtonSingleSelectSample()
     },
+    Example(
+        name = ::SegmentedButtonMultiSelectSample.name,
+        description = SegmentedButtonExampleDescription,
+        sourceUrl = SegmentedButtonSourceUrl
+    ) {
+        SegmentedButtonMultiSelectSample()
+    },
 )
 
 private const val SlidersExampleDescription = "Sliders examples"
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SearchBarSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SearchBarSamples.kt
index e5ca504..ebd1a75 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SearchBarSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SearchBarSamples.kt
@@ -42,11 +42,11 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.semantics.isContainer
+import androidx.compose.ui.semantics.isTraversalGroup
 import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.traversalIndex
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
-import androidx.compose.ui.zIndex
 
 @OptIn(ExperimentalMaterial3Api::class)
 @Preview
@@ -56,45 +56,36 @@
     var text by rememberSaveable { mutableStateOf("") }
     var active by rememberSaveable { mutableStateOf(false) }
 
-    Box(Modifier.fillMaxSize()) {
-        // Talkback focus order sorts based on x and y position before considering z-index. The
-        // extra Box with semantics and fillMaxWidth is a workaround to get the search bar to focus
-        // before the content.
-        Box(Modifier.semantics {
-            @Suppress("DEPRECATION")
-            isContainer = true
-        }.zIndex(1f).fillMaxWidth()) {
-            SearchBar(
-                modifier = Modifier.align(Alignment.TopCenter),
-                query = text,
-                onQueryChange = { text = it },
-                onSearch = { active = false },
-                active = active,
-                onActiveChange = {
-                    active = it
-                },
-                placeholder = { Text("Hinted search text") },
-                leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) },
-                trailingIcon = { Icon(Icons.Default.MoreVert, contentDescription = null) },
-            ) {
-                LazyColumn(
-                    modifier = Modifier.fillMaxWidth(),
-                    contentPadding = PaddingValues(16.dp),
-                    verticalArrangement = Arrangement.spacedBy(4.dp)
-                ) {
-                    items(4) { idx ->
-                        val resultText = "Suggestion $idx"
-                        ListItem(
-                            headlineContent = { Text(resultText) },
-                            supportingContent = { Text("Additional info") },
-                            leadingContent = { Icon(Icons.Filled.Star, contentDescription = null) },
-                            modifier = Modifier.clickable {
-                                text = resultText
-                                active = false
-                            }
-                        )
-                    }
-                }
+    Box(Modifier.fillMaxSize().semantics { isTraversalGroup = true }) {
+        SearchBar(
+            modifier = Modifier
+                .align(Alignment.TopCenter)
+                .semantics { traversalIndex = -1f },
+            query = text,
+            onQueryChange = { text = it },
+            onSearch = { active = false },
+            active = active,
+            onActiveChange = {
+                active = it
+            },
+            placeholder = { Text("Hinted search text") },
+            leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) },
+            trailingIcon = { Icon(Icons.Default.MoreVert, contentDescription = null) },
+        ) {
+            repeat(4) { idx ->
+                val resultText = "Suggestion $idx"
+                ListItem(
+                    headlineContent = { Text(resultText) },
+                    supportingContent = { Text("Additional info") },
+                    leadingContent = { Icon(Icons.Filled.Star, contentDescription = null) },
+                    modifier = Modifier
+                        .clickable {
+                            text = resultText
+                            active = false
+                        }
+                        .fillMaxWidth()
+                        .padding(horizontal = 16.dp, vertical = 4.dp)
+                )
             }
         }
 
@@ -118,43 +109,35 @@
     var text by rememberSaveable { mutableStateOf("") }
     var active by rememberSaveable { mutableStateOf(false) }
 
-    Box(Modifier.fillMaxSize()) {
-        // Talkback focus order sorts based on x and y position before considering z-index. The
-        // extra Box with semantics and fillMaxWidth is a workaround to get the search bar to focus
-        // before the content.
-        Box(Modifier.semantics {
-            @Suppress("DEPRECATION")
-            isContainer = true
-        }.zIndex(1f).fillMaxWidth()) {
-            DockedSearchBar(
-                modifier = Modifier.align(Alignment.TopCenter).padding(top = 8.dp),
-                query = text,
-                onQueryChange = { text = it },
-                onSearch = { active = false },
-                active = active,
-                onActiveChange = { active = it },
-                placeholder = { Text("Hinted search text") },
-                leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) },
-                trailingIcon = { Icon(Icons.Default.MoreVert, contentDescription = null) },
-            ) {
-                LazyColumn(
-                    modifier = Modifier.fillMaxWidth(),
-                    contentPadding = PaddingValues(16.dp),
-                    verticalArrangement = Arrangement.spacedBy(4.dp)
-                ) {
-                    items(4) { idx ->
-                        val resultText = "Suggestion $idx"
-                        ListItem(
-                            headlineContent = { Text(resultText) },
-                            supportingContent = { Text("Additional info") },
-                            leadingContent = { Icon(Icons.Filled.Star, contentDescription = null) },
-                            modifier = Modifier.clickable {
-                                text = resultText
-                                active = false
-                            }
-                        )
-                    }
-                }
+    Box(Modifier.fillMaxSize().semantics { isTraversalGroup = true }) {
+        DockedSearchBar(
+            modifier = Modifier
+                .align(Alignment.TopCenter)
+                .padding(top = 8.dp)
+                .semantics { traversalIndex = -1f },
+            query = text,
+            onQueryChange = { text = it },
+            onSearch = { active = false },
+            active = active,
+            onActiveChange = { active = it },
+            placeholder = { Text("Hinted search text") },
+            leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) },
+            trailingIcon = { Icon(Icons.Default.MoreVert, contentDescription = null) },
+        ) {
+            repeat(4) { idx ->
+                val resultText = "Suggestion $idx"
+                ListItem(
+                    headlineContent = { Text(resultText) },
+                    supportingContent = { Text("Additional info") },
+                    leadingContent = { Icon(Icons.Filled.Star, contentDescription = null) },
+                    modifier = Modifier
+                        .clickable {
+                            text = resultText
+                            active = false
+                        }
+                        .fillMaxWidth()
+                        .padding(horizontal = 16.dp, vertical = 4.dp)
+                )
             }
         }
 
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SegmentedButtonSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SegmentedButtonSamples.kt
index cb5896e..0ac189c 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SegmentedButtonSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SegmentedButtonSamples.kt
@@ -17,16 +17,25 @@
 package androidx.compose.material3.samples
 
 import androidx.annotation.Sampled
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.Icon
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.BookmarkBorder
+import androidx.compose.material.icons.filled.StarBorder
+import androidx.compose.material.icons.filled.TrendingUp
 import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.MultiChoiceSegmentedButtonRow
 import androidx.compose.material3.SegmentedButton
 import androidx.compose.material3.SegmentedButtonDefaults
-import androidx.compose.material3.SegmentedButtonRow
+import androidx.compose.material3.SingleChoiceSegmentedButtonRow
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateListOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
 import androidx.compose.ui.tooling.preview.Preview
 
 @OptIn(ExperimentalMaterial3Api::class)
@@ -34,14 +43,54 @@
 @Composable
 @Preview
 fun SegmentedButtonSingleSelectSample() {
-    var checkedIndex by remember { mutableStateOf(0) }
-    val options = listOf("$", "$$", "$$$")
-    SegmentedButtonRow {
+    var selectedIndex by remember { mutableStateOf(0) }
+    val options = listOf("Day", "Month", "Week")
+    SingleChoiceSegmentedButtonRow {
         options.forEachIndexed { index, label ->
             SegmentedButton(
                 shape = SegmentedButtonDefaults.shape(position = index, count = options.size),
-                onCheckedChange = { checkedIndex = index },
-                checked = index == checkedIndex
+                onClick = { selectedIndex = index },
+                selected = index == selectedIndex
+            ) {
+                Text(label)
+            }
+        }
+    }
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Sampled
+@Composable
+@Preview
+fun SegmentedButtonMultiSelectSample() {
+    val checkedList = remember { mutableStateListOf<Int>() }
+    val options = listOf("Favorites", "Trending", "Saved")
+    val icons = listOf(
+        Icons.Filled.StarBorder,
+        Icons.Filled.TrendingUp,
+        Icons.Filled.BookmarkBorder
+    )
+    MultiChoiceSegmentedButtonRow {
+        options.forEachIndexed { index, label ->
+            SegmentedButton(
+                shape = SegmentedButtonDefaults.shape(position = index, count = options.size),
+                icon = {
+                    SegmentedButtonDefaults.SegmentedButtonIcon(active = index in checkedList) {
+                        Icon(
+                            imageVector = icons[index],
+                            contentDescription = null,
+                            modifier = Modifier.size(SegmentedButtonDefaults.IconSize)
+                        )
+                    }
+                },
+                onCheckedChange = {
+                    if (index in checkedList) {
+                        checkedList.remove(index)
+                    } else {
+                        checkedList.add(index)
+                    }
+                },
+                checked = index in checkedList
             ) {
                 Text(label)
             }
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TimePickerSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TimePickerSamples.kt
index 3a38e5e..5ee9ff0f 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TimePickerSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/TimePickerSamples.kt
@@ -23,11 +23,9 @@
 import androidx.compose.foundation.layout.IntrinsicSize
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
-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.layout.size
 import androidx.compose.foundation.layout.width
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.outlined.Keyboard
@@ -54,13 +52,10 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalConfiguration
-import androidx.compose.ui.semantics.isContainer
-import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.window.Dialog
 import androidx.compose.ui.window.DialogProperties
-import androidx.compose.ui.zIndex
 import java.text.SimpleDateFormat
 import java.util.Calendar
 import java.util.Locale
@@ -81,7 +76,9 @@
         Button(
             modifier = Modifier.align(Alignment.Center),
             onClick = { showTimePicker = true }
-        ) { Text("Set Time") }
+        ) {
+            Text("Set Time")
+        }
         SnackbarHost(hostState = snackState)
     }
 
@@ -119,7 +116,9 @@
         Button(
             modifier = Modifier.align(Alignment.Center),
             onClick = { showTimePicker = true }
-        ) { Text("Set Time") }
+        ) {
+            Text("Set Time")
+        }
         SnackbarHost(hostState = snackState)
     }
 
@@ -159,13 +158,19 @@
         Button(
             modifier = Modifier.align(Alignment.Center),
             onClick = { showTimePicker = true }
-        ) { Text("Set Time") }
+        ) {
+            Text("Set Time")
+        }
         SnackbarHost(hostState = snackState)
     }
 
     if (showTimePicker) {
         TimePickerDialog(
-            title = if (showingPicker.value) { "Select Time " } else { "Enter Time" },
+            title = if (showingPicker.value) {
+                "Select Time "
+            } else {
+                "Enter Time"
+            },
             onCancel = { showTimePicker = false },
             onConfirm = {
                 val cal = Calendar.getInstance()
@@ -179,40 +184,20 @@
             },
             toggle = {
                 if (configuration.screenHeightDp > 400) {
-                    // Make this take the entire viewport. This will guarantee that Screen readers
-                    // focus the toggle first.
-                    Box(
-                        Modifier
-                            .fillMaxSize()
-                        .semantics {
-                            @Suppress("DEPRECATION")
-                            isContainer = true
+                    IconButton(onClick = { showingPicker.value = !showingPicker.value }) {
+                        val icon = if (showingPicker.value) {
+                            Icons.Outlined.Keyboard
+                        } else {
+                            Icons.Outlined.Schedule
                         }
-                    ) {
-                        IconButton(
-                            modifier = Modifier
-                                // This is a workaround so that the Icon comes up first
-                                // in the talkback traversal order. So that users of a11y
-                                // services can use the text input. When talkback traversal
-                                // order is customizable we can remove this.
-                                .size(64.dp, 72.dp)
-                                .align(Alignment.BottomStart)
-                                .zIndex(5f),
-                            onClick = { showingPicker.value = !showingPicker.value }) {
-                            val icon = if (showingPicker.value) {
-                                Icons.Outlined.Keyboard
+                        Icon(
+                            icon,
+                            contentDescription = if (showingPicker.value) {
+                                "Switch to Text Input"
                             } else {
-                                Icons.Outlined.Schedule
+                                "Switch to Touch Input"
                             }
-                            Icon(
-                                icon,
-                                contentDescription = if (showingPicker.value) {
-                                    "Switch to Text Input"
-                                } else {
-                                    "Switch to Touch Input"
-                                }
-                            )
-                        }
+                        )
                     }
                 }
             }
@@ -236,9 +221,7 @@
 ) {
     Dialog(
         onDismissRequest = onCancel,
-        properties = DialogProperties(
-            usePlatformDefaultWidth = false
-        ),
+        properties = DialogProperties(usePlatformDefaultWidth = false),
     ) {
         Surface(
             shape = MaterialTheme.shapes.extraLarge,
@@ -251,7 +234,6 @@
                     color = MaterialTheme.colorScheme.surface
                 ),
         ) {
-            toggle()
             Column(
                 modifier = Modifier.padding(24.dp),
                 horizontalAlignment = Alignment.CenterHorizontally
@@ -264,18 +246,18 @@
                     style = MaterialTheme.typography.labelMedium
                 )
                 content()
-                Row(
-                    modifier = Modifier
-                        .height(40.dp)
-                        .fillMaxWidth()
+                Row(modifier = Modifier
+                    .height(40.dp)
+                    .fillMaxWidth()
                 ) {
+                    toggle()
                     Spacer(modifier = Modifier.weight(1f))
-                    TextButton(
-                        onClick = onCancel
-                    ) { Text("Cancel") }
-                    TextButton(
-                        onClick = onConfirm
-                    ) { Text("OK") }
+                    TextButton(onClick = onCancel) {
+                        Text("Cancel")
+                    }
+                    TextButton(onClick = onConfirm) {
+                        Text("OK")
+                    }
                 }
             }
         }
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/AlertDialogTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/AlertDialogTest.kt
index d3ed41d..5950e2c 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/AlertDialogTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/AlertDialogTest.kt
@@ -92,7 +92,7 @@
                     TextButton(onClick = { /* doSomething() */ }) {
                         Text("Confirm")
                         buttonContentColor = LocalContentColor.current
-                        expectedButtonContentColor = DialogTokens.ActionLabelTextColor.toColor()
+                        expectedButtonContentColor = DialogTokens.ActionLabelTextColor.value
                     }
                 },
                 containerColor = Color.Yellow,
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/AppBarTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/AppBarTest.kt
index 80d43c8..ef6ffe4 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/AppBarTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/AppBarTest.kt
@@ -1435,7 +1435,7 @@
             scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
             // Using the mediumTopAppBarColors for both Medium and Large top app bars, as the
             // current content color settings are the same.
-            expandedAppBarBackgroundColor = TopAppBarMediumTokens.ContainerColor.toColor()
+            expandedAppBarBackgroundColor = TopAppBarMediumTokens.ContainerColor.value
             fullyCollapsedContainerColor =
                 TopAppBarDefaults.mediumTopAppBarColors()
                     .containerColor(colorTransitionFraction = 1f)
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/BadgeTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/BadgeTest.kt
index 21d3299..7805580 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/BadgeTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/BadgeTest.kt
@@ -115,8 +115,8 @@
         var shape = RectangleShape
         var errorColor = Color.Unspecified
         rule.setMaterialContent(lightColorScheme()) {
-            shape = BadgeTokens.Shape.toShape()
-            errorColor = BadgeTokens.Color.toColor()
+            shape = BadgeTokens.Shape.value
+            errorColor = BadgeTokens.Color.value
             Badge(modifier = Modifier.testTag(TestBadgeTag))
         }
 
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/BottomSheetScaffoldTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/BottomSheetScaffoldTest.kt
index 53beeb5..6735b7d 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/BottomSheetScaffoldTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/BottomSheetScaffoldTest.kt
@@ -253,6 +253,7 @@
             skipPartiallyExpanded = false,
             skipHiddenState = true,
             initialValue = SheetValue.PartiallyExpanded,
+            density = rule.density
         )
         rule.setContent {
             scope = rememberCoroutineScope()
@@ -772,7 +773,7 @@
 
         rule.setContent {
             dragHandleContentDescription = getString(Strings.BottomSheetDragHandleDescription)
-            dragHandleColor = SheetBottomTokens.DockedDragHandleColor.toColor()
+            dragHandleColor = SheetBottomTokens.DockedDragHandleColor.value
                 .copy(SheetBottomTokens.DockedDragHandleOpacity)
             surface = MaterialTheme.colorScheme.surface
             density = LocalDensity.current
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/CheckboxScreenshotTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/CheckboxScreenshotTest.kt
index 07f862f..a09e2a3 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/CheckboxScreenshotTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/CheckboxScreenshotTest.kt
@@ -16,8 +16,11 @@
 package androidx.compose.material3
 
 import android.os.Build
+import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.runtime.Composable
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.testutils.assertAgainstGolden
 import androidx.compose.ui.Alignment
@@ -25,6 +28,7 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.input.InputMode
 import androidx.compose.ui.input.InputModeManager
 import androidx.compose.ui.platform.LocalInputModeManager
@@ -37,6 +41,7 @@
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.performMouseInput
 import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.unit.dp
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
 import androidx.test.screenshot.AndroidXScreenshotTestRule
@@ -187,7 +192,7 @@
 
         rule.mainClock.advanceTimeByFrame()
         rule.waitForIdle() // Wait for measure
-        rule.mainClock.advanceTimeBy(milliseconds = 80)
+        rule.mainClock.advanceTimeBy(milliseconds = 100)
 
         assertToggeableAgainstGolden("checkBox_${scheme.name}_unchecked_animateToChecked")
     }
@@ -216,7 +221,7 @@
 
         rule.mainClock.advanceTimeByFrame()
         rule.waitForIdle() // Wait for measure
-        rule.mainClock.advanceTimeBy(milliseconds = 80)
+        rule.mainClock.advanceTimeBy(milliseconds = 100)
 
         assertToggeableAgainstGolden("checkBox_${scheme.name}_checked_animateToUnchecked")
     }
@@ -269,6 +274,107 @@
         assertToggeableAgainstGolden("checkBox_${scheme.name}_focus")
     }
 
+    @Test
+    fun checkBox_customColors() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            Column(wrap.testTag(wrapperTestTag), verticalArrangement = Arrangement.spacedBy(2.dp)) {
+                val colors = CheckboxDefaults.colors(
+                    checkedColor = Color.Red,
+                    uncheckedColor = Color.Gray,
+                    checkmarkColor = Color.Green,
+                    disabledCheckedColor = Color.Red.copy(alpha = 0.38f),
+                    disabledUncheckedColor = Color.Gray.copy(alpha = 0.38f),
+                    disabledIndeterminateColor = Color.Magenta.copy(alpha = 0.38f)
+                )
+                Checkboxes(colors = colors)
+            }
+        }
+
+        rule.waitForIdle()
+
+        assertToggeableAgainstGolden("checkBox_${scheme.name}_customColors")
+    }
+
+    @Test
+    fun checkBox_customCheckboxColorsConstruct() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            Column(wrap.testTag(wrapperTestTag), verticalArrangement = Arrangement.spacedBy(2.dp)) {
+                val colors = CheckboxColors(
+                    checkedCheckmarkColor = Color.Black,
+                    // Irrelevant for the test, as this color only appears when the check mark
+                    // transitions from checked to unchecked.
+                    uncheckedCheckmarkColor = Color.Transparent,
+                    checkedBoxColor = Color.Green,
+                    uncheckedBoxColor = Color.Yellow,
+                    disabledCheckedBoxColor = Color.Green.copy(alpha = 0.38f),
+                    disabledUncheckedBoxColor = Color.Yellow.copy(alpha = 0.38f),
+                    disabledIndeterminateBoxColor = Color.Magenta.copy(alpha = 0.38f),
+                    checkedBorderColor = Color.Red,
+                    uncheckedBorderColor = Color.Black,
+                    disabledBorderColor = Color.Red.copy(alpha = 0.38f),
+                    disabledUncheckedBorderColor = Color.Blue,
+                    disabledIndeterminateBorderColor = Color.LightGray
+                )
+                Checkboxes(colors = colors)
+            }
+        }
+
+        rule.waitForIdle()
+
+        assertToggeableAgainstGolden("checkBox_${scheme.name}_customCheckboxColorsConstruct")
+    }
+
+    @Composable
+    private fun Checkboxes(colors: CheckboxColors) {
+        TriStateCheckbox(state = ToggleableState.Off, onClick = { }, colors = colors)
+        TriStateCheckbox(
+            state = ToggleableState.Off,
+            onClick = { },
+            enabled = false,
+            colors = colors
+        )
+        TriStateCheckbox(state = ToggleableState.On, onClick = { }, colors = colors)
+        TriStateCheckbox(
+            state = ToggleableState.On,
+            onClick = { },
+            enabled = false,
+            colors = colors
+        )
+        TriStateCheckbox(
+            state = ToggleableState.Indeterminate,
+            onClick = { },
+            colors = colors
+        )
+        TriStateCheckbox(
+            state = ToggleableState.Indeterminate,
+            onClick = { },
+            enabled = false,
+            colors = colors
+        )
+        Checkbox(
+            checked = false,
+            onCheckedChange = { },
+            colors = colors
+        )
+        Checkbox(
+            checked = false,
+            onCheckedChange = { },
+            enabled = false,
+            colors = colors
+        )
+        Checkbox(
+            checked = true,
+            onCheckedChange = { },
+            colors = colors
+        )
+        Checkbox(
+            checked = true,
+            onCheckedChange = { },
+            enabled = false,
+            colors = colors
+        )
+    }
+
     private fun assertToggeableAgainstGolden(goldenName: String) {
         // TODO: replace with find(isToggeable()) after b/157687898 is fixed
         rule.onNodeWithTag(wrapperTestTag)
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
index 19cef5d..697685b9 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ChipTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ChipTest.kt
@@ -272,7 +272,7 @@
         var expectedLabelColor = Color.Unspecified
         var contentColor = Color.Unspecified
         rule.setMaterialContent(lightColorScheme()) {
-            expectedLabelColor = AssistChipTokens.LabelTextColor.toColor()
+            expectedLabelColor = AssistChipTokens.LabelTextColor.value
             AssistChip(onClick = {}, label = {
                 contentColor = LocalContentColor.current
             })
@@ -289,7 +289,7 @@
     fun elevatedDisabled_assistChip() {
         var containerColor = Color.Unspecified
         rule.setMaterialContent(lightColorScheme()) {
-            containerColor = AssistChipTokens.ElevatedDisabledContainerColor.toColor()
+            containerColor = AssistChipTokens.ElevatedDisabledContainerColor.value
                 .copy(alpha = AssistChipTokens.ElevatedDisabledContainerOpacity)
                 .compositeOver(MaterialTheme.colorScheme.surface)
             ElevatedAssistChip(
@@ -562,7 +562,7 @@
         var expectedLabelColor = Color.Unspecified
         var contentColor = Color.Unspecified
         rule.setMaterialContent(lightColorScheme()) {
-            expectedLabelColor = FilterChipTokens.UnselectedLabelTextColor.toColor()
+            expectedLabelColor = FilterChipTokens.UnselectedLabelTextColor.value
             FilterChip(selected = false, onClick = {}, label = {
                 contentColor = LocalContentColor.current
             })
@@ -578,7 +578,7 @@
         var expectedLabelColor = Color.Unspecified
         var contentColor = Color.Unspecified
         rule.setMaterialContent(lightColorScheme()) {
-            expectedLabelColor = FilterChipTokens.SelectedLabelTextColor.toColor()
+            expectedLabelColor = FilterChipTokens.SelectedLabelTextColor.value
             FilterChip(selected = true, onClick = {}, label = {
                 contentColor = LocalContentColor.current
             })
@@ -807,8 +807,8 @@
         var contentColor = Color.Unspecified
         rule.setMaterialContent(lightColorScheme()) {
             val selected = remember { mutableStateOf(false) }
-            selectedLabelColor = InputChipTokens.SelectedLabelTextColor.toColor()
-            unselectedLabelColor = InputChipTokens.UnselectedLabelTextColor.toColor()
+            selectedLabelColor = InputChipTokens.SelectedLabelTextColor.value
+            unselectedLabelColor = InputChipTokens.UnselectedLabelTextColor.value
             InputChip(
                 selected = selected.value,
                 onClick = { selected.value = !selected.value },
@@ -967,7 +967,7 @@
         var expectedLabelColor = Color.Unspecified
         var contentColor = Color.Unspecified
         rule.setMaterialContent(lightColorScheme()) {
-            expectedLabelColor = SuggestionChipTokens.LabelTextColor.toColor()
+            expectedLabelColor = SuggestionChipTokens.LabelTextColor.value
             SuggestionChip(onClick = {}, label = {
                 contentColor = LocalContentColor.current
             })
@@ -984,7 +984,7 @@
     fun elevatedDisabled_suggestionChip() {
         var containerColor = Color.Unspecified
         rule.setMaterialContent(lightColorScheme()) {
-            containerColor = SuggestionChipTokens.ElevatedDisabledContainerColor.toColor()
+            containerColor = SuggestionChipTokens.ElevatedDisabledContainerColor.value
                 .copy(alpha = SuggestionChipTokens.ElevatedDisabledContainerOpacity)
                 .compositeOver(MaterialTheme.colorScheme.surface)
             ElevatedSuggestionChip(
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ModalBottomSheetTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ModalBottomSheetTest.kt
index 8a34a90..a3fcc88 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ModalBottomSheetTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ModalBottomSheetTest.kt
@@ -107,7 +107,7 @@
     @Test
     fun modalBottomSheet_isDismissedOnTapOutside() {
         var showBottomSheet by mutableStateOf(true)
-        val sheetState = SheetState(skipPartiallyExpanded = false)
+        val sheetState = SheetState(skipPartiallyExpanded = false, density = rule.density)
 
         rule.setContent {
             val windowInsets = if (edgeToEdgeWrapper.edgeToEdgeEnabled)
@@ -294,7 +294,7 @@
     @Test
     fun modalBottomSheet_shortSheet_isDismissedOnBackPress() {
         var showBottomSheet by mutableStateOf(true)
-        val sheetState = SheetState(skipPartiallyExpanded = true)
+        val sheetState = SheetState(skipPartiallyExpanded = true, density = rule.density)
 
         rule.setContent {
             val dispatcher = LocalOnBackPressedDispatcherOwner.current!!.onBackPressedDispatcher
@@ -333,7 +333,7 @@
     @Test
     fun modalBottomSheet_tallSheet_isDismissedOnBackPress() {
         var showBottomSheet by mutableStateOf(true)
-        val sheetState = SheetState(skipPartiallyExpanded = false)
+        val sheetState = SheetState(skipPartiallyExpanded = false, density = rule.density)
 
         rule.setContent {
             val dispatcher = LocalOnBackPressedDispatcherOwner.current!!.onBackPressedDispatcher
@@ -565,7 +565,7 @@
     fun modalBottomSheet_missingAnchors_findsClosest() {
         val topTag = "ModalBottomSheetLayout"
         var showShortContent by mutableStateOf(false)
-        val sheetState = SheetState(skipPartiallyExpanded = false)
+        val sheetState = SheetState(skipPartiallyExpanded = false, density = rule.density)
         lateinit var scope: CoroutineScope
 
         rule.setContent {
@@ -809,6 +809,7 @@
         lateinit var scope: CoroutineScope
         val bottomSheetState = SheetState(
             skipPartiallyExpanded = true,
+            density = rule.density
         )
         rule.setContent {
             scope = rememberCoroutineScope()
@@ -1000,7 +1001,7 @@
 
     @Test
     fun modalBottomSheet_shortSheet_anchorChangeHandler_previousTargetNotInAnchors_reconciles() {
-        val sheetState = SheetState(skipPartiallyExpanded = false)
+        val sheetState = SheetState(skipPartiallyExpanded = false, density = rule.density)
         var hasSheetContent by mutableStateOf(false) // Start out with empty sheet content
         lateinit var scope: CoroutineScope
         rule.setContent {
@@ -1039,7 +1040,7 @@
 
     @Test
     fun modalBottomSheet_tallSheet_anchorChangeHandler_previousTargetNotInAnchors_reconciles() {
-        val sheetState = SheetState(skipPartiallyExpanded = false)
+        val sheetState = SheetState(skipPartiallyExpanded = false, density = rule.density)
         var hasSheetContent by mutableStateOf(false) // Start out with empty sheet content
         lateinit var scope: CoroutineScope
         rule.setContent {
@@ -1080,7 +1081,7 @@
     fun modalBottomSheet_callsOnDismissRequest_onNestedScrollFling() {
         var callCount by mutableStateOf(0)
         val expectedCallCount = 1
-        val sheetState = SheetState(skipPartiallyExpanded = true)
+        val sheetState = SheetState(skipPartiallyExpanded = true, density = rule.density)
 
         val nestedScrollDispatcher = NestedScrollDispatcher()
         val nestedScrollConnection = object : NestedScrollConnection {
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SearchBarTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SearchBarTest.kt
index 88f33e2..89721c6 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SearchBarTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SearchBarTest.kt
@@ -28,6 +28,8 @@
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.node.Ref
 import androidx.compose.ui.platform.testTag
@@ -99,6 +101,38 @@
     }
 
     @Test
+    fun searchBar_doesNotOverwriteFocusOfOtherComponents() {
+        val focusRequester = FocusRequester()
+        rule.setMaterialContent(lightColorScheme()) {
+            Column(Modifier.fillMaxSize()) {
+                SearchBar(
+                    modifier = Modifier.testTag(SearchBarTestTag),
+                    query = "Query",
+                    onQueryChange = {},
+                    onSearch = {},
+                    active = false,
+                    onActiveChange = {},
+                    content = {},
+                )
+                TextField(
+                    value = "",
+                    onValueChange = {},
+                    modifier = Modifier.testTag("SIBLING").focusRequester(focusRequester)
+                )
+            }
+        }
+
+        rule.runOnIdle {
+            focusRequester.requestFocus()
+        }
+
+        rule.onNodeWithTag("SIBLING").assertIsFocused()
+
+        rule.onNodeWithTag(SearchBarTestTag).performClick()
+        rule.onNodeWithText("Query").assertIsFocused()
+    }
+
+    @Test
     fun searchBar_onImeAction_executesSearchCallback() {
         var capturedSearchQuery = ""
 
@@ -246,6 +280,38 @@
     }
 
     @Test
+    fun dockedSearchBar_doesNotOverwriteFocusOfOtherComponents() {
+        val focusRequester = FocusRequester()
+        rule.setMaterialContent(lightColorScheme()) {
+            Column(Modifier.fillMaxSize()) {
+                DockedSearchBar(
+                    modifier = Modifier.testTag(SearchBarTestTag),
+                    query = "Query",
+                    onQueryChange = {},
+                    onSearch = {},
+                    active = false,
+                    onActiveChange = {},
+                    content = {},
+                )
+                TextField(
+                    value = "",
+                    onValueChange = {},
+                    modifier = Modifier.testTag("SIBLING").focusRequester(focusRequester)
+                )
+            }
+        }
+
+        rule.runOnIdle {
+            focusRequester.requestFocus()
+        }
+
+        rule.onNodeWithTag("SIBLING").assertIsFocused()
+
+        rule.onNodeWithTag(SearchBarTestTag).performClick()
+        rule.onNodeWithText("Query").assertIsFocused()
+    }
+
+    @Test
     fun dockedSearchBar_onImeAction_executesSearchCallback() {
         var capturedSearchQuery = ""
 
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SegmentedButtonScreenshotTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SegmentedButtonScreenshotTest.kt
index be7c546..89ae9dd 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SegmentedButtonScreenshotTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SegmentedButtonScreenshotTest.kt
@@ -17,6 +17,9 @@
 package androidx.compose.material3
 
 import android.os.Build
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Favorite
 import androidx.compose.testutils.assertAgainstGolden
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
@@ -48,7 +51,7 @@
     @Test
     fun all_unselected() {
         rule.setMaterialContent(lightColorScheme()) {
-            SegmentedButtonRow(modifier = Modifier.testTag(testTag)) {
+            MultiChoiceSegmentedButtonRow(modifier = Modifier.testTag(testTag)) {
                 values.forEach {
                     SegmentedButton(checked = false, onCheckedChange = {}) {
                         Text(it)
@@ -63,7 +66,7 @@
     @Test
     fun all_selected() {
         rule.setMaterialContent(lightColorScheme()) {
-            SegmentedButtonRow(modifier = Modifier.testTag(testTag)) {
+            MultiChoiceSegmentedButtonRow(modifier = Modifier.testTag(testTag)) {
                 values.forEach {
                     SegmentedButton(checked = true, onCheckedChange = {}) {
                         Text(it)
@@ -78,7 +81,7 @@
     @Test
     fun middle_selected() {
         rule.setMaterialContent(lightColorScheme()) {
-            SegmentedButtonRow(modifier = Modifier.testTag(testTag)) {
+            MultiChoiceSegmentedButtonRow(modifier = Modifier.testTag(testTag)) {
                 values.forEachIndexed { index, item ->
                     SegmentedButton(checked = index == 1, onCheckedChange = {}) {
                         Text(item)
@@ -91,13 +94,42 @@
     }
 
     @Test
+    fun middle_selected_with_icon() {
+        rule.setMaterialContent(lightColorScheme()) {
+            MultiChoiceSegmentedButtonRow(modifier = Modifier.testTag(testTag)) {
+                values.forEachIndexed { index, item ->
+                    SegmentedButton(
+                        checked = index == 1,
+                        onCheckedChange = {},
+                        icon = if (index == 1) {
+                            { SegmentedButtonDefaults.ActiveIcon() }
+                        } else {
+                            {
+                                Icon(
+                                    imageVector = Icons.Outlined.Favorite,
+                                    contentDescription = null,
+                                    modifier = Modifier.size(SegmentedButtonDefaults.IconSize)
+                                )
+                            }
+                        }
+                    ) {
+                        Text(item)
+                    }
+                }
+            }
+        }
+
+        assertButtonAgainstGolden("middle_selected_with_icon")
+    }
+
+    @Test
     fun stroke_zIndex() {
         rule.setMaterialContent(lightColorScheme()) {
             val colors = SegmentedButtonDefaults.colors(
-                checkedBorderColor = Color.Blue,
-                uncheckedBorderColor = Color.Yellow
+                activeBorderColor = Color.Blue,
+                inactiveBorderColor = Color.Yellow
             )
-            SegmentedButtonRow(modifier = Modifier.testTag(testTag)) {
+            MultiChoiceSegmentedButtonRow(modifier = Modifier.testTag(testTag)) {
                 values.forEachIndexed { index, item ->
                     SegmentedButton(
                         checked = index == 1,
@@ -117,10 +149,10 @@
     fun button_shape() {
         rule.setMaterialContent(lightColorScheme()) {
             val colors = SegmentedButtonDefaults.colors(
-                checkedBorderColor = Color.Blue,
-                uncheckedBorderColor = Color.Yellow
+                activeBorderColor = Color.Blue,
+                inactiveBorderColor = Color.Yellow
             )
-            SegmentedButtonRow(modifier = Modifier.testTag(testTag)) {
+            MultiChoiceSegmentedButtonRow(modifier = Modifier.testTag(testTag)) {
                 values.forEachIndexed { index, item ->
                     val shape = SegmentedButtonDefaults.shape(index, values.size)
 
@@ -142,7 +174,7 @@
     @Test
     fun all_unselected_darkTheme() {
         rule.setMaterialContent(darkColorScheme()) {
-            SegmentedButtonRow(modifier = Modifier.testTag(testTag)) {
+            MultiChoiceSegmentedButtonRow(modifier = Modifier.testTag(testTag)) {
                 values.forEach {
                     SegmentedButton(checked = false, onCheckedChange = {}) {
                         Text(it)
@@ -157,7 +189,7 @@
     @Test
     fun all_selected_darkTheme() {
         rule.setMaterialContent(darkColorScheme()) {
-            SegmentedButtonRow(modifier = Modifier.testTag(testTag)) {
+            MultiChoiceSegmentedButtonRow(modifier = Modifier.testTag(testTag)) {
                 values.forEach {
                     SegmentedButton(checked = true, onCheckedChange = {}) {
                         Text(it)
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SegmentedButtonTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SegmentedButtonTest.kt
index abb6ba0..37c5862 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SegmentedButtonTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/SegmentedButtonTest.kt
@@ -53,11 +53,11 @@
     val rule = createComposeRule()
 
     @Test
-    fun segmentedButton_itemsDisplay() {
+    fun toggleableSegmentedButton_itemsDisplay() {
         val values = listOf("Day", "Month", "Week")
 
         rule.setMaterialContent(lightColorScheme()) {
-            SegmentedButtonRow {
+            MultiChoiceSegmentedButtonRow {
                 values.forEach {
                     SegmentedButton(checked = false, onCheckedChange = {}) {
                         Text(it)
@@ -70,10 +70,27 @@
     }
 
     @Test
+    fun selectableSegmentedButton_itemsDisplay() {
+        val values = listOf("Day", "Month", "Week")
+
+        rule.setMaterialContent(lightColorScheme()) {
+            SingleChoiceSegmentedButtonRow {
+                values.forEach {
+                    SegmentedButton(selected = false, onClick = {}) {
+                        Text(it)
+                    }
+                }
+            }
+        }
+
+        values.forEach { rule.onNodeWithText(it).assertIsDisplayed() }
+    }
+
+    @Test
     fun segmentedButton_itemsChecked() {
         var checked by mutableStateOf(true)
         rule.setMaterialContent(lightColorScheme()) {
-            SegmentedButtonRow {
+            MultiChoiceSegmentedButtonRow {
                 SegmentedButton(onCheckedChange = { checked = it }, checked = checked) {
                     Text("Day")
                 }
@@ -95,13 +112,13 @@
     }
 
     @Test
-    fun segmentedButton_semantics() {
+    fun selectableSegmentedButton_semantics() {
         rule.setMaterialContent(lightColorScheme()) {
-            SegmentedButtonRow(modifier = Modifier.testTag("row")) {
-                SegmentedButton(checked = false, onCheckedChange = {}) {
+            SingleChoiceSegmentedButtonRow(modifier = Modifier.testTag("row")) {
+                SegmentedButton(selected = false, onClick = {}) {
                     Text("Day")
                 }
-                SegmentedButton(checked = false, onCheckedChange = {}) {
+                SegmentedButton(selected = false, onClick = {}) {
                     Text("Month")
                 }
             }
@@ -114,13 +131,36 @@
     }
 
     @Test
+    fun segmentedButton_icon() {
+        var checked by mutableStateOf(false)
+        rule.setMaterialContent(lightColorScheme()) {
+            MultiChoiceSegmentedButtonRow(modifier = Modifier.testTag("row")) {
+                SegmentedButton(
+                    checked = checked,
+                    onCheckedChange = {},
+                    icon = { Text(if (checked) "checked" else "unchecked") },
+                ) {
+                    Text("Day")
+                }
+            }
+        }
+
+        rule.onNodeWithText("unchecked").assertIsDisplayed()
+
+        rule.runOnIdle { checked = true }
+        rule.waitForIdle()
+
+        rule.onNodeWithText("checked").assertIsDisplayed()
+    }
+
+    @Test
     fun segmentedButton_Sizing() {
         val itemSize = 60.dp
 
         rule.setMaterialContentForSizeAssertions(
             parentMaxWidth = 300.dp, parentMaxHeight = 100.dp
         ) {
-            SegmentedButtonRow {
+            MultiChoiceSegmentedButtonRow {
                 SegmentedButton(checked = false, onCheckedChange = {}) {
                     Text(modifier = Modifier.width(60.dp), text = "Day")
                 }
@@ -138,7 +178,7 @@
         lateinit var border: BorderStroke
         var specColor: Color = Color.Unspecified
         rule.setMaterialContent(lightColorScheme()) {
-            specColor = OutlinedSegmentedButtonTokens.OutlineColor.toColor()
+            specColor = OutlinedSegmentedButtonTokens.OutlineColor.value
             border = SegmentedButtonDefaults.Border.borderStroke(
                 checked = true,
                 enabled = true,
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/AndroidAlertDialog.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/AndroidAlertDialog.android.kt
index 2fc5514..cb4c0e5 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/AndroidAlertDialog.android.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/AndroidAlertDialog.android.kt
@@ -110,7 +110,7 @@
         // most cases, TextButtons should be used for dismiss and confirm buttons.
         // TextButtons will not consume this provided content color value, and will used their
         // own defined or default colors.
-        buttonContentColor = DialogTokens.ActionLabelTextColor.toColor(),
+        buttonContentColor = DialogTokens.ActionLabelTextColor.value,
         iconContentColor = iconContentColor,
         titleContentColor = titleContentColor,
         textContentColor = textContentColor,
@@ -169,19 +169,19 @@
  */
 object AlertDialogDefaults {
     /** The default shape for alert dialogs */
-    val shape: Shape @Composable get() = DialogTokens.ContainerShape.toShape()
+    val shape: Shape @Composable get() = DialogTokens.ContainerShape.value
 
     /** The default container color for alert dialogs */
-    val containerColor: Color @Composable get() = DialogTokens.ContainerColor.toColor()
+    val containerColor: Color @Composable get() = DialogTokens.ContainerColor.value
 
     /** The default icon color for alert dialogs */
-    val iconContentColor: Color @Composable get() = DialogTokens.IconColor.toColor()
+    val iconContentColor: Color @Composable get() = DialogTokens.IconColor.value
 
     /** The default title color for alert dialogs */
-    val titleContentColor: Color @Composable get() = DialogTokens.HeadlineColor.toColor()
+    val titleContentColor: Color @Composable get() = DialogTokens.HeadlineColor.value
 
     /** The default text color for alert dialogs */
-    val textContentColor: Color @Composable get() = DialogTokens.SupportingTextColor.toColor()
+    val textContentColor: Color @Composable get() = DialogTokens.SupportingTextColor.value
 
     /** The default tonal elevation for alert dialogs */
     val TonalElevation: Dp = DialogTokens.ContainerElevation
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/DatePickerDialog.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/DatePickerDialog.android.kt
index f77adef..b374406 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/DatePickerDialog.android.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/DatePickerDialog.android.kt
@@ -95,7 +95,7 @@
                         .padding(DialogButtonsPadding)
                 ) {
                     CompositionLocalProvider(
-                        LocalContentColor provides DialogTokens.ActionLabelTextColor.toColor()
+                        LocalContentColor provides DialogTokens.ActionLabelTextColor.value
                     ) {
                         val textStyle =
                             MaterialTheme.typography.fromToken(DialogTokens.ActionLabelTextFont)
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/DynamicTonalPalette.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/DynamicTonalPalette.kt
index 231f4d5..bafe835 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/DynamicTonalPalette.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/DynamicTonalPalette.kt
@@ -20,6 +20,7 @@
 import android.os.Build
 import androidx.annotation.ColorRes
 import androidx.annotation.DoNotInline
+import androidx.annotation.FloatRange
 import androidx.annotation.RequiresApi
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.colorspace.ColorSpaces
@@ -240,7 +241,7 @@
  * @param newLuminance 0 <= newLuminance <= 100; invalid values are corrected.
  */
 internal fun Color.setLuminance(
-    /*@FloatRange(from = 0.0, to = 100.0)*/
+    @FloatRange(from = 0.0, to = 100.0)
     newLuminance: Float
 ): Color {
     if ((newLuminance < 0.0001) or (newLuminance > 99.9999)) {
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.kt
index cd1d852..3a885da 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.kt
@@ -358,67 +358,67 @@
      */
     @Composable
     fun textFieldColors(
-        focusedTextColor: Color = FilledAutocompleteTokens.FieldFocusInputTextColor.toColor(),
-        unfocusedTextColor: Color = FilledAutocompleteTokens.FieldInputTextColor.toColor(),
-        disabledTextColor: Color = FilledAutocompleteTokens.FieldDisabledInputTextColor.toColor()
+        focusedTextColor: Color = FilledAutocompleteTokens.FieldFocusInputTextColor.value,
+        unfocusedTextColor: Color = FilledAutocompleteTokens.FieldInputTextColor.value,
+        disabledTextColor: Color = FilledAutocompleteTokens.FieldDisabledInputTextColor.value
             .copy(alpha = FilledAutocompleteTokens.FieldDisabledInputTextOpacity),
-        errorTextColor: Color = FilledAutocompleteTokens.FieldErrorInputTextColor.toColor(),
-        focusedContainerColor: Color = FilledAutocompleteTokens.TextFieldContainerColor.toColor(),
-        unfocusedContainerColor: Color = FilledAutocompleteTokens.TextFieldContainerColor.toColor(),
-        disabledContainerColor: Color = FilledAutocompleteTokens.TextFieldContainerColor.toColor(),
-        errorContainerColor: Color = FilledAutocompleteTokens.TextFieldContainerColor.toColor(),
-        cursorColor: Color = FilledAutocompleteTokens.TextFieldCaretColor.toColor(),
-        errorCursorColor: Color = FilledAutocompleteTokens.TextFieldErrorFocusCaretColor.toColor(),
+        errorTextColor: Color = FilledAutocompleteTokens.FieldErrorInputTextColor.value,
+        focusedContainerColor: Color = FilledAutocompleteTokens.TextFieldContainerColor.value,
+        unfocusedContainerColor: Color = FilledAutocompleteTokens.TextFieldContainerColor.value,
+        disabledContainerColor: Color = FilledAutocompleteTokens.TextFieldContainerColor.value,
+        errorContainerColor: Color = FilledAutocompleteTokens.TextFieldContainerColor.value,
+        cursorColor: Color = FilledAutocompleteTokens.TextFieldCaretColor.value,
+        errorCursorColor: Color = FilledAutocompleteTokens.TextFieldErrorFocusCaretColor.value,
         selectionColors: TextSelectionColors = LocalTextSelectionColors.current,
         focusedIndicatorColor: Color =
-            FilledAutocompleteTokens.TextFieldFocusActiveIndicatorColor.toColor(),
+            FilledAutocompleteTokens.TextFieldFocusActiveIndicatorColor.value,
         unfocusedIndicatorColor: Color =
-            FilledAutocompleteTokens.TextFieldActiveIndicatorColor.toColor(),
+            FilledAutocompleteTokens.TextFieldActiveIndicatorColor.value,
         disabledIndicatorColor: Color =
-            FilledAutocompleteTokens.TextFieldDisabledActiveIndicatorColor.toColor()
+            FilledAutocompleteTokens.TextFieldDisabledActiveIndicatorColor.value
                 .copy(alpha = FilledAutocompleteTokens.TextFieldDisabledActiveIndicatorOpacity),
         errorIndicatorColor: Color =
-            FilledAutocompleteTokens.TextFieldErrorActiveIndicatorColor.toColor(),
+            FilledAutocompleteTokens.TextFieldErrorActiveIndicatorColor.value,
         focusedLeadingIconColor: Color =
-            FilledAutocompleteTokens.TextFieldFocusLeadingIconColor.toColor(),
+            FilledAutocompleteTokens.TextFieldFocusLeadingIconColor.value,
         unfocusedLeadingIconColor: Color =
-            FilledAutocompleteTokens.TextFieldLeadingIconColor.toColor(),
+            FilledAutocompleteTokens.TextFieldLeadingIconColor.value,
         disabledLeadingIconColor: Color =
-            FilledAutocompleteTokens.TextFieldDisabledLeadingIconColor.toColor()
+            FilledAutocompleteTokens.TextFieldDisabledLeadingIconColor.value
                 .copy(alpha = FilledAutocompleteTokens.TextFieldDisabledLeadingIconOpacity),
         errorLeadingIconColor: Color =
-            FilledAutocompleteTokens.TextFieldErrorLeadingIconColor.toColor(),
+            FilledAutocompleteTokens.TextFieldErrorLeadingIconColor.value,
         focusedTrailingIconColor: Color =
-            FilledAutocompleteTokens.TextFieldFocusTrailingIconColor.toColor(),
+            FilledAutocompleteTokens.TextFieldFocusTrailingIconColor.value,
         unfocusedTrailingIconColor: Color =
-            FilledAutocompleteTokens.TextFieldTrailingIconColor.toColor(),
+            FilledAutocompleteTokens.TextFieldTrailingIconColor.value,
         disabledTrailingIconColor: Color =
-            FilledAutocompleteTokens.TextFieldDisabledTrailingIconColor.toColor()
+            FilledAutocompleteTokens.TextFieldDisabledTrailingIconColor.value
                 .copy(alpha = FilledAutocompleteTokens.TextFieldDisabledTrailingIconOpacity),
         errorTrailingIconColor: Color =
-            FilledAutocompleteTokens.TextFieldErrorTrailingIconColor.toColor(),
-        focusedLabelColor: Color = FilledAutocompleteTokens.FieldFocusLabelTextColor.toColor(),
-        unfocusedLabelColor: Color = FilledAutocompleteTokens.FieldLabelTextColor.toColor(),
-        disabledLabelColor: Color = FilledAutocompleteTokens.FieldDisabledLabelTextColor.toColor(),
-        errorLabelColor: Color = FilledAutocompleteTokens.FieldErrorLabelTextColor.toColor(),
+            FilledAutocompleteTokens.TextFieldErrorTrailingIconColor.value,
+        focusedLabelColor: Color = FilledAutocompleteTokens.FieldFocusLabelTextColor.value,
+        unfocusedLabelColor: Color = FilledAutocompleteTokens.FieldLabelTextColor.value,
+        disabledLabelColor: Color = FilledAutocompleteTokens.FieldDisabledLabelTextColor.value,
+        errorLabelColor: Color = FilledAutocompleteTokens.FieldErrorLabelTextColor.value,
         focusedPlaceholderColor: Color =
-            FilledAutocompleteTokens.FieldSupportingTextColor.toColor(),
+            FilledAutocompleteTokens.FieldSupportingTextColor.value,
         unfocusedPlaceholderColor: Color =
-            FilledAutocompleteTokens.FieldSupportingTextColor.toColor(),
+            FilledAutocompleteTokens.FieldSupportingTextColor.value,
         disabledPlaceholderColor: Color =
-            FilledAutocompleteTokens.FieldDisabledSupportingTextColor.toColor()
+            FilledAutocompleteTokens.FieldDisabledSupportingTextColor.value
                 .copy(alpha = FilledAutocompleteTokens.FieldDisabledSupportingTextOpacity),
-        errorPlaceholderColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.toColor(),
-        focusedPrefixColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.toColor(),
-        unfocusedPrefixColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.toColor(),
+        errorPlaceholderColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.value,
+        focusedPrefixColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.value,
+        unfocusedPrefixColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.value,
         disabledPrefixColor: Color = FilledAutocompleteTokens.FieldDisabledSupportingTextColor
-            .toColor().copy(alpha = FilledAutocompleteTokens.FieldDisabledSupportingTextOpacity),
-        errorPrefixColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.toColor(),
-        focusedSuffixColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.toColor(),
-        unfocusedSuffixColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.toColor(),
+            .value.copy(alpha = FilledAutocompleteTokens.FieldDisabledSupportingTextOpacity),
+        errorPrefixColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.value,
+        focusedSuffixColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.value,
+        unfocusedSuffixColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.value,
         disabledSuffixColor: Color = FilledAutocompleteTokens.FieldDisabledSupportingTextColor
-            .toColor().copy(alpha = FilledAutocompleteTokens.FieldDisabledSupportingTextOpacity),
-        errorSuffixColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.toColor(),
+            .value.copy(alpha = FilledAutocompleteTokens.FieldDisabledSupportingTextOpacity),
+        errorSuffixColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.value,
     ): TextFieldColors =
         TextFieldDefaults.colors(
             focusedTextColor = focusedTextColor,
@@ -511,67 +511,67 @@
      */
     @Composable
     fun outlinedTextFieldColors(
-        focusedTextColor: Color = OutlinedAutocompleteTokens.FieldFocusInputTextColor.toColor(),
-        unfocusedTextColor: Color = OutlinedAutocompleteTokens.FieldInputTextColor.toColor(),
-        disabledTextColor: Color = OutlinedAutocompleteTokens.FieldDisabledInputTextColor.toColor()
+        focusedTextColor: Color = OutlinedAutocompleteTokens.FieldFocusInputTextColor.value,
+        unfocusedTextColor: Color = OutlinedAutocompleteTokens.FieldInputTextColor.value,
+        disabledTextColor: Color = OutlinedAutocompleteTokens.FieldDisabledInputTextColor.value
             .copy(alpha = OutlinedAutocompleteTokens.FieldDisabledInputTextOpacity),
-        errorTextColor: Color = OutlinedAutocompleteTokens.FieldErrorInputTextColor.toColor(),
+        errorTextColor: Color = OutlinedAutocompleteTokens.FieldErrorInputTextColor.value,
         focusedContainerColor: Color = Color.Transparent,
         unfocusedContainerColor: Color = Color.Transparent,
         disabledContainerColor: Color = Color.Transparent,
         errorContainerColor: Color = Color.Transparent,
-        cursorColor: Color = OutlinedAutocompleteTokens.TextFieldCaretColor.toColor(),
+        cursorColor: Color = OutlinedAutocompleteTokens.TextFieldCaretColor.value,
         errorCursorColor: Color =
-            OutlinedAutocompleteTokens.TextFieldErrorFocusCaretColor.toColor(),
+            OutlinedAutocompleteTokens.TextFieldErrorFocusCaretColor.value,
         selectionColors: TextSelectionColors = LocalTextSelectionColors.current,
-        focusedBorderColor: Color = OutlinedAutocompleteTokens.TextFieldFocusOutlineColor.toColor(),
-        unfocusedBorderColor: Color = OutlinedAutocompleteTokens.TextFieldOutlineColor.toColor(),
+        focusedBorderColor: Color = OutlinedAutocompleteTokens.TextFieldFocusOutlineColor.value,
+        unfocusedBorderColor: Color = OutlinedAutocompleteTokens.TextFieldOutlineColor.value,
         disabledBorderColor: Color =
-            OutlinedAutocompleteTokens.TextFieldDisabledOutlineColor.toColor()
+            OutlinedAutocompleteTokens.TextFieldDisabledOutlineColor.value
                 .copy(alpha = OutlinedAutocompleteTokens.TextFieldDisabledOutlineOpacity),
-        errorBorderColor: Color = OutlinedAutocompleteTokens.TextFieldErrorOutlineColor.toColor(),
+        errorBorderColor: Color = OutlinedAutocompleteTokens.TextFieldErrorOutlineColor.value,
         focusedLeadingIconColor: Color =
-            OutlinedAutocompleteTokens.TextFieldFocusLeadingIconColor.toColor(),
+            OutlinedAutocompleteTokens.TextFieldFocusLeadingIconColor.value,
         unfocusedLeadingIconColor: Color =
-            OutlinedAutocompleteTokens.TextFieldLeadingIconColor.toColor(),
+            OutlinedAutocompleteTokens.TextFieldLeadingIconColor.value,
         disabledLeadingIconColor: Color =
-            OutlinedAutocompleteTokens.TextFieldDisabledLeadingIconColor.toColor()
+            OutlinedAutocompleteTokens.TextFieldDisabledLeadingIconColor.value
                 .copy(alpha = OutlinedAutocompleteTokens.TextFieldDisabledLeadingIconOpacity),
         errorLeadingIconColor: Color =
-            OutlinedAutocompleteTokens.TextFieldErrorLeadingIconColor.toColor(),
+            OutlinedAutocompleteTokens.TextFieldErrorLeadingIconColor.value,
         focusedTrailingIconColor: Color =
-            OutlinedAutocompleteTokens.TextFieldFocusTrailingIconColor.toColor(),
+            OutlinedAutocompleteTokens.TextFieldFocusTrailingIconColor.value,
         unfocusedTrailingIconColor: Color =
-            OutlinedAutocompleteTokens.TextFieldTrailingIconColor.toColor(),
+            OutlinedAutocompleteTokens.TextFieldTrailingIconColor.value,
         disabledTrailingIconColor: Color =
-            OutlinedAutocompleteTokens.TextFieldDisabledTrailingIconColor.toColor()
+            OutlinedAutocompleteTokens.TextFieldDisabledTrailingIconColor.value
                 .copy(alpha = OutlinedAutocompleteTokens.TextFieldDisabledTrailingIconOpacity),
         errorTrailingIconColor: Color =
-            OutlinedAutocompleteTokens.TextFieldErrorTrailingIconColor.toColor(),
-        focusedLabelColor: Color = OutlinedAutocompleteTokens.FieldFocusLabelTextColor.toColor(),
-        unfocusedLabelColor: Color = OutlinedAutocompleteTokens.FieldLabelTextColor.toColor(),
-        disabledLabelColor: Color = OutlinedAutocompleteTokens.FieldDisabledLabelTextColor.toColor()
+            OutlinedAutocompleteTokens.TextFieldErrorTrailingIconColor.value,
+        focusedLabelColor: Color = OutlinedAutocompleteTokens.FieldFocusLabelTextColor.value,
+        unfocusedLabelColor: Color = OutlinedAutocompleteTokens.FieldLabelTextColor.value,
+        disabledLabelColor: Color = OutlinedAutocompleteTokens.FieldDisabledLabelTextColor.value
             .copy(alpha = OutlinedAutocompleteTokens.FieldDisabledLabelTextOpacity),
-        errorLabelColor: Color = OutlinedAutocompleteTokens.FieldErrorLabelTextColor.toColor(),
+        errorLabelColor: Color = OutlinedAutocompleteTokens.FieldErrorLabelTextColor.value,
         focusedPlaceholderColor: Color =
-            OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
+            OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
         unfocusedPlaceholderColor: Color =
-            OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
+            OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
         disabledPlaceholderColor: Color =
-            OutlinedAutocompleteTokens.FieldDisabledSupportingTextColor.toColor()
+            OutlinedAutocompleteTokens.FieldDisabledSupportingTextColor.value
                 .copy(alpha = OutlinedAutocompleteTokens.FieldDisabledSupportingTextOpacity),
         errorPlaceholderColor: Color =
-            OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
-        focusedPrefixColor: Color = OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
-        unfocusedPrefixColor: Color = OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
+            OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
+        focusedPrefixColor: Color = OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
+        unfocusedPrefixColor: Color = OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
         disabledPrefixColor: Color = OutlinedAutocompleteTokens.FieldDisabledSupportingTextColor
-            .toColor().copy(alpha = OutlinedAutocompleteTokens.FieldDisabledSupportingTextOpacity),
-        errorPrefixColor: Color = OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
-        focusedSuffixColor: Color = OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
-        unfocusedSuffixColor: Color = OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
+            .value.copy(alpha = OutlinedAutocompleteTokens.FieldDisabledSupportingTextOpacity),
+        errorPrefixColor: Color = OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
+        focusedSuffixColor: Color = OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
+        unfocusedSuffixColor: Color = OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
         disabledSuffixColor: Color = OutlinedAutocompleteTokens.FieldDisabledSupportingTextColor
-            .toColor().copy(alpha = OutlinedAutocompleteTokens.FieldDisabledSupportingTextOpacity),
-        errorSuffixColor: Color = OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
+            .value.copy(alpha = OutlinedAutocompleteTokens.FieldDisabledSupportingTextOpacity),
+        errorSuffixColor: Color = OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
     ): TextFieldColors =
         OutlinedTextFieldDefaults.colors(
             focusedTextColor = focusedTextColor,
@@ -627,65 +627,65 @@
     @Deprecated("Maintained for binary compatibility", level = DeprecationLevel.HIDDEN)
     @Composable
     fun textFieldColors(
-        focusedTextColor: Color = FilledAutocompleteTokens.FieldFocusInputTextColor.toColor(),
-        unfocusedTextColor: Color = FilledAutocompleteTokens.FieldInputTextColor.toColor(),
-        disabledTextColor: Color = FilledAutocompleteTokens.FieldDisabledInputTextColor.toColor()
+        focusedTextColor: Color = FilledAutocompleteTokens.FieldFocusInputTextColor.value,
+        unfocusedTextColor: Color = FilledAutocompleteTokens.FieldInputTextColor.value,
+        disabledTextColor: Color = FilledAutocompleteTokens.FieldDisabledInputTextColor.value
             .copy(alpha = FilledAutocompleteTokens.FieldDisabledInputTextOpacity),
-        errorTextColor: Color = FilledAutocompleteTokens.FieldErrorInputTextColor.toColor(),
-        containerColor: Color = FilledAutocompleteTokens.TextFieldContainerColor.toColor(),
-        errorContainerColor: Color = FilledAutocompleteTokens.TextFieldContainerColor.toColor(),
-        cursorColor: Color = FilledAutocompleteTokens.TextFieldCaretColor.toColor(),
-        errorCursorColor: Color = FilledAutocompleteTokens.TextFieldErrorFocusCaretColor.toColor(),
+        errorTextColor: Color = FilledAutocompleteTokens.FieldErrorInputTextColor.value,
+        containerColor: Color = FilledAutocompleteTokens.TextFieldContainerColor.value,
+        errorContainerColor: Color = FilledAutocompleteTokens.TextFieldContainerColor.value,
+        cursorColor: Color = FilledAutocompleteTokens.TextFieldCaretColor.value,
+        errorCursorColor: Color = FilledAutocompleteTokens.TextFieldErrorFocusCaretColor.value,
         selectionColors: TextSelectionColors = LocalTextSelectionColors.current,
         focusedIndicatorColor: Color =
-            FilledAutocompleteTokens.TextFieldFocusActiveIndicatorColor.toColor(),
+            FilledAutocompleteTokens.TextFieldFocusActiveIndicatorColor.value,
         unfocusedIndicatorColor: Color =
-            FilledAutocompleteTokens.TextFieldActiveIndicatorColor.toColor(),
+            FilledAutocompleteTokens.TextFieldActiveIndicatorColor.value,
         disabledIndicatorColor: Color =
-            FilledAutocompleteTokens.TextFieldDisabledActiveIndicatorColor.toColor()
+            FilledAutocompleteTokens.TextFieldDisabledActiveIndicatorColor.value
                 .copy(alpha = FilledAutocompleteTokens.TextFieldDisabledActiveIndicatorOpacity),
         errorIndicatorColor: Color =
-            FilledAutocompleteTokens.TextFieldErrorActiveIndicatorColor.toColor(),
+            FilledAutocompleteTokens.TextFieldErrorActiveIndicatorColor.value,
         focusedLeadingIconColor: Color =
-            FilledAutocompleteTokens.TextFieldFocusLeadingIconColor.toColor(),
+            FilledAutocompleteTokens.TextFieldFocusLeadingIconColor.value,
         unfocusedLeadingIconColor: Color =
-            FilledAutocompleteTokens.TextFieldLeadingIconColor.toColor(),
+            FilledAutocompleteTokens.TextFieldLeadingIconColor.value,
         disabledLeadingIconColor: Color =
-            FilledAutocompleteTokens.TextFieldDisabledLeadingIconColor.toColor()
+            FilledAutocompleteTokens.TextFieldDisabledLeadingIconColor.value
                 .copy(alpha = FilledAutocompleteTokens.TextFieldDisabledLeadingIconOpacity),
         errorLeadingIconColor: Color =
-            FilledAutocompleteTokens.TextFieldErrorLeadingIconColor.toColor(),
+            FilledAutocompleteTokens.TextFieldErrorLeadingIconColor.value,
         focusedTrailingIconColor: Color =
-            FilledAutocompleteTokens.TextFieldFocusTrailingIconColor.toColor(),
+            FilledAutocompleteTokens.TextFieldFocusTrailingIconColor.value,
         unfocusedTrailingIconColor: Color =
-            FilledAutocompleteTokens.TextFieldTrailingIconColor.toColor(),
+            FilledAutocompleteTokens.TextFieldTrailingIconColor.value,
         disabledTrailingIconColor: Color =
-            FilledAutocompleteTokens.TextFieldDisabledTrailingIconColor.toColor()
+            FilledAutocompleteTokens.TextFieldDisabledTrailingIconColor.value
                 .copy(alpha = FilledAutocompleteTokens.TextFieldDisabledTrailingIconOpacity),
         errorTrailingIconColor: Color =
-            FilledAutocompleteTokens.TextFieldErrorTrailingIconColor.toColor(),
-        focusedLabelColor: Color = FilledAutocompleteTokens.FieldFocusLabelTextColor.toColor(),
-        unfocusedLabelColor: Color = FilledAutocompleteTokens.FieldLabelTextColor.toColor(),
-        disabledLabelColor: Color = FilledAutocompleteTokens.FieldDisabledLabelTextColor.toColor(),
-        errorLabelColor: Color = FilledAutocompleteTokens.FieldErrorLabelTextColor.toColor(),
+            FilledAutocompleteTokens.TextFieldErrorTrailingIconColor.value,
+        focusedLabelColor: Color = FilledAutocompleteTokens.FieldFocusLabelTextColor.value,
+        unfocusedLabelColor: Color = FilledAutocompleteTokens.FieldLabelTextColor.value,
+        disabledLabelColor: Color = FilledAutocompleteTokens.FieldDisabledLabelTextColor.value,
+        errorLabelColor: Color = FilledAutocompleteTokens.FieldErrorLabelTextColor.value,
         focusedPlaceholderColor: Color =
-            FilledAutocompleteTokens.FieldSupportingTextColor.toColor(),
+            FilledAutocompleteTokens.FieldSupportingTextColor.value,
         unfocusedPlaceholderColor: Color =
-            FilledAutocompleteTokens.FieldSupportingTextColor.toColor(),
+            FilledAutocompleteTokens.FieldSupportingTextColor.value,
         disabledPlaceholderColor: Color =
-            FilledAutocompleteTokens.FieldDisabledSupportingTextColor.toColor()
+            FilledAutocompleteTokens.FieldDisabledSupportingTextColor.value
                 .copy(alpha = FilledAutocompleteTokens.FieldDisabledSupportingTextOpacity),
-        errorPlaceholderColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.toColor(),
-        focusedPrefixColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.toColor(),
-        unfocusedPrefixColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.toColor(),
+        errorPlaceholderColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.value,
+        focusedPrefixColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.value,
+        unfocusedPrefixColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.value,
         disabledPrefixColor: Color = FilledAutocompleteTokens.FieldDisabledSupportingTextColor
-            .toColor().copy(alpha = FilledAutocompleteTokens.FieldDisabledSupportingTextOpacity),
-        errorPrefixColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.toColor(),
-        focusedSuffixColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.toColor(),
-        unfocusedSuffixColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.toColor(),
+            .value.copy(alpha = FilledAutocompleteTokens.FieldDisabledSupportingTextOpacity),
+        errorPrefixColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.value,
+        focusedSuffixColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.value,
+        unfocusedSuffixColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.value,
         disabledSuffixColor: Color = FilledAutocompleteTokens.FieldDisabledSupportingTextColor
-            .toColor().copy(alpha = FilledAutocompleteTokens.FieldDisabledSupportingTextOpacity),
-        errorSuffixColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.toColor(),
+            .value.copy(alpha = FilledAutocompleteTokens.FieldDisabledSupportingTextOpacity),
+        errorSuffixColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.value,
     ): TextFieldColors =
         textFieldColors(
             focusedTextColor = focusedTextColor,
@@ -732,65 +732,65 @@
     @Deprecated("Maintained for binary compatibility", level = DeprecationLevel.HIDDEN)
     @Composable
     fun outlinedTextFieldColors(
-        focusedTextColor: Color = OutlinedAutocompleteTokens.FieldFocusInputTextColor.toColor(),
-        unfocusedTextColor: Color = OutlinedAutocompleteTokens.FieldInputTextColor.toColor(),
-        disabledTextColor: Color = OutlinedAutocompleteTokens.FieldDisabledInputTextColor.toColor()
+        focusedTextColor: Color = OutlinedAutocompleteTokens.FieldFocusInputTextColor.value,
+        unfocusedTextColor: Color = OutlinedAutocompleteTokens.FieldInputTextColor.value,
+        disabledTextColor: Color = OutlinedAutocompleteTokens.FieldDisabledInputTextColor.value
             .copy(alpha = OutlinedAutocompleteTokens.FieldDisabledInputTextOpacity),
-        errorTextColor: Color = OutlinedAutocompleteTokens.FieldErrorInputTextColor.toColor(),
+        errorTextColor: Color = OutlinedAutocompleteTokens.FieldErrorInputTextColor.value,
         containerColor: Color = Color.Transparent,
         errorContainerColor: Color = Color.Transparent,
-        cursorColor: Color = OutlinedAutocompleteTokens.TextFieldCaretColor.toColor(),
+        cursorColor: Color = OutlinedAutocompleteTokens.TextFieldCaretColor.value,
         errorCursorColor: Color =
-            OutlinedAutocompleteTokens.TextFieldErrorFocusCaretColor.toColor(),
+            OutlinedAutocompleteTokens.TextFieldErrorFocusCaretColor.value,
         selectionColors: TextSelectionColors = LocalTextSelectionColors.current,
-        focusedBorderColor: Color = OutlinedAutocompleteTokens.TextFieldFocusOutlineColor.toColor(),
-        unfocusedBorderColor: Color = OutlinedAutocompleteTokens.TextFieldOutlineColor.toColor(),
+        focusedBorderColor: Color = OutlinedAutocompleteTokens.TextFieldFocusOutlineColor.value,
+        unfocusedBorderColor: Color = OutlinedAutocompleteTokens.TextFieldOutlineColor.value,
         disabledBorderColor: Color =
-            OutlinedAutocompleteTokens.TextFieldDisabledOutlineColor.toColor()
+            OutlinedAutocompleteTokens.TextFieldDisabledOutlineColor.value
                 .copy(alpha = OutlinedAutocompleteTokens.TextFieldDisabledOutlineOpacity),
-        errorBorderColor: Color = OutlinedAutocompleteTokens.TextFieldErrorOutlineColor.toColor(),
+        errorBorderColor: Color = OutlinedAutocompleteTokens.TextFieldErrorOutlineColor.value,
         focusedLeadingIconColor: Color =
-            OutlinedAutocompleteTokens.TextFieldFocusLeadingIconColor.toColor(),
+            OutlinedAutocompleteTokens.TextFieldFocusLeadingIconColor.value,
         unfocusedLeadingIconColor: Color =
-            OutlinedAutocompleteTokens.TextFieldLeadingIconColor.toColor(),
+            OutlinedAutocompleteTokens.TextFieldLeadingIconColor.value,
         disabledLeadingIconColor: Color =
-            OutlinedAutocompleteTokens.TextFieldDisabledLeadingIconColor.toColor()
+            OutlinedAutocompleteTokens.TextFieldDisabledLeadingIconColor.value
                 .copy(alpha = OutlinedAutocompleteTokens.TextFieldDisabledLeadingIconOpacity),
         errorLeadingIconColor: Color =
-            OutlinedAutocompleteTokens.TextFieldErrorLeadingIconColor.toColor(),
+            OutlinedAutocompleteTokens.TextFieldErrorLeadingIconColor.value,
         focusedTrailingIconColor: Color =
-            OutlinedAutocompleteTokens.TextFieldFocusTrailingIconColor.toColor(),
+            OutlinedAutocompleteTokens.TextFieldFocusTrailingIconColor.value,
         unfocusedTrailingIconColor: Color =
-            OutlinedAutocompleteTokens.TextFieldTrailingIconColor.toColor(),
+            OutlinedAutocompleteTokens.TextFieldTrailingIconColor.value,
         disabledTrailingIconColor: Color =
-            OutlinedAutocompleteTokens.TextFieldDisabledTrailingIconColor.toColor()
+            OutlinedAutocompleteTokens.TextFieldDisabledTrailingIconColor.value
                 .copy(alpha = OutlinedAutocompleteTokens.TextFieldDisabledTrailingIconOpacity),
         errorTrailingIconColor: Color =
-            OutlinedAutocompleteTokens.TextFieldErrorTrailingIconColor.toColor(),
-        focusedLabelColor: Color = OutlinedAutocompleteTokens.FieldFocusLabelTextColor.toColor(),
-        unfocusedLabelColor: Color = OutlinedAutocompleteTokens.FieldLabelTextColor.toColor(),
-        disabledLabelColor: Color = OutlinedAutocompleteTokens.FieldDisabledLabelTextColor.toColor()
+            OutlinedAutocompleteTokens.TextFieldErrorTrailingIconColor.value,
+        focusedLabelColor: Color = OutlinedAutocompleteTokens.FieldFocusLabelTextColor.value,
+        unfocusedLabelColor: Color = OutlinedAutocompleteTokens.FieldLabelTextColor.value,
+        disabledLabelColor: Color = OutlinedAutocompleteTokens.FieldDisabledLabelTextColor.value
             .copy(alpha = OutlinedAutocompleteTokens.FieldDisabledLabelTextOpacity),
-        errorLabelColor: Color = OutlinedAutocompleteTokens.FieldErrorLabelTextColor.toColor(),
+        errorLabelColor: Color = OutlinedAutocompleteTokens.FieldErrorLabelTextColor.value,
         focusedPlaceholderColor: Color =
-            OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
+            OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
         unfocusedPlaceholderColor: Color =
-            OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
+            OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
         disabledPlaceholderColor: Color =
-            OutlinedAutocompleteTokens.FieldDisabledSupportingTextColor.toColor()
+            OutlinedAutocompleteTokens.FieldDisabledSupportingTextColor.value
                 .copy(alpha = OutlinedAutocompleteTokens.FieldDisabledSupportingTextOpacity),
         errorPlaceholderColor: Color =
-            OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
-        focusedPrefixColor: Color = OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
-        unfocusedPrefixColor: Color = OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
+            OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
+        focusedPrefixColor: Color = OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
+        unfocusedPrefixColor: Color = OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
         disabledPrefixColor: Color = OutlinedAutocompleteTokens.FieldDisabledSupportingTextColor
-            .toColor().copy(alpha = OutlinedAutocompleteTokens.FieldDisabledSupportingTextOpacity),
-        errorPrefixColor: Color = OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
-        focusedSuffixColor: Color = OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
-        unfocusedSuffixColor: Color = OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
+            .value.copy(alpha = OutlinedAutocompleteTokens.FieldDisabledSupportingTextOpacity),
+        errorPrefixColor: Color = OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
+        focusedSuffixColor: Color = OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
+        unfocusedSuffixColor: Color = OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
         disabledSuffixColor: Color = OutlinedAutocompleteTokens.FieldDisabledSupportingTextColor
-            .toColor().copy(alpha = OutlinedAutocompleteTokens.FieldDisabledSupportingTextOpacity),
-        errorSuffixColor: Color = OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
+            .value.copy(alpha = OutlinedAutocompleteTokens.FieldDisabledSupportingTextOpacity),
+        errorSuffixColor: Color = OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
     ): TextFieldColors =
         outlinedTextFieldColors(
             focusedTextColor = focusedTextColor,
@@ -837,47 +837,47 @@
     @Deprecated("Maintained for binary compatibility", level = DeprecationLevel.HIDDEN)
     @Composable
     fun textFieldColors(
-        textColor: Color = FilledAutocompleteTokens.FieldInputTextColor.toColor(),
-        disabledTextColor: Color = FilledAutocompleteTokens.FieldDisabledInputTextColor.toColor()
+        textColor: Color = FilledAutocompleteTokens.FieldInputTextColor.value,
+        disabledTextColor: Color = FilledAutocompleteTokens.FieldDisabledInputTextColor.value
             .copy(alpha = FilledAutocompleteTokens.FieldDisabledInputTextOpacity),
-        containerColor: Color = FilledAutocompleteTokens.TextFieldContainerColor.toColor(),
-        cursorColor: Color = FilledAutocompleteTokens.TextFieldCaretColor.toColor(),
-        errorCursorColor: Color = FilledAutocompleteTokens.TextFieldErrorFocusCaretColor.toColor(),
+        containerColor: Color = FilledAutocompleteTokens.TextFieldContainerColor.value,
+        cursorColor: Color = FilledAutocompleteTokens.TextFieldCaretColor.value,
+        errorCursorColor: Color = FilledAutocompleteTokens.TextFieldErrorFocusCaretColor.value,
         selectionColors: TextSelectionColors = LocalTextSelectionColors.current,
         focusedIndicatorColor: Color =
-            FilledAutocompleteTokens.TextFieldFocusActiveIndicatorColor.toColor(),
+            FilledAutocompleteTokens.TextFieldFocusActiveIndicatorColor.value,
         unfocusedIndicatorColor: Color =
-            FilledAutocompleteTokens.TextFieldActiveIndicatorColor.toColor(),
+            FilledAutocompleteTokens.TextFieldActiveIndicatorColor.value,
         disabledIndicatorColor: Color =
-            FilledAutocompleteTokens.TextFieldDisabledActiveIndicatorColor.toColor()
+            FilledAutocompleteTokens.TextFieldDisabledActiveIndicatorColor.value
                 .copy(alpha = FilledAutocompleteTokens.TextFieldDisabledActiveIndicatorOpacity),
         errorIndicatorColor: Color =
-            FilledAutocompleteTokens.TextFieldErrorActiveIndicatorColor.toColor(),
+            FilledAutocompleteTokens.TextFieldErrorActiveIndicatorColor.value,
         focusedLeadingIconColor: Color =
-            FilledAutocompleteTokens.TextFieldFocusLeadingIconColor.toColor(),
+            FilledAutocompleteTokens.TextFieldFocusLeadingIconColor.value,
         unfocusedLeadingIconColor: Color =
-            FilledAutocompleteTokens.TextFieldLeadingIconColor.toColor(),
+            FilledAutocompleteTokens.TextFieldLeadingIconColor.value,
         disabledLeadingIconColor: Color =
-            FilledAutocompleteTokens.TextFieldDisabledLeadingIconColor.toColor()
+            FilledAutocompleteTokens.TextFieldDisabledLeadingIconColor.value
                 .copy(alpha = FilledAutocompleteTokens.TextFieldDisabledLeadingIconOpacity),
         errorLeadingIconColor: Color =
-            FilledAutocompleteTokens.TextFieldErrorLeadingIconColor.toColor(),
+            FilledAutocompleteTokens.TextFieldErrorLeadingIconColor.value,
         focusedTrailingIconColor: Color =
-            FilledAutocompleteTokens.TextFieldFocusTrailingIconColor.toColor(),
+            FilledAutocompleteTokens.TextFieldFocusTrailingIconColor.value,
         unfocusedTrailingIconColor: Color =
-            FilledAutocompleteTokens.TextFieldTrailingIconColor.toColor(),
+            FilledAutocompleteTokens.TextFieldTrailingIconColor.value,
         disabledTrailingIconColor: Color =
-            FilledAutocompleteTokens.TextFieldDisabledTrailingIconColor.toColor()
+            FilledAutocompleteTokens.TextFieldDisabledTrailingIconColor.value
                 .copy(alpha = FilledAutocompleteTokens.TextFieldDisabledTrailingIconOpacity),
         errorTrailingIconColor: Color =
-            FilledAutocompleteTokens.TextFieldErrorTrailingIconColor.toColor(),
-        focusedLabelColor: Color = FilledAutocompleteTokens.FieldFocusLabelTextColor.toColor(),
-        unfocusedLabelColor: Color = FilledAutocompleteTokens.FieldLabelTextColor.toColor(),
-        disabledLabelColor: Color = FilledAutocompleteTokens.FieldDisabledLabelTextColor.toColor(),
-        errorLabelColor: Color = FilledAutocompleteTokens.FieldErrorLabelTextColor.toColor(),
-        placeholderColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.toColor(),
+            FilledAutocompleteTokens.TextFieldErrorTrailingIconColor.value,
+        focusedLabelColor: Color = FilledAutocompleteTokens.FieldFocusLabelTextColor.value,
+        unfocusedLabelColor: Color = FilledAutocompleteTokens.FieldLabelTextColor.value,
+        disabledLabelColor: Color = FilledAutocompleteTokens.FieldDisabledLabelTextColor.value,
+        errorLabelColor: Color = FilledAutocompleteTokens.FieldErrorLabelTextColor.value,
+        placeholderColor: Color = FilledAutocompleteTokens.FieldSupportingTextColor.value,
         disabledPlaceholderColor: Color =
-            FilledAutocompleteTokens.FieldDisabledInputTextColor.toColor()
+            FilledAutocompleteTokens.FieldDisabledInputTextColor.value
                 .copy(alpha = FilledAutocompleteTokens.FieldDisabledInputTextOpacity)
     ): TextFieldColors = textFieldColors(
         focusedTextColor = textColor,
@@ -911,61 +911,61 @@
         unfocusedPlaceholderColor = placeholderColor,
         disabledPlaceholderColor = disabledPlaceholderColor,
         errorPlaceholderColor = placeholderColor,
-        focusedPrefixColor = OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
-        unfocusedPrefixColor = OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
-        disabledPrefixColor = OutlinedAutocompleteTokens.FieldDisabledSupportingTextColor.toColor()
+        focusedPrefixColor = OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
+        unfocusedPrefixColor = OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
+        disabledPrefixColor = OutlinedAutocompleteTokens.FieldDisabledSupportingTextColor.value
             .copy(alpha = OutlinedAutocompleteTokens.FieldDisabledSupportingTextOpacity),
-        errorPrefixColor = OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
-        focusedSuffixColor = OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
-        unfocusedSuffixColor = OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
-        disabledSuffixColor = OutlinedAutocompleteTokens.FieldDisabledSupportingTextColor.toColor()
+        errorPrefixColor = OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
+        focusedSuffixColor = OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
+        unfocusedSuffixColor = OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
+        disabledSuffixColor = OutlinedAutocompleteTokens.FieldDisabledSupportingTextColor.value
             .copy(alpha = OutlinedAutocompleteTokens.FieldDisabledSupportingTextOpacity),
-        errorSuffixColor = OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
+        errorSuffixColor = OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
     )
 
     @Deprecated("Maintained for binary compatibility", level = DeprecationLevel.HIDDEN)
     @Composable
     fun outlinedTextFieldColors(
-        textColor: Color = OutlinedAutocompleteTokens.FieldInputTextColor.toColor(),
-        disabledTextColor: Color = OutlinedAutocompleteTokens.FieldDisabledInputTextColor.toColor()
+        textColor: Color = OutlinedAutocompleteTokens.FieldInputTextColor.value,
+        disabledTextColor: Color = OutlinedAutocompleteTokens.FieldDisabledInputTextColor.value
             .copy(alpha = OutlinedAutocompleteTokens.FieldDisabledInputTextOpacity),
         containerColor: Color = Color.Transparent,
-        cursorColor: Color = OutlinedAutocompleteTokens.TextFieldCaretColor.toColor(),
+        cursorColor: Color = OutlinedAutocompleteTokens.TextFieldCaretColor.value,
         errorCursorColor: Color =
-            OutlinedAutocompleteTokens.TextFieldErrorFocusCaretColor.toColor(),
+            OutlinedAutocompleteTokens.TextFieldErrorFocusCaretColor.value,
         selectionColors: TextSelectionColors = LocalTextSelectionColors.current,
-        focusedBorderColor: Color = OutlinedAutocompleteTokens.TextFieldFocusOutlineColor.toColor(),
-        unfocusedBorderColor: Color = OutlinedAutocompleteTokens.TextFieldOutlineColor.toColor(),
+        focusedBorderColor: Color = OutlinedAutocompleteTokens.TextFieldFocusOutlineColor.value,
+        unfocusedBorderColor: Color = OutlinedAutocompleteTokens.TextFieldOutlineColor.value,
         disabledBorderColor: Color =
-            OutlinedAutocompleteTokens.TextFieldDisabledOutlineColor.toColor()
+            OutlinedAutocompleteTokens.TextFieldDisabledOutlineColor.value
                 .copy(alpha = OutlinedAutocompleteTokens.TextFieldDisabledOutlineOpacity),
-        errorBorderColor: Color = OutlinedAutocompleteTokens.TextFieldErrorOutlineColor.toColor(),
+        errorBorderColor: Color = OutlinedAutocompleteTokens.TextFieldErrorOutlineColor.value,
         focusedLeadingIconColor: Color =
-            OutlinedAutocompleteTokens.TextFieldFocusLeadingIconColor.toColor(),
+            OutlinedAutocompleteTokens.TextFieldFocusLeadingIconColor.value,
         unfocusedLeadingIconColor: Color =
-            OutlinedAutocompleteTokens.TextFieldLeadingIconColor.toColor(),
+            OutlinedAutocompleteTokens.TextFieldLeadingIconColor.value,
         disabledLeadingIconColor: Color =
-            OutlinedAutocompleteTokens.TextFieldDisabledLeadingIconColor.toColor()
+            OutlinedAutocompleteTokens.TextFieldDisabledLeadingIconColor.value
                 .copy(alpha = OutlinedAutocompleteTokens.TextFieldDisabledLeadingIconOpacity),
         errorLeadingIconColor: Color =
-            OutlinedAutocompleteTokens.TextFieldErrorLeadingIconColor.toColor(),
+            OutlinedAutocompleteTokens.TextFieldErrorLeadingIconColor.value,
         focusedTrailingIconColor: Color =
-            OutlinedAutocompleteTokens.TextFieldFocusTrailingIconColor.toColor(),
+            OutlinedAutocompleteTokens.TextFieldFocusTrailingIconColor.value,
         unfocusedTrailingIconColor: Color =
-            OutlinedAutocompleteTokens.TextFieldTrailingIconColor.toColor(),
+            OutlinedAutocompleteTokens.TextFieldTrailingIconColor.value,
         disabledTrailingIconColor: Color =
-            OutlinedAutocompleteTokens.TextFieldDisabledTrailingIconColor.toColor()
+            OutlinedAutocompleteTokens.TextFieldDisabledTrailingIconColor.value
                 .copy(alpha = OutlinedAutocompleteTokens.TextFieldDisabledTrailingIconOpacity),
         errorTrailingIconColor: Color =
-            OutlinedAutocompleteTokens.TextFieldErrorTrailingIconColor.toColor(),
-        focusedLabelColor: Color = OutlinedAutocompleteTokens.FieldFocusLabelTextColor.toColor(),
-        unfocusedLabelColor: Color = OutlinedAutocompleteTokens.FieldLabelTextColor.toColor(),
-        disabledLabelColor: Color = OutlinedAutocompleteTokens.FieldDisabledLabelTextColor.toColor()
+            OutlinedAutocompleteTokens.TextFieldErrorTrailingIconColor.value,
+        focusedLabelColor: Color = OutlinedAutocompleteTokens.FieldFocusLabelTextColor.value,
+        unfocusedLabelColor: Color = OutlinedAutocompleteTokens.FieldLabelTextColor.value,
+        disabledLabelColor: Color = OutlinedAutocompleteTokens.FieldDisabledLabelTextColor.value
             .copy(alpha = OutlinedAutocompleteTokens.FieldDisabledLabelTextOpacity),
-        errorLabelColor: Color = OutlinedAutocompleteTokens.FieldErrorLabelTextColor.toColor(),
-        placeholderColor: Color = OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
+        errorLabelColor: Color = OutlinedAutocompleteTokens.FieldErrorLabelTextColor.value,
+        placeholderColor: Color = OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
         disabledPlaceholderColor: Color =
-            OutlinedAutocompleteTokens.FieldDisabledInputTextColor.toColor()
+            OutlinedAutocompleteTokens.FieldDisabledInputTextColor.value
                 .copy(alpha = OutlinedAutocompleteTokens.FieldDisabledInputTextOpacity)
     ): TextFieldColors = outlinedTextFieldColors(
         focusedTextColor = textColor,
@@ -999,16 +999,16 @@
         unfocusedPlaceholderColor = placeholderColor,
         disabledPlaceholderColor = disabledPlaceholderColor,
         errorPlaceholderColor = placeholderColor,
-        focusedPrefixColor = OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
-        unfocusedPrefixColor = OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
-        disabledPrefixColor = OutlinedAutocompleteTokens.FieldDisabledSupportingTextColor.toColor()
+        focusedPrefixColor = OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
+        unfocusedPrefixColor = OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
+        disabledPrefixColor = OutlinedAutocompleteTokens.FieldDisabledSupportingTextColor.value
             .copy(alpha = OutlinedAutocompleteTokens.FieldDisabledSupportingTextOpacity),
-        errorPrefixColor = OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
-        focusedSuffixColor = OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
-        unfocusedSuffixColor = OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
-        disabledSuffixColor = OutlinedAutocompleteTokens.FieldDisabledSupportingTextColor.toColor()
+        errorPrefixColor = OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
+        focusedSuffixColor = OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
+        unfocusedSuffixColor = OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
+        disabledSuffixColor = OutlinedAutocompleteTokens.FieldDisabledSupportingTextColor.value
             .copy(alpha = OutlinedAutocompleteTokens.FieldDisabledSupportingTextOpacity),
-        errorSuffixColor = OutlinedAutocompleteTokens.FieldSupportingTextColor.toColor(),
+        errorSuffixColor = OutlinedAutocompleteTokens.FieldSupportingTextColor.value,
     )
 }
 
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/IncludeFontPaddingHelper.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/IncludeFontPaddingHelper.android.kt
deleted file mode 100644
index 8be2585..0000000
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/IncludeFontPaddingHelper.android.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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.ui.text.PlatformTextStyle
-import androidx.compose.ui.text.TextStyle
-
-// TODO(b/237588251) remove this once the default includeFontPadding is false
-@Suppress("DEPRECATION")
-internal actual fun copyAndSetFontPadding(
-    style: TextStyle,
-    includeFontPadding: Boolean
-): TextStyle =
-    style.copy(platformStyle = PlatformTextStyle(includeFontPadding = includeFontPadding))
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ModalBottomSheet.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ModalBottomSheet.android.kt
index 1dfdc5a..abd7fee 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ModalBottomSheet.android.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ModalBottomSheet.android.kt
@@ -48,6 +48,7 @@
 import androidx.compose.runtime.CompositionContext
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -64,6 +65,7 @@
 import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.platform.AbstractComposeView
+import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.platform.LocalView
 import androidx.compose.ui.platform.ViewRootForInspector
@@ -135,6 +137,11 @@
     windowInsets: WindowInsets = BottomSheetDefaults.windowInsets,
     content: @Composable ColumnScope.() -> Unit,
 ) {
+    // b/291735717 Remove this once deprecated methods without density are removed
+    val density = LocalDensity.current
+    SideEffect {
+        sheetState.density = density
+    }
     val scope = rememberCoroutineScope()
     val animateToDismiss: () -> Unit = {
         if (sheetState.swipeableState.confirmValueChange(Hidden)) {
@@ -247,8 +254,7 @@
                                             }
                                         } else if (hasPartiallyExpandedState) {
                                             collapse(collapseActionLabel) {
-                                                if (
-                                                    swipeableState.confirmValueChange(
+                                                if (swipeableState.confirmValueChange(
                                                         PartiallyExpanded
                                                     )
                                                 ) {
@@ -329,12 +335,12 @@
     screenHeight: Float,
     onDragStopped: CoroutineScope.(velocity: Float) -> Unit,
 ) = draggable(
-        state = sheetState.swipeableState.swipeDraggableState,
-        orientation = Orientation.Vertical,
-        enabled = sheetState.isVisible,
-        startDragImmediately = sheetState.swipeableState.isAnimationRunning,
-        onDragStopped = onDragStopped
-    )
+    state = sheetState.swipeableState.swipeDraggableState,
+    orientation = Orientation.Vertical,
+    enabled = sheetState.isVisible,
+    startDragImmediately = sheetState.swipeableState.isAnimationRunning,
+    onDragStopped = onDragStopped
+)
     .swipeAnchors(
         state = sheetState.swipeableState,
         anchorChangeHandler = anchorChangeHandler,
@@ -347,6 +353,7 @@
                 sheetState.skipPartiallyExpanded -> null
                 else -> screenHeight / 2f
             }
+
             Expanded -> if (sheetSize.height != 0) {
                 max(0f, screenHeight - sheetSize.height)
             } else null
@@ -479,8 +486,8 @@
             // Flags specific to modal bottom sheet.
             flags = flags and (
                 WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES or
-                WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
-            ).inv()
+                    WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
+                ).inv()
 
             flags = flags or WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
         }
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/SearchBar.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/SearchBar.kt
index 270f81a..c4afd00 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/SearchBar.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/SearchBar.kt
@@ -30,6 +30,7 @@
 import androidx.compose.animation.shrinkVertically
 import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsFocusedAsState
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.ColumnScope
@@ -247,8 +248,8 @@
                     .offset(vertical = -animatedTopPadding))
                 layout(width, height) {
                     placeable.placeRelative(0, animatedTopPadding)
+                }
             }
-        }
     ) {
         Column {
             val animatedInputFieldPadding = remember {
@@ -281,8 +282,10 @@
         }
     }
 
+    val isFocused = interactionSource.collectIsFocusedAsState().value
+    val shouldClearFocus = !active && isFocused
     LaunchedEffect(active) {
-        if (!active) {
+        if (shouldClearFocus) {
             // Not strictly needed according to the motion spec, but since the animation already has
             // a delay, this works around b/261632544.
             delay(AnimationDelayMillis.toLong())
@@ -408,8 +411,10 @@
         }
     }
 
+    val isFocused = interactionSource.collectIsFocusedAsState().value
+    val shouldClearFocus = !active && isFocused
     LaunchedEffect(active) {
-        if (!active) {
+        if (shouldClearFocus) {
             // Not strictly needed according to the motion spec, but since the animation already has
             // a delay, this works around b/261632544.
             delay(AnimationDelayMillis.toLong())
@@ -516,14 +521,14 @@
     val InputFieldHeight: Dp = SearchBarTokens.ContainerHeight
 
     /** Default shape for a search bar's input field, or a search bar in the inactive state. */
-    val inputFieldShape: Shape @Composable get() = SearchBarTokens.ContainerShape.toShape()
+    val inputFieldShape: Shape @Composable get() = SearchBarTokens.ContainerShape.value
 
     /** Default shape for a [SearchBar] in the active state. */
     val fullScreenShape: Shape
-        @Composable get() = SearchViewTokens.FullScreenContainerShape.toShape()
+        @Composable get() = SearchViewTokens.FullScreenContainerShape.value
 
     /** Default shape for a [DockedSearchBar]. */
-    val dockedShape: Shape @Composable get() = SearchViewTokens.DockedContainerShape.toShape()
+    val dockedShape: Shape @Composable get() = SearchViewTokens.DockedContainerShape.value
 
     /** Default window insets for a [SearchBar]. */
     val windowInsets: WindowInsets @Composable get() = WindowInsets.statusBars
@@ -538,8 +543,8 @@
      */
     @Composable
     fun colors(
-        containerColor: Color = SearchBarTokens.ContainerColor.toColor(),
-        dividerColor: Color = SearchViewTokens.DividerColor.toColor(),
+        containerColor: Color = SearchBarTokens.ContainerColor.value,
+        dividerColor: Color = SearchViewTokens.DividerColor.value,
         inputFieldColors: TextFieldColors = inputFieldColors(),
     ): SearchBarColors = SearchBarColors(
         containerColor = containerColor,
@@ -573,23 +578,23 @@
      */
     @Composable
     fun inputFieldColors(
-        focusedTextColor: Color = SearchBarTokens.InputTextColor.toColor(),
-        unfocusedTextColor: Color = SearchBarTokens.InputTextColor.toColor(),
-        disabledTextColor: Color = FilledTextFieldTokens.DisabledInputColor.toColor()
+        focusedTextColor: Color = SearchBarTokens.InputTextColor.value,
+        unfocusedTextColor: Color = SearchBarTokens.InputTextColor.value,
+        disabledTextColor: Color = FilledTextFieldTokens.DisabledInputColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledInputOpacity),
-        cursorColor: Color = FilledTextFieldTokens.CaretColor.toColor(),
+        cursorColor: Color = FilledTextFieldTokens.CaretColor.value,
         selectionColors: TextSelectionColors = LocalTextSelectionColors.current,
-        focusedLeadingIconColor: Color = SearchBarTokens.LeadingIconColor.toColor(),
-        unfocusedLeadingIconColor: Color = SearchBarTokens.LeadingIconColor.toColor(),
+        focusedLeadingIconColor: Color = SearchBarTokens.LeadingIconColor.value,
+        unfocusedLeadingIconColor: Color = SearchBarTokens.LeadingIconColor.value,
         disabledLeadingIconColor: Color = FilledTextFieldTokens.DisabledLeadingIconColor
-            .toColor().copy(alpha = FilledTextFieldTokens.DisabledLeadingIconOpacity),
-        focusedTrailingIconColor: Color = SearchBarTokens.TrailingIconColor.toColor(),
-        unfocusedTrailingIconColor: Color = SearchBarTokens.TrailingIconColor.toColor(),
+            .value.copy(alpha = FilledTextFieldTokens.DisabledLeadingIconOpacity),
+        focusedTrailingIconColor: Color = SearchBarTokens.TrailingIconColor.value,
+        unfocusedTrailingIconColor: Color = SearchBarTokens.TrailingIconColor.value,
         disabledTrailingIconColor: Color = FilledTextFieldTokens.DisabledTrailingIconColor
-            .toColor().copy(alpha = FilledTextFieldTokens.DisabledTrailingIconOpacity),
-        focusedPlaceholderColor: Color = SearchBarTokens.SupportingTextColor.toColor(),
-        unfocusedPlaceholderColor: Color = SearchBarTokens.SupportingTextColor.toColor(),
-        disabledPlaceholderColor: Color = FilledTextFieldTokens.DisabledInputColor.toColor()
+            .value.copy(alpha = FilledTextFieldTokens.DisabledTrailingIconOpacity),
+        focusedPlaceholderColor: Color = SearchBarTokens.SupportingTextColor.value,
+        unfocusedPlaceholderColor: Color = SearchBarTokens.SupportingTextColor.value,
+        disabledPlaceholderColor: Color = FilledTextFieldTokens.DisabledInputColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledInputOpacity),
     ): TextFieldColors =
         TextFieldDefaults.colors(
@@ -612,21 +617,21 @@
     @Deprecated("Maintained for binary compatibility", level = DeprecationLevel.HIDDEN)
     @Composable
     fun inputFieldColors(
-        textColor: Color = SearchBarTokens.InputTextColor.toColor(),
-        disabledTextColor: Color = FilledTextFieldTokens.DisabledInputColor.toColor()
+        textColor: Color = SearchBarTokens.InputTextColor.value,
+        disabledTextColor: Color = FilledTextFieldTokens.DisabledInputColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledInputOpacity),
-        cursorColor: Color = FilledTextFieldTokens.CaretColor.toColor(),
+        cursorColor: Color = FilledTextFieldTokens.CaretColor.value,
         selectionColors: TextSelectionColors = LocalTextSelectionColors.current,
-        focusedLeadingIconColor: Color = SearchBarTokens.LeadingIconColor.toColor(),
-        unfocusedLeadingIconColor: Color = SearchBarTokens.LeadingIconColor.toColor(),
+        focusedLeadingIconColor: Color = SearchBarTokens.LeadingIconColor.value,
+        unfocusedLeadingIconColor: Color = SearchBarTokens.LeadingIconColor.value,
         disabledLeadingIconColor: Color = FilledTextFieldTokens.DisabledLeadingIconColor
-            .toColor().copy(alpha = FilledTextFieldTokens.DisabledLeadingIconOpacity),
-        focusedTrailingIconColor: Color = SearchBarTokens.TrailingIconColor.toColor(),
-        unfocusedTrailingIconColor: Color = SearchBarTokens.TrailingIconColor.toColor(),
+            .value.copy(alpha = FilledTextFieldTokens.DisabledLeadingIconOpacity),
+        focusedTrailingIconColor: Color = SearchBarTokens.TrailingIconColor.value,
+        unfocusedTrailingIconColor: Color = SearchBarTokens.TrailingIconColor.value,
         disabledTrailingIconColor: Color = FilledTextFieldTokens.DisabledTrailingIconColor
-            .toColor().copy(alpha = FilledTextFieldTokens.DisabledTrailingIconOpacity),
-        placeholderColor: Color = SearchBarTokens.SupportingTextColor.toColor(),
-        disabledPlaceholderColor: Color = FilledTextFieldTokens.DisabledInputColor.toColor()
+            .value.copy(alpha = FilledTextFieldTokens.DisabledTrailingIconOpacity),
+        placeholderColor: Color = SearchBarTokens.SupportingTextColor.value,
+        disabledPlaceholderColor: Color = FilledTextFieldTokens.DisabledInputColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledInputOpacity),
     ) = inputFieldColors(
         focusedTextColor = textColor,
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/AppBar.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/AppBar.kt
index a774514..c63eeb4 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/AppBar.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/AppBar.kt
@@ -469,7 +469,7 @@
         contentColor = contentColor,
         tonalElevation = tonalElevation,
         // TODO(b/209583788): Consider adding a shape parameter if updated design guidance allows
-        shape = BottomAppBarTokens.ContainerShape.toShape(),
+        shape = BottomAppBarTokens.ContainerShape.value,
         modifier = modifier
     ) {
         Row(
@@ -547,14 +547,14 @@
      */
     @Composable
     fun topAppBarColors(
-        containerColor: Color = TopAppBarSmallTokens.ContainerColor.toColor(),
+        containerColor: Color = TopAppBarSmallTokens.ContainerColor.value,
         scrolledContainerColor: Color = MaterialTheme.colorScheme.applyTonalElevation(
             backgroundColor = containerColor,
             elevation = TopAppBarSmallTokens.OnScrollContainerElevation
         ),
-        navigationIconContentColor: Color = TopAppBarSmallTokens.LeadingIconColor.toColor(),
-        titleContentColor: Color = TopAppBarSmallTokens.HeadlineColor.toColor(),
-        actionIconContentColor: Color = TopAppBarSmallTokens.TrailingIconColor.toColor(),
+        navigationIconContentColor: Color = TopAppBarSmallTokens.LeadingIconColor.value,
+        titleContentColor: Color = TopAppBarSmallTokens.HeadlineColor.value,
+        actionIconContentColor: Color = TopAppBarSmallTokens.TrailingIconColor.value,
     ): TopAppBarColors =
         TopAppBarColors(
             containerColor,
@@ -586,14 +586,14 @@
     )
     @Composable
     fun smallTopAppBarColors(
-        containerColor: Color = TopAppBarSmallTokens.ContainerColor.toColor(),
+        containerColor: Color = TopAppBarSmallTokens.ContainerColor.value,
         scrolledContainerColor: Color = MaterialTheme.colorScheme.applyTonalElevation(
             backgroundColor = containerColor,
             elevation = TopAppBarSmallTokens.OnScrollContainerElevation
         ),
-        navigationIconContentColor: Color = TopAppBarSmallTokens.LeadingIconColor.toColor(),
-        titleContentColor: Color = TopAppBarSmallTokens.HeadlineColor.toColor(),
-        actionIconContentColor: Color = TopAppBarSmallTokens.TrailingIconColor.toColor(),
+        navigationIconContentColor: Color = TopAppBarSmallTokens.LeadingIconColor.value,
+        titleContentColor: Color = TopAppBarSmallTokens.HeadlineColor.value,
+        actionIconContentColor: Color = TopAppBarSmallTokens.TrailingIconColor.value,
     ): TopAppBarColors =
         topAppBarColors(
             containerColor,
@@ -624,14 +624,14 @@
      */
     @Composable
     fun centerAlignedTopAppBarColors(
-        containerColor: Color = TopAppBarSmallCenteredTokens.ContainerColor.toColor(),
+        containerColor: Color = TopAppBarSmallCenteredTokens.ContainerColor.value,
         scrolledContainerColor: Color = MaterialTheme.colorScheme.applyTonalElevation(
             backgroundColor = containerColor,
             elevation = TopAppBarSmallTokens.OnScrollContainerElevation
         ),
-        navigationIconContentColor: Color = TopAppBarSmallCenteredTokens.LeadingIconColor.toColor(),
-        titleContentColor: Color = TopAppBarSmallCenteredTokens.HeadlineColor.toColor(),
-        actionIconContentColor: Color = TopAppBarSmallCenteredTokens.TrailingIconColor.toColor(),
+        navigationIconContentColor: Color = TopAppBarSmallCenteredTokens.LeadingIconColor.value,
+        titleContentColor: Color = TopAppBarSmallCenteredTokens.HeadlineColor.value,
+        actionIconContentColor: Color = TopAppBarSmallCenteredTokens.TrailingIconColor.value,
     ): TopAppBarColors =
         TopAppBarColors(
             containerColor,
@@ -655,14 +655,14 @@
      */
     @Composable
     fun mediumTopAppBarColors(
-        containerColor: Color = TopAppBarMediumTokens.ContainerColor.toColor(),
+        containerColor: Color = TopAppBarMediumTokens.ContainerColor.value,
         scrolledContainerColor: Color = MaterialTheme.colorScheme.applyTonalElevation(
             backgroundColor = containerColor,
             elevation = TopAppBarSmallTokens.OnScrollContainerElevation
         ),
-        navigationIconContentColor: Color = TopAppBarMediumTokens.LeadingIconColor.toColor(),
-        titleContentColor: Color = TopAppBarMediumTokens.HeadlineColor.toColor(),
-        actionIconContentColor: Color = TopAppBarMediumTokens.TrailingIconColor.toColor(),
+        navigationIconContentColor: Color = TopAppBarMediumTokens.LeadingIconColor.value,
+        titleContentColor: Color = TopAppBarMediumTokens.HeadlineColor.value,
+        actionIconContentColor: Color = TopAppBarMediumTokens.TrailingIconColor.value,
     ): TopAppBarColors =
         TopAppBarColors(
             containerColor,
@@ -686,14 +686,14 @@
      */
     @Composable
     fun largeTopAppBarColors(
-        containerColor: Color = TopAppBarLargeTokens.ContainerColor.toColor(),
+        containerColor: Color = TopAppBarLargeTokens.ContainerColor.value,
         scrolledContainerColor: Color = MaterialTheme.colorScheme.applyTonalElevation(
             backgroundColor = containerColor,
             elevation = TopAppBarSmallTokens.OnScrollContainerElevation
         ),
-        navigationIconContentColor: Color = TopAppBarLargeTokens.LeadingIconColor.toColor(),
-        titleContentColor: Color = TopAppBarLargeTokens.HeadlineColor.toColor(),
-        actionIconContentColor: Color = TopAppBarLargeTokens.TrailingIconColor.toColor(),
+        navigationIconContentColor: Color = TopAppBarLargeTokens.LeadingIconColor.value,
+        titleContentColor: Color = TopAppBarLargeTokens.HeadlineColor.value,
+        actionIconContentColor: Color = TopAppBarLargeTokens.TrailingIconColor.value,
     ): TopAppBarColors =
         TopAppBarColors(
             containerColor,
@@ -842,9 +842,9 @@
      * Updates to the [heightOffset] value are coerced between zero and [heightOffsetLimit].
      */
     var heightOffset: Float
-        get() = _heightOffset.value
+        get() = _heightOffset.floatValue
         set(newOffset) {
-            _heightOffset.value = newOffset.coerceIn(
+            _heightOffset.floatValue = newOffset.coerceIn(
                 minimumValue = heightOffsetLimit,
                 maximumValue = 0f
             )
@@ -982,7 +982,7 @@
 object BottomAppBarDefaults {
 
     /** Default color used for [BottomAppBar] container */
-    val containerColor: Color @Composable get() = BottomAppBarTokens.ContainerColor.toColor()
+    val containerColor: Color @Composable get() = BottomAppBarTokens.ContainerColor.value
 
     /** Default elevation used for [BottomAppBar] */
     val ContainerElevation: Dp = BottomAppBarTokens.ContainerElevation
@@ -1010,7 +1010,7 @@
     /** The color of a [BottomAppBar]'s [FloatingActionButton] */
     val bottomAppBarFabColor: Color
         @Composable get() =
-            FabSecondaryTokens.ContainerColor.toColor()
+            FabSecondaryTokens.ContainerColor.value
 }
 
 // Padding minus IconButton's min touch target expansion
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Badge.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Badge.kt
index 811d6fa..bc2ff8e 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Badge.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Badge.kt
@@ -186,9 +186,9 @@
 ) {
     val size = if (content != null) BadgeTokens.LargeSize else BadgeTokens.Size
     val shape = if (content != null) {
-        BadgeTokens.LargeShape.toShape()
+        BadgeTokens.LargeShape.value
     } else {
-        BadgeTokens.Shape.toShape()
+        BadgeTokens.Shape.value
     }
 
     // Draw badge container.
@@ -212,10 +212,7 @@
             CompositionLocalProvider(
                 LocalContentColor provides contentColor
             ) {
-                val style = copyAndSetFontPadding(
-                    style = MaterialTheme.typography.fromToken(BadgeTokens.LargeLabelTextFont),
-                    includeFontPadding = false
-                )
+                val style = MaterialTheme.typography.fromToken(BadgeTokens.LargeLabelTextFont)
                 ProvideTextStyle(
                     value = style,
                     content = { content() }
@@ -229,7 +226,7 @@
 @ExperimentalMaterial3Api
 object BadgeDefaults {
     /** Default container color for a badge. */
-    val containerColor: Color @Composable get() = BadgeTokens.Color.toColor()
+    val containerColor: Color @Composable get() = BadgeTokens.Color.value
 }
 
 /*@VisibleForTesting*/
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/BottomSheetScaffold.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/BottomSheetScaffold.kt
index 43e8c1a..0e30d0c 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/BottomSheetScaffold.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/BottomSheetScaffold.kt
@@ -28,6 +28,7 @@
 import androidx.compose.material3.SheetValue.Hidden
 import androidx.compose.material3.SheetValue.PartiallyExpanded
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
@@ -326,6 +327,11 @@
     containerColor: Color,
     contentColor: Color,
 ) {
+    // b/291735717 Remove this once deprecated methods without density are removed
+    val density = LocalDensity.current
+    SideEffect {
+        sheetState.density = density
+    }
     SubcomposeLayout { constraints ->
         val layoutWidth = constraints.maxWidth
         val layoutHeight = constraints.maxHeight
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Button.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Button.kt
index 09efb85..88b46ce 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Button.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Button.kt
@@ -526,19 +526,19 @@
     val IconSpacing = 8.dp
 
     /** Default shape for a button. */
-    val shape: Shape @Composable get() = FilledButtonTokens.ContainerShape.toShape()
+    val shape: Shape @Composable get() = FilledButtonTokens.ContainerShape.value
 
     /** Default shape for an elevated button. */
-    val elevatedShape: Shape @Composable get() = ElevatedButtonTokens.ContainerShape.toShape()
+    val elevatedShape: Shape @Composable get() = ElevatedButtonTokens.ContainerShape.value
 
     /** Default shape for a filled tonal button. */
-    val filledTonalShape: Shape @Composable get() = FilledTonalButtonTokens.ContainerShape.toShape()
+    val filledTonalShape: Shape @Composable get() = FilledTonalButtonTokens.ContainerShape.value
 
     /** Default shape for an outlined button. */
-    val outlinedShape: Shape @Composable get() = OutlinedButtonTokens.ContainerShape.toShape()
+    val outlinedShape: Shape @Composable get() = OutlinedButtonTokens.ContainerShape.value
 
     /** Default shape for a text button. */
-    val textShape: Shape @Composable get() = TextButtonTokens.ContainerShape.toShape()
+    val textShape: Shape @Composable get() = TextButtonTokens.ContainerShape.value
 
     /**
      * Creates a [ButtonColors] that represents the default container and content colors used in a
@@ -551,12 +551,12 @@
      */
     @Composable
     fun buttonColors(
-        containerColor: Color = FilledButtonTokens.ContainerColor.toColor(),
-        contentColor: Color = FilledButtonTokens.LabelTextColor.toColor(),
+        containerColor: Color = FilledButtonTokens.ContainerColor.value,
+        contentColor: Color = FilledButtonTokens.LabelTextColor.value,
         disabledContainerColor: Color =
-            FilledButtonTokens.DisabledContainerColor.toColor()
+            FilledButtonTokens.DisabledContainerColor.value
                 .copy(alpha = FilledButtonTokens.DisabledContainerOpacity),
-        disabledContentColor: Color = FilledButtonTokens.DisabledLabelTextColor.toColor()
+        disabledContentColor: Color = FilledButtonTokens.DisabledLabelTextColor.value
             .copy(alpha = FilledButtonTokens.DisabledLabelTextOpacity),
     ): ButtonColors = ButtonColors(
         containerColor = containerColor,
@@ -576,13 +576,13 @@
      */
     @Composable
     fun elevatedButtonColors(
-        containerColor: Color = ElevatedButtonTokens.ContainerColor.toColor(),
-        contentColor: Color = ElevatedButtonTokens.LabelTextColor.toColor(),
+        containerColor: Color = ElevatedButtonTokens.ContainerColor.value,
+        contentColor: Color = ElevatedButtonTokens.LabelTextColor.value,
         disabledContainerColor: Color = ElevatedButtonTokens.DisabledContainerColor
-            .toColor()
+            .value
             .copy(alpha = ElevatedButtonTokens.DisabledContainerOpacity),
         disabledContentColor: Color = ElevatedButtonTokens.DisabledLabelTextColor
-            .toColor()
+            .value
             .copy(alpha = ElevatedButtonTokens.DisabledLabelTextOpacity),
     ): ButtonColors = ButtonColors(
         containerColor = containerColor,
@@ -602,13 +602,13 @@
      */
     @Composable
     fun filledTonalButtonColors(
-        containerColor: Color = FilledTonalButtonTokens.ContainerColor.toColor(),
-        contentColor: Color = FilledTonalButtonTokens.LabelTextColor.toColor(),
+        containerColor: Color = FilledTonalButtonTokens.ContainerColor.value,
+        contentColor: Color = FilledTonalButtonTokens.LabelTextColor.value,
         disabledContainerColor: Color = FilledTonalButtonTokens.DisabledContainerColor
-            .toColor()
+            .value
             .copy(alpha = FilledTonalButtonTokens.DisabledContainerOpacity),
         disabledContentColor: Color = FilledTonalButtonTokens.DisabledLabelTextColor
-            .toColor()
+            .value
             .copy(alpha = FilledTonalButtonTokens.DisabledLabelTextOpacity),
     ): ButtonColors = ButtonColors(
         containerColor = containerColor,
@@ -629,10 +629,10 @@
     @Composable
     fun outlinedButtonColors(
         containerColor: Color = Color.Transparent,
-        contentColor: Color = OutlinedButtonTokens.LabelTextColor.toColor(),
+        contentColor: Color = OutlinedButtonTokens.LabelTextColor.value,
         disabledContainerColor: Color = Color.Transparent,
         disabledContentColor: Color = OutlinedButtonTokens.DisabledLabelTextColor
-            .toColor()
+            .value
             .copy(alpha = OutlinedButtonTokens.DisabledLabelTextOpacity),
     ): ButtonColors = ButtonColors(
         containerColor = containerColor,
@@ -653,10 +653,10 @@
     @Composable
     fun textButtonColors(
         containerColor: Color = Color.Transparent,
-        contentColor: Color = TextButtonTokens.LabelTextColor.toColor(),
+        contentColor: Color = TextButtonTokens.LabelTextColor.value,
         disabledContainerColor: Color = Color.Transparent,
         disabledContentColor: Color = TextButtonTokens.DisabledLabelTextColor
-            .toColor()
+            .value
             .copy(alpha = TextButtonTokens.DisabledLabelTextOpacity),
     ): ButtonColors = ButtonColors(
         containerColor = containerColor,
@@ -749,7 +749,7 @@
         @Composable
         get() = BorderStroke(
             width = OutlinedButtonTokens.OutlineWidth,
-            color = OutlinedButtonTokens.OutlineColor.toColor(),
+            color = OutlinedButtonTokens.OutlineColor.value,
         )
 }
 
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Card.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Card.kt
index cf4ce4c..8a55fb0 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Card.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Card.kt
@@ -352,13 +352,13 @@
 object CardDefaults {
     // shape Defaults
     /** Default shape for a card. */
-    val shape: Shape @Composable get() = FilledCardTokens.ContainerShape.toShape()
+    val shape: Shape @Composable get() = FilledCardTokens.ContainerShape.value
 
     /** Default shape for an elevated card. */
-    val elevatedShape: Shape @Composable get() = ElevatedCardTokens.ContainerShape.toShape()
+    val elevatedShape: Shape @Composable get() = ElevatedCardTokens.ContainerShape.value
 
     /** Default shape for an outlined card. */
-    val outlinedShape: Shape @Composable get() = OutlinedCardTokens.ContainerShape.toShape()
+    val outlinedShape: Shape @Composable get() = OutlinedCardTokens.ContainerShape.value
 
     /**
      * Creates a [CardElevation] that will animate between the provided values according to the
@@ -454,10 +454,10 @@
      */
     @Composable
     fun cardColors(
-        containerColor: Color = FilledCardTokens.ContainerColor.toColor(),
+        containerColor: Color = FilledCardTokens.ContainerColor.value,
         contentColor: Color = contentColorFor(containerColor),
         disabledContainerColor: Color =
-            FilledCardTokens.DisabledContainerColor.toColor()
+            FilledCardTokens.DisabledContainerColor.value
                 .copy(alpha = FilledCardTokens.DisabledContainerOpacity)
                 .compositeOver(
                     MaterialTheme.colorScheme.surfaceColorAtElevation(
@@ -483,10 +483,10 @@
      */
     @Composable
     fun elevatedCardColors(
-        containerColor: Color = ElevatedCardTokens.ContainerColor.toColor(),
+        containerColor: Color = ElevatedCardTokens.ContainerColor.value,
         contentColor: Color = contentColorFor(containerColor),
         disabledContainerColor: Color =
-            ElevatedCardTokens.DisabledContainerColor.toColor()
+            ElevatedCardTokens.DisabledContainerColor.value
                 .copy(alpha = ElevatedCardTokens.DisabledContainerOpacity)
                 .compositeOver(
                     MaterialTheme.colorScheme.surfaceColorAtElevation(
@@ -513,7 +513,7 @@
      */
     @Composable
     fun outlinedCardColors(
-        containerColor: Color = OutlinedCardTokens.ContainerColor.toColor(),
+        containerColor: Color = OutlinedCardTokens.ContainerColor.value,
         contentColor: Color = contentColorFor(containerColor),
         disabledContainerColor: Color = containerColor,
         disabledContentColor: Color = contentColor.copy(DisabledAlpha),
@@ -533,9 +533,9 @@
     @Composable
     fun outlinedCardBorder(enabled: Boolean = true): BorderStroke {
         val color = if (enabled) {
-            OutlinedCardTokens.OutlineColor.toColor()
+            OutlinedCardTokens.OutlineColor.value
         } else {
-            OutlinedCardTokens.DisabledOutlineColor.toColor()
+            OutlinedCardTokens.DisabledOutlineColor.value
                 .copy(alpha = OutlinedCardTokens.DisabledOutlineOpacity)
                 .compositeOver(
                     MaterialTheme.colorScheme.surfaceColorAtElevation(
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Checkbox.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Checkbox.kt
index 55571a3..3191c2f 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Checkbox.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Checkbox.kt
@@ -185,44 +185,39 @@
      * Material specification.
      *
      * @param checkedColor the color that will be used for the border and box when checked
-     * @param uncheckedColor color that will be used for the border when unchecked
+     * @param uncheckedColor color that will be used for the border when unchecked. By default, the
+     * inner box is transparent when unchecked.
      * @param checkmarkColor color that will be used for the checkmark when checked
      * @param disabledCheckedColor color that will be used for the box and border when disabled and
      * checked
-     * @param disabledUncheckedColor color that will be used for the box and border when disabled
-     * and not checked
+     * @param disabledUncheckedColor color that will be used for the border when disabled and
+     * unchecked. By default, the inner box is transparent when unchecked.
      * @param disabledIndeterminateColor color that will be used for the box and
-     * border in a [TriStateCheckbox] when disabled AND in an [ToggleableState.Indeterminate] state.
+     * border in a [TriStateCheckbox] when disabled AND in an [ToggleableState.Indeterminate] state
      */
     @Composable
     fun colors(
-        checkedColor: Color =
-            MaterialTheme.colorScheme.fromToken(CheckboxTokens.SelectedContainerColor),
-        uncheckedColor: Color =
-            MaterialTheme.colorScheme.fromToken(CheckboxTokens.UnselectedOutlineColor),
-        checkmarkColor: Color =
-            MaterialTheme.colorScheme.fromToken(CheckboxTokens.SelectedIconColor),
-        disabledCheckedColor: Color =
-            MaterialTheme.colorScheme
-                .fromToken(CheckboxTokens.SelectedDisabledContainerColor)
-                .copy(alpha = CheckboxTokens.SelectedDisabledContainerOpacity),
-        disabledUncheckedColor: Color =
-            MaterialTheme.colorScheme
-                .fromToken(CheckboxTokens.UnselectedDisabledOutlineColor)
-                .copy(alpha = CheckboxTokens.UnselectedDisabledContainerOpacity),
+        checkedColor: Color = CheckboxTokens.SelectedContainerColor.value,
+        uncheckedColor: Color = CheckboxTokens.UnselectedOutlineColor.value,
+        checkmarkColor: Color = CheckboxTokens.SelectedIconColor.value,
+        disabledCheckedColor: Color = CheckboxTokens.SelectedDisabledContainerColor.value
+            .copy(alpha = CheckboxTokens.SelectedDisabledContainerOpacity),
+        disabledUncheckedColor: Color = CheckboxTokens.UnselectedDisabledOutlineColor.value
+            .copy(alpha = CheckboxTokens.UnselectedDisabledContainerOpacity),
         disabledIndeterminateColor: Color = disabledCheckedColor
     ): CheckboxColors = CheckboxColors(
-        checkedBorderColor = checkedColor,
-        checkedBoxColor = checkedColor,
         checkedCheckmarkColor = checkmarkColor,
-        uncheckedCheckmarkColor = checkmarkColor.copy(alpha = 0f),
-        uncheckedBoxColor = checkedColor.copy(alpha = 0f),
+        uncheckedCheckmarkColor = Color.Transparent,
+        checkedBoxColor = checkedColor,
+        uncheckedBoxColor = Color.Transparent,
         disabledCheckedBoxColor = disabledCheckedColor,
-        disabledUncheckedBoxColor = disabledUncheckedColor.copy(alpha = 0f),
+        disabledUncheckedBoxColor = Color.Transparent,
         disabledIndeterminateBoxColor = disabledIndeterminateColor,
+        checkedBorderColor = checkedColor,
         uncheckedBorderColor = uncheckedColor,
         disabledBorderColor = disabledCheckedColor,
-        disabledIndeterminateBorderColor = disabledIndeterminateColor,
+        disabledUncheckedBorderColor = disabledUncheckedColor,
+        disabledIndeterminateBorderColor = disabledIndeterminateColor
     )
 }
 
@@ -380,17 +375,17 @@
  * @param uncheckedCheckmarkColor color that will be used for the checkmark when unchecked
  * @param checkedBoxColor the color that will be used for the box when checked
  * @param uncheckedBoxColor color that will be used for the box when unchecked
- * @param disabledCheckedBoxColor color that will be used for the box when disabled and
- * checked
- * @param disabledUncheckedBoxColor color that will be used for the box and border when disabled
- * and not checked
- * @param disabledIndeterminateBoxColor color that will be used for the box and
- * border in a [TriStateCheckbox] when disabled AND in an [ToggleableState.Indeterminate] state.
+ * @param disabledCheckedBoxColor color that will be used for the box when disabled and checked
+ * @param disabledUncheckedBoxColor color that will be used for the box when disabled and unchecked
+ * @param disabledIndeterminateBoxColor color that will be used for the box and border in a
+ * [TriStateCheckbox] when disabled AND in an [ToggleableState.Indeterminate] state.
  * @param checkedBorderColor color that will be used for the border when checked
  * @param uncheckedBorderColor color that will be used for the border when unchecked
- * @param disabledBorderColor color that will be used for the border when disabled
- * @param disabledIndeterminateBorderColor color that will be used for the border when in an
- * [ToggleableState.Indeterminate] state.
+ * @param disabledBorderColor color that will be used for the border when disabled and checked
+ * @param disabledUncheckedBorderColor color that will be used for the border when disabled and
+ * unchecked
+ * @param disabledIndeterminateBorderColor color that will be used for the border when disabled and
+ * in an [ToggleableState.Indeterminate] state.
  */
 @Immutable
 class CheckboxColors constructor(
@@ -404,6 +399,7 @@
     val checkedBorderColor: Color,
     val uncheckedBorderColor: Color,
     val disabledBorderColor: Color,
+    val disabledUncheckedBorderColor: Color,
     val disabledIndeterminateBorderColor: Color
 ) {
     /**
@@ -471,7 +467,8 @@
         } else {
             when (state) {
                 ToggleableState.Indeterminate -> disabledIndeterminateBorderColor
-                ToggleableState.On, ToggleableState.Off -> disabledBorderColor
+                ToggleableState.On -> disabledBorderColor
+                ToggleableState.Off -> disabledUncheckedBorderColor
             }
         }
 
@@ -499,6 +496,7 @@
         if (checkedBorderColor != other.checkedBorderColor) return false
         if (uncheckedBorderColor != other.uncheckedBorderColor) return false
         if (disabledBorderColor != other.disabledBorderColor) return false
+        if (disabledUncheckedBorderColor != other.disabledUncheckedBorderColor) return false
         if (disabledIndeterminateBorderColor != other.disabledIndeterminateBorderColor) return false
 
         return true
@@ -515,6 +513,7 @@
         result = 31 * result + checkedBorderColor.hashCode()
         result = 31 * result + uncheckedBorderColor.hashCode()
         result = 31 * result + disabledBorderColor.hashCode()
+        result = 31 * result + disabledUncheckedBorderColor.hashCode()
         result = 31 * result + disabledIndeterminateBorderColor.hashCode()
         return result
     }
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
index 897d1d4..4f00d2c 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Chip.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Chip.kt
@@ -440,7 +440,7 @@
     var shapedAvatar: @Composable (() -> Unit)? = null
     if (avatar != null) {
         val avatarOpacity = if (enabled) 1f else InputChipTokens.DisabledAvatarOpacity
-        val avatarShape = InputChipTokens.AvatarShape.toShape()
+        val avatarShape = InputChipTokens.AvatarShape.value
         shapedAvatar = @Composable {
             Box(
                 modifier = Modifier.graphicsLayer {
@@ -646,14 +646,14 @@
     @Composable
     fun assistChipColors(
         containerColor: Color = Color.Transparent,
-        labelColor: Color = AssistChipTokens.LabelTextColor.toColor(),
-        leadingIconContentColor: Color = AssistChipTokens.IconColor.toColor(),
+        labelColor: Color = AssistChipTokens.LabelTextColor.value,
+        leadingIconContentColor: Color = AssistChipTokens.IconColor.value,
         trailingIconContentColor: Color = leadingIconContentColor,
         disabledContainerColor: Color = Color.Transparent,
-        disabledLabelColor: Color = AssistChipTokens.DisabledLabelTextColor.toColor()
+        disabledLabelColor: Color = AssistChipTokens.DisabledLabelTextColor.value
             .copy(alpha = AssistChipTokens.DisabledLabelTextOpacity),
         disabledLeadingIconContentColor: Color =
-            AssistChipTokens.DisabledIconColor.toColor()
+            AssistChipTokens.DisabledIconColor.value
                 .copy(alpha = AssistChipTokens.DisabledIconOpacity),
         disabledTrailingIconContentColor: Color = disabledLeadingIconContentColor,
     ): ChipColors = ChipColors(
@@ -705,8 +705,8 @@
      */
     @Composable
     fun assistChipBorder(
-        borderColor: Color = AssistChipTokens.FlatOutlineColor.toColor(),
-        disabledBorderColor: Color = AssistChipTokens.FlatDisabledOutlineColor.toColor()
+        borderColor: Color = AssistChipTokens.FlatOutlineColor.value,
+        disabledBorderColor: Color = AssistChipTokens.FlatDisabledOutlineColor.value
             .copy(alpha = AssistChipTokens.FlatDisabledOutlineOpacity),
         borderWidth: Dp = AssistChipTokens.FlatOutlineWidth,
     ): ChipBorder = ChipBorder(
@@ -730,16 +730,16 @@
      */
     @Composable
     fun elevatedAssistChipColors(
-        containerColor: Color = AssistChipTokens.ElevatedContainerColor.toColor(),
-        labelColor: Color = AssistChipTokens.LabelTextColor.toColor(),
-        leadingIconContentColor: Color = AssistChipTokens.IconColor.toColor(),
+        containerColor: Color = AssistChipTokens.ElevatedContainerColor.value,
+        labelColor: Color = AssistChipTokens.LabelTextColor.value,
+        leadingIconContentColor: Color = AssistChipTokens.IconColor.value,
         trailingIconContentColor: Color = leadingIconContentColor,
-        disabledContainerColor: Color = AssistChipTokens.ElevatedDisabledContainerColor.toColor()
+        disabledContainerColor: Color = AssistChipTokens.ElevatedDisabledContainerColor.value
             .copy(alpha = AssistChipTokens.ElevatedDisabledContainerOpacity),
-        disabledLabelColor: Color = AssistChipTokens.DisabledLabelTextColor.toColor()
+        disabledLabelColor: Color = AssistChipTokens.DisabledLabelTextColor.value
             .copy(alpha = AssistChipTokens.DisabledLabelTextOpacity),
         disabledLeadingIconContentColor: Color =
-            AssistChipTokens.DisabledIconColor.toColor()
+            AssistChipTokens.DisabledIconColor.value
                 .copy(alpha = AssistChipTokens.DisabledIconOpacity),
         disabledTrailingIconContentColor: Color = disabledLeadingIconContentColor,
     ): ChipColors = ChipColors(
@@ -783,7 +783,7 @@
     )
 
     /** Default shape of an assist chip. */
-    val shape: Shape @Composable get() = AssistChipTokens.ContainerShape.toShape()
+    val shape: Shape @Composable get() = AssistChipTokens.ContainerShape.value
 }
 
 /**
@@ -823,20 +823,20 @@
     @Composable
     fun filterChipColors(
         containerColor: Color = Color.Transparent,
-        labelColor: Color = FilterChipTokens.UnselectedLabelTextColor.toColor(),
-        iconColor: Color = FilterChipTokens.LeadingIconUnselectedColor.toColor(),
+        labelColor: Color = FilterChipTokens.UnselectedLabelTextColor.value,
+        iconColor: Color = FilterChipTokens.LeadingIconUnselectedColor.value,
         disabledContainerColor: Color = Color.Transparent,
-        disabledLabelColor: Color = FilterChipTokens.DisabledLabelTextColor.toColor()
+        disabledLabelColor: Color = FilterChipTokens.DisabledLabelTextColor.value
             .copy(alpha = FilterChipTokens.DisabledLabelTextOpacity),
-        disabledLeadingIconColor: Color = FilterChipTokens.DisabledLeadingIconColor.toColor()
+        disabledLeadingIconColor: Color = FilterChipTokens.DisabledLeadingIconColor.value
             .copy(alpha = FilterChipTokens.DisabledLeadingIconOpacity),
         disabledTrailingIconColor: Color = disabledLeadingIconColor,
-        selectedContainerColor: Color = FilterChipTokens.FlatSelectedContainerColor.toColor(),
+        selectedContainerColor: Color = FilterChipTokens.FlatSelectedContainerColor.value,
         disabledSelectedContainerColor: Color =
-            FilterChipTokens.FlatDisabledSelectedContainerColor.toColor()
+            FilterChipTokens.FlatDisabledSelectedContainerColor.value
                 .copy(alpha = FilterChipTokens.FlatDisabledSelectedContainerOpacity),
-        selectedLabelColor: Color = FilterChipTokens.SelectedLabelTextColor.toColor(),
-        selectedLeadingIconColor: Color = FilterChipTokens.SelectedLeadingIconColor.toColor(),
+        selectedLabelColor: Color = FilterChipTokens.SelectedLabelTextColor.value,
+        selectedLeadingIconColor: Color = FilterChipTokens.SelectedLeadingIconColor.value,
         selectedTrailingIconColor: Color = selectedLeadingIconColor
     ): SelectableChipColors = SelectableChipColors(
         containerColor = containerColor,
@@ -898,9 +898,9 @@
      */
     @Composable
     fun filterChipBorder(
-        borderColor: Color = FilterChipTokens.FlatUnselectedOutlineColor.toColor(),
+        borderColor: Color = FilterChipTokens.FlatUnselectedOutlineColor.value,
         selectedBorderColor: Color = Color.Transparent,
-        disabledBorderColor: Color = FilterChipTokens.FlatDisabledUnselectedOutlineColor.toColor()
+        disabledBorderColor: Color = FilterChipTokens.FlatDisabledUnselectedOutlineColor.value
             .copy(alpha = FilterChipTokens.FlatDisabledUnselectedOutlineOpacity),
         disabledSelectedBorderColor: Color = Color.Transparent,
         borderWidth: Dp = FilterChipTokens.FlatUnselectedOutlineWidth,
@@ -934,20 +934,20 @@
      */
     @Composable
     fun elevatedFilterChipColors(
-        containerColor: Color = FilterChipTokens.ElevatedUnselectedContainerColor.toColor(),
-        labelColor: Color = FilterChipTokens.UnselectedLabelTextColor.toColor(),
-        iconColor: Color = FilterChipTokens.LeadingIconUnselectedColor.toColor(),
-        disabledContainerColor: Color = FilterChipTokens.ElevatedDisabledContainerColor.toColor()
+        containerColor: Color = FilterChipTokens.ElevatedUnselectedContainerColor.value,
+        labelColor: Color = FilterChipTokens.UnselectedLabelTextColor.value,
+        iconColor: Color = FilterChipTokens.LeadingIconUnselectedColor.value,
+        disabledContainerColor: Color = FilterChipTokens.ElevatedDisabledContainerColor.value
             .copy(alpha = FilterChipTokens.ElevatedDisabledContainerOpacity),
-        disabledLabelColor: Color = FilterChipTokens.DisabledLabelTextColor.toColor()
+        disabledLabelColor: Color = FilterChipTokens.DisabledLabelTextColor.value
             .copy(alpha = FilterChipTokens.DisabledLabelTextOpacity),
-        disabledLeadingIconColor: Color = FilterChipTokens.DisabledLeadingIconColor.toColor()
+        disabledLeadingIconColor: Color = FilterChipTokens.DisabledLeadingIconColor.value
             .copy(alpha = FilterChipTokens.DisabledLeadingIconOpacity),
         disabledTrailingIconColor: Color = disabledLeadingIconColor,
-        selectedContainerColor: Color = FilterChipTokens.ElevatedSelectedContainerColor.toColor(),
+        selectedContainerColor: Color = FilterChipTokens.ElevatedSelectedContainerColor.value,
         disabledSelectedContainerColor: Color = disabledContainerColor,
-        selectedLabelColor: Color = FilterChipTokens.SelectedLabelTextColor.toColor(),
-        selectedLeadingIconColor: Color = FilterChipTokens.SelectedLeadingIconColor.toColor(),
+        selectedLabelColor: Color = FilterChipTokens.SelectedLabelTextColor.value,
+        selectedLeadingIconColor: Color = FilterChipTokens.SelectedLeadingIconColor.value,
         selectedTrailingIconColor: Color = selectedLeadingIconColor
     ): SelectableChipColors = SelectableChipColors(
         containerColor = containerColor,
@@ -995,7 +995,7 @@
     )
 
     /** Default shape of a filter chip. */
-    val shape: Shape @Composable get() = FilterChipTokens.ContainerShape.toShape()
+    val shape: Shape @Composable get() = FilterChipTokens.ContainerShape.value
 }
 
 /**
@@ -1041,23 +1041,23 @@
     @Composable
     fun inputChipColors(
         containerColor: Color = Color.Transparent,
-        labelColor: Color = InputChipTokens.UnselectedLabelTextColor.toColor(),
-        leadingIconColor: Color = InputChipTokens.UnselectedLeadingIconColor.toColor(),
-        trailingIconColor: Color = InputChipTokens.UnselectedTrailingIconColor.toColor(),
+        labelColor: Color = InputChipTokens.UnselectedLabelTextColor.value,
+        leadingIconColor: Color = InputChipTokens.UnselectedLeadingIconColor.value,
+        trailingIconColor: Color = InputChipTokens.UnselectedTrailingIconColor.value,
         disabledContainerColor: Color = Color.Transparent,
-        disabledLabelColor: Color = InputChipTokens.DisabledLabelTextColor.toColor()
+        disabledLabelColor: Color = InputChipTokens.DisabledLabelTextColor.value
             .copy(alpha = InputChipTokens.DisabledLabelTextOpacity),
-        disabledLeadingIconColor: Color = InputChipTokens.DisabledLeadingIconColor.toColor()
+        disabledLeadingIconColor: Color = InputChipTokens.DisabledLeadingIconColor.value
             .copy(alpha = InputChipTokens.DisabledLeadingIconOpacity),
-        disabledTrailingIconColor: Color = InputChipTokens.DisabledTrailingIconColor.toColor()
+        disabledTrailingIconColor: Color = InputChipTokens.DisabledTrailingIconColor.value
             .copy(alpha = InputChipTokens.DisabledTrailingIconOpacity),
-        selectedContainerColor: Color = InputChipTokens.SelectedContainerColor.toColor(),
+        selectedContainerColor: Color = InputChipTokens.SelectedContainerColor.value,
         disabledSelectedContainerColor: Color =
-            InputChipTokens.DisabledSelectedContainerColor.toColor()
+            InputChipTokens.DisabledSelectedContainerColor.value
                 .copy(alpha = InputChipTokens.DisabledSelectedContainerOpacity),
-        selectedLabelColor: Color = InputChipTokens.SelectedLabelTextColor.toColor(),
-        selectedLeadingIconColor: Color = InputChipTokens.SelectedLeadingIconColor.toColor(),
-        selectedTrailingIconColor: Color = InputChipTokens.SelectedTrailingIconColor.toColor()
+        selectedLabelColor: Color = InputChipTokens.SelectedLabelTextColor.value,
+        selectedLeadingIconColor: Color = InputChipTokens.SelectedLeadingIconColor.value,
+        selectedTrailingIconColor: Color = InputChipTokens.SelectedTrailingIconColor.value
     ): SelectableChipColors = SelectableChipColors(
         containerColor = containerColor,
         labelColor = labelColor,
@@ -1117,9 +1117,9 @@
      */
     @Composable
     fun inputChipBorder(
-        borderColor: Color = InputChipTokens.UnselectedOutlineColor.toColor(),
+        borderColor: Color = InputChipTokens.UnselectedOutlineColor.value,
         selectedBorderColor: Color = Color.Transparent,
-        disabledBorderColor: Color = InputChipTokens.DisabledUnselectedOutlineColor.toColor()
+        disabledBorderColor: Color = InputChipTokens.DisabledUnselectedOutlineColor.value
             .copy(alpha = InputChipTokens.DisabledUnselectedOutlineOpacity),
         disabledSelectedBorderColor: Color = Color.Transparent,
         borderWidth: Dp = InputChipTokens.UnselectedOutlineWidth,
@@ -1134,7 +1134,7 @@
     )
 
     /** Default shape of an input chip. */
-    val shape: Shape @Composable get() = InputChipTokens.ContainerShape.toShape()
+    val shape: Shape @Composable get() = InputChipTokens.ContainerShape.value
 }
 
 /**
@@ -1166,12 +1166,12 @@
     @Composable
     fun suggestionChipColors(
         containerColor: Color = Color.Transparent,
-        labelColor: Color = SuggestionChipTokens.LabelTextColor.toColor(),
-        iconContentColor: Color = SuggestionChipTokens.LeadingIconColor.toColor(),
+        labelColor: Color = SuggestionChipTokens.LabelTextColor.value,
+        iconContentColor: Color = SuggestionChipTokens.LeadingIconColor.value,
         disabledContainerColor: Color = Color.Transparent,
-        disabledLabelColor: Color = SuggestionChipTokens.DisabledLabelTextColor.toColor()
+        disabledLabelColor: Color = SuggestionChipTokens.DisabledLabelTextColor.value
             .copy(alpha = SuggestionChipTokens.DisabledLabelTextOpacity),
-        disabledIconContentColor: Color = SuggestionChipTokens.DisabledLeadingIconColor.toColor()
+        disabledIconContentColor: Color = SuggestionChipTokens.DisabledLeadingIconColor.value
             .copy(alpha = SuggestionChipTokens.DisabledLeadingIconOpacity)
     ): ChipColors = ChipColors(
         containerColor = containerColor,
@@ -1222,8 +1222,8 @@
      */
     @Composable
     fun suggestionChipBorder(
-        borderColor: Color = SuggestionChipTokens.FlatOutlineColor.toColor(),
-        disabledBorderColor: Color = SuggestionChipTokens.FlatDisabledOutlineColor.toColor()
+        borderColor: Color = SuggestionChipTokens.FlatOutlineColor.value,
+        disabledBorderColor: Color = SuggestionChipTokens.FlatDisabledOutlineColor.value
             .copy(alpha = SuggestionChipTokens.FlatDisabledOutlineOpacity),
         borderWidth: Dp = SuggestionChipTokens.FlatOutlineWidth,
     ): ChipBorder = ChipBorder(
@@ -1245,15 +1245,15 @@
      */
     @Composable
     fun elevatedSuggestionChipColors(
-        containerColor: Color = SuggestionChipTokens.ElevatedContainerColor.toColor(),
-        labelColor: Color = SuggestionChipTokens.LabelTextColor.toColor(),
+        containerColor: Color = SuggestionChipTokens.ElevatedContainerColor.value,
+        labelColor: Color = SuggestionChipTokens.LabelTextColor.value,
         // TODO(b/229778210) Read from the tokens when available
         //  (i.e. SuggestionChipTokens.IconColor.toColor()).
         iconContentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
         disabledContainerColor: Color =
-            SuggestionChipTokens.ElevatedDisabledContainerColor.toColor()
+            SuggestionChipTokens.ElevatedDisabledContainerColor.value
                 .copy(alpha = SuggestionChipTokens.ElevatedDisabledContainerOpacity),
-        disabledLabelColor: Color = SuggestionChipTokens.DisabledLabelTextColor.toColor()
+        disabledLabelColor: Color = SuggestionChipTokens.DisabledLabelTextColor.value
             .copy(alpha = SuggestionChipTokens.DisabledLabelTextOpacity),
         // TODO(b/229778210): Read from the tokens when available.
         disabledIconContentColor: Color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)
@@ -1298,7 +1298,7 @@
     )
 
     /** Default shape of a suggestion chip. */
-    val shape: Shape @Composable get() = SuggestionChipTokens.ContainerShape.toShape()
+    val shape: Shape @Composable get() = SuggestionChipTokens.ContainerShape.value
 }
 
 @Composable
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ColorScheme.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ColorScheme.kt
index 279033a..1684398 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ColorScheme.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ColorScheme.kt
@@ -1005,9 +1005,11 @@
  */
 internal const val DisabledAlpha = 0.38f
 
-/** Converts a color token key to the local color scheme provided by the theme */
-@ReadOnlyComposable
-@Composable
-internal fun ColorSchemeKeyTokens.toColor(): Color {
-    return MaterialTheme.colorScheme.fromToken(this)
-}
+/**
+ * Converts a color token key to the local color scheme provided by the theme
+ * The color is subscribed to [LocalColorScheme] changes.
+ */
+internal val ColorSchemeKeyTokens.value: Color
+    @ReadOnlyComposable
+    @Composable
+    get() = MaterialTheme.colorScheme.fromToken(this)
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/DatePicker.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/DatePicker.kt
index 9b34d5e..67134c1 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/DatePicker.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/DatePicker.kt
@@ -422,43 +422,43 @@
      */
     @Composable
     fun colors(
-        containerColor: Color = DatePickerModalTokens.ContainerColor.toColor(),
-        titleContentColor: Color = DatePickerModalTokens.HeaderSupportingTextColor.toColor(),
-        headlineContentColor: Color = DatePickerModalTokens.HeaderHeadlineColor.toColor(),
-        weekdayContentColor: Color = DatePickerModalTokens.WeekdaysLabelTextColor.toColor(),
+        containerColor: Color = DatePickerModalTokens.ContainerColor.value,
+        titleContentColor: Color = DatePickerModalTokens.HeaderSupportingTextColor.value,
+        headlineContentColor: Color = DatePickerModalTokens.HeaderHeadlineColor.value,
+        weekdayContentColor: Color = DatePickerModalTokens.WeekdaysLabelTextColor.value,
         subheadContentColor: Color =
-            DatePickerModalTokens.RangeSelectionMonthSubheadColor.toColor(),
+            DatePickerModalTokens.RangeSelectionMonthSubheadColor.value,
         // TODO(b/234060211): Apply this from the MenuButton tokens or defaults.
         navigationContentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
         yearContentColor: Color =
-            DatePickerModalTokens.SelectionYearUnselectedLabelTextColor.toColor(),
+            DatePickerModalTokens.SelectionYearUnselectedLabelTextColor.value,
         // TODO: Using DisabledAlpha as there are no token values for the disabled states.
         disabledYearContentColor: Color = yearContentColor.copy(alpha = DisabledAlpha),
-        currentYearContentColor: Color = DatePickerModalTokens.DateTodayLabelTextColor.toColor(),
+        currentYearContentColor: Color = DatePickerModalTokens.DateTodayLabelTextColor.value,
         selectedYearContentColor: Color =
-            DatePickerModalTokens.SelectionYearSelectedLabelTextColor.toColor(),
+            DatePickerModalTokens.SelectionYearSelectedLabelTextColor.value,
         disabledSelectedYearContentColor: Color =
             selectedYearContentColor.copy(alpha = DisabledAlpha),
         selectedYearContainerColor: Color =
-            DatePickerModalTokens.SelectionYearSelectedContainerColor.toColor(),
+            DatePickerModalTokens.SelectionYearSelectedContainerColor.value,
         disabledSelectedYearContainerColor: Color =
             selectedYearContainerColor.copy(alpha = DisabledAlpha),
-        dayContentColor: Color = DatePickerModalTokens.DateUnselectedLabelTextColor.toColor(),
+        dayContentColor: Color = DatePickerModalTokens.DateUnselectedLabelTextColor.value,
         disabledDayContentColor: Color = dayContentColor.copy(alpha = DisabledAlpha),
-        selectedDayContentColor: Color = DatePickerModalTokens.DateSelectedLabelTextColor.toColor(),
+        selectedDayContentColor: Color = DatePickerModalTokens.DateSelectedLabelTextColor.value,
         disabledSelectedDayContentColor: Color =
             selectedDayContentColor.copy(alpha = DisabledAlpha),
         selectedDayContainerColor: Color =
-            DatePickerModalTokens.DateSelectedContainerColor.toColor(),
+            DatePickerModalTokens.DateSelectedContainerColor.value,
         disabledSelectedDayContainerColor: Color =
             selectedDayContainerColor.copy(alpha = DisabledAlpha),
-        todayContentColor: Color = DatePickerModalTokens.DateTodayLabelTextColor.toColor(),
+        todayContentColor: Color = DatePickerModalTokens.DateTodayLabelTextColor.value,
         todayDateBorderColor: Color =
-            DatePickerModalTokens.DateTodayContainerOutlineColor.toColor(),
+            DatePickerModalTokens.DateTodayContainerOutlineColor.value,
         dayInSelectionRangeContentColor: Color =
-            DatePickerModalTokens.SelectionDateInRangeLabelTextColor.toColor(),
+            DatePickerModalTokens.SelectionDateInRangeLabelTextColor.value,
         dayInSelectionRangeContainerColor: Color =
-            DatePickerModalTokens.RangeSelectionActiveIndicatorContainerColor.toColor(),
+            DatePickerModalTokens.RangeSelectionActiveIndicatorContainerColor.value,
         dividerColor: Color = DividerDefaults.color,
         dateTextFieldColors: TextFieldColors = OutlinedTextFieldDefaults.colors()
     ): DatePickerColors =
@@ -622,7 +622,7 @@
     val TonalElevation: Dp = DatePickerModalTokens.ContainerElevation
 
     /** The default shape for date picker dialogs. */
-    val shape: Shape @Composable get() = DatePickerModalTokens.ContainerShape.toShape()
+    val shape: Shape @Composable get() = DatePickerModalTokens.ContainerShape.value
 
     /**
      * A date format skeleton used to format the date picker's year selection menu button (e.g.
@@ -1773,7 +1773,7 @@
                 role = Role.Button
             },
         enabled = enabled,
-        shape = DatePickerModalTokens.DateContainerShape.toShape(),
+        shape = DatePickerModalTokens.DateContainerShape.value,
         color = colors.dayContainerColor(
             selected = selected,
             enabled = enabled,
@@ -1925,7 +1925,7 @@
             role = Role.Button
         },
         enabled = enabled,
-        shape = DatePickerModalTokens.SelectionYearStateLayerShape.toShape(),
+        shape = DatePickerModalTokens.SelectionYearStateLayerShape.value,
         color = colors.yearContainerColor(selected = selected, enabled = enabled).value,
         contentColor = colors.yearContentColor(
             currentYear = currentYear,
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Divider.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Divider.kt
index c2fd8b1..b99a1291 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Divider.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Divider.kt
@@ -113,5 +113,5 @@
     val Thickness: Dp = DividerTokens.Thickness
 
     /** Default color of a divider. */
-    val color: Color @Composable get() = DividerTokens.Color.toColor()
+    val color: Color @Composable get() = DividerTokens.Color.value
 }
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/FloatingActionButton.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/FloatingActionButton.kt
index 42f3f07..127c07b 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/FloatingActionButton.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/FloatingActionButton.kt
@@ -395,20 +395,20 @@
     val LargeIconSize = FabPrimaryLargeTokens.IconSize
 
     /** Default shape for a floating action button. */
-    val shape: Shape @Composable get() = FabPrimaryTokens.ContainerShape.toShape()
+    val shape: Shape @Composable get() = FabPrimaryTokens.ContainerShape.value
 
     /** Default shape for a small floating action button. */
-    val smallShape: Shape @Composable get() = FabPrimarySmallTokens.ContainerShape.toShape()
+    val smallShape: Shape @Composable get() = FabPrimarySmallTokens.ContainerShape.value
 
     /** Default shape for a large floating action button. */
-    val largeShape: Shape @Composable get() = FabPrimaryLargeTokens.ContainerShape.toShape()
+    val largeShape: Shape @Composable get() = FabPrimaryLargeTokens.ContainerShape.value
 
     /** Default shape for an extended floating action button. */
     val extendedFabShape: Shape @Composable get() =
-        ExtendedFabPrimaryTokens.ContainerShape.toShape()
+        ExtendedFabPrimaryTokens.ContainerShape.value
 
     /** Default container color for a floating action button. */
-    val containerColor: Color @Composable get() = FabPrimaryTokens.ContainerColor.toColor()
+    val containerColor: Color @Composable get() = FabPrimaryTokens.ContainerColor.value
 
     /**
      * Creates a [FloatingActionButtonElevation] that represents the elevation of a
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/IconButton.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/IconButton.kt
index 51ae14d..57c2bbc 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/IconButton.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/IconButton.kt
@@ -83,7 +83,7 @@
         modifier = modifier
             .minimumInteractiveComponentSize()
             .size(IconButtonTokens.StateLayerSize)
-            .clip(IconButtonTokens.StateLayerShape.toShape())
+            .clip(IconButtonTokens.StateLayerShape.value)
             .background(color = colors.containerColor(enabled).value)
             .clickable(
                 onClick = onClick,
@@ -144,7 +144,7 @@
         modifier = modifier
             .minimumInteractiveComponentSize()
             .size(IconButtonTokens.StateLayerSize)
-            .clip(IconButtonTokens.StateLayerShape.toShape())
+            .clip(IconButtonTokens.StateLayerShape.value)
             .background(color = colors.containerColor(enabled, checked).value)
             .toggleable(
                 value = checked,
@@ -535,12 +535,12 @@
  */
 object IconButtonDefaults {
     /** Default shape for a filled icon button. */
-    val filledShape: Shape @Composable get() = FilledIconButtonTokens.ContainerShape.toShape()
+    val filledShape: Shape @Composable get() = FilledIconButtonTokens.ContainerShape.value
 
     /** Default shape for an outlined icon button. */
     val outlinedShape: Shape
         @Composable get() =
-            OutlinedIconButtonTokens.ContainerShape.toShape()
+            OutlinedIconButtonTokens.ContainerShape.value
 
     /**
      * Creates a [IconButtonColors] that represents the default colors used in a [IconButton].
@@ -584,7 +584,7 @@
         disabledContentColor: Color =
             contentColor.copy(alpha = IconButtonTokens.DisabledIconOpacity),
         checkedContainerColor: Color = Color.Transparent,
-        checkedContentColor: Color = IconButtonTokens.SelectedIconColor.toColor()
+        checkedContentColor: Color = IconButtonTokens.SelectedIconColor.value
     ): IconToggleButtonColors =
         IconToggleButtonColors(
             containerColor = containerColor,
@@ -605,11 +605,11 @@
      */
     @Composable
     fun filledIconButtonColors(
-        containerColor: Color = FilledIconButtonTokens.ContainerColor.toColor(),
+        containerColor: Color = FilledIconButtonTokens.ContainerColor.value,
         contentColor: Color = contentColorFor(containerColor),
-        disabledContainerColor: Color = FilledIconButtonTokens.DisabledContainerColor.toColor()
+        disabledContainerColor: Color = FilledIconButtonTokens.DisabledContainerColor.value
             .copy(alpha = FilledIconButtonTokens.DisabledContainerOpacity),
-        disabledContentColor: Color = FilledIconButtonTokens.DisabledColor.toColor()
+        disabledContentColor: Color = FilledIconButtonTokens.DisabledColor.value
             .copy(alpha = FilledIconButtonTokens.DisabledOpacity)
     ): IconButtonColors =
         IconButtonColors(
@@ -632,15 +632,15 @@
      */
     @Composable
     fun filledIconToggleButtonColors(
-        containerColor: Color = FilledIconButtonTokens.UnselectedContainerColor.toColor(),
+        containerColor: Color = FilledIconButtonTokens.UnselectedContainerColor.value,
         // TODO(b/228455081): Using contentColorFor here will return OnSurfaceVariant,
         //  while the token value is Primary.
-        contentColor: Color = FilledIconButtonTokens.ToggleUnselectedColor.toColor(),
-        disabledContainerColor: Color = FilledIconButtonTokens.DisabledContainerColor.toColor()
+        contentColor: Color = FilledIconButtonTokens.ToggleUnselectedColor.value,
+        disabledContainerColor: Color = FilledIconButtonTokens.DisabledContainerColor.value
             .copy(alpha = FilledIconButtonTokens.DisabledContainerOpacity),
-        disabledContentColor: Color = FilledIconButtonTokens.DisabledColor.toColor()
+        disabledContentColor: Color = FilledIconButtonTokens.DisabledColor.value
             .copy(alpha = FilledIconButtonTokens.DisabledOpacity),
-        checkedContainerColor: Color = FilledIconButtonTokens.SelectedContainerColor.toColor(),
+        checkedContainerColor: Color = FilledIconButtonTokens.SelectedContainerColor.value,
         checkedContentColor: Color = contentColorFor(checkedContainerColor)
     ): IconToggleButtonColors =
         IconToggleButtonColors(
@@ -663,11 +663,11 @@
      */
     @Composable
     fun filledTonalIconButtonColors(
-        containerColor: Color = FilledTonalIconButtonTokens.ContainerColor.toColor(),
+        containerColor: Color = FilledTonalIconButtonTokens.ContainerColor.value,
         contentColor: Color = contentColorFor(containerColor),
-        disabledContainerColor: Color = FilledTonalIconButtonTokens.DisabledContainerColor.toColor()
+        disabledContainerColor: Color = FilledTonalIconButtonTokens.DisabledContainerColor.value
             .copy(alpha = FilledTonalIconButtonTokens.DisabledContainerOpacity),
-        disabledContentColor: Color = FilledTonalIconButtonTokens.DisabledColor.toColor()
+        disabledContentColor: Color = FilledTonalIconButtonTokens.DisabledColor.value
             .copy(alpha = FilledTonalIconButtonTokens.DisabledOpacity)
     ): IconButtonColors =
         IconButtonColors(
@@ -690,15 +690,15 @@
      */
     @Composable
     fun filledTonalIconToggleButtonColors(
-        containerColor: Color = FilledTonalIconButtonTokens.UnselectedContainerColor.toColor(),
+        containerColor: Color = FilledTonalIconButtonTokens.UnselectedContainerColor.value,
         contentColor: Color = contentColorFor(containerColor),
-        disabledContainerColor: Color = FilledTonalIconButtonTokens.DisabledContainerColor.toColor()
+        disabledContainerColor: Color = FilledTonalIconButtonTokens.DisabledContainerColor.value
             .copy(alpha = FilledTonalIconButtonTokens.DisabledContainerOpacity),
-        disabledContentColor: Color = FilledTonalIconButtonTokens.DisabledColor.toColor()
+        disabledContentColor: Color = FilledTonalIconButtonTokens.DisabledColor.value
             .copy(alpha = FilledTonalIconButtonTokens.DisabledOpacity),
         checkedContainerColor: Color =
-            FilledTonalIconButtonTokens.SelectedContainerColor.toColor(),
-        checkedContentColor: Color = FilledTonalIconButtonTokens.ToggleSelectedColor.toColor()
+            FilledTonalIconButtonTokens.SelectedContainerColor.value,
+        checkedContentColor: Color = FilledTonalIconButtonTokens.ToggleSelectedColor.value
     ): IconToggleButtonColors =
         IconToggleButtonColors(
             containerColor = containerColor,
@@ -752,7 +752,7 @@
         disabledContentColor: Color =
             contentColor.copy(alpha = OutlinedIconButtonTokens.DisabledOpacity),
         checkedContainerColor: Color =
-            OutlinedIconButtonTokens.SelectedContainerColor.toColor(),
+            OutlinedIconButtonTokens.SelectedContainerColor.value,
         checkedContentColor: Color = contentColorFor(checkedContainerColor)
     ): IconToggleButtonColors =
         IconToggleButtonColors(
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/IncludeFontPaddingHelper.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/IncludeFontPaddingHelper.kt
deleted file mode 100644
index e6ace4e..0000000
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/IncludeFontPaddingHelper.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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.ui.text.TextStyle
-
-// TODO(b/237588251) remove this once the default includeFontPadding is false
-internal expect fun copyAndSetFontPadding(style: TextStyle, includeFontPadding: Boolean): TextStyle
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ListItem.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ListItem.kt
index f083945..20588bd 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ListItem.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ListItem.kt
@@ -386,17 +386,17 @@
     /** The default shape of a list item */
     val shape: Shape
         @Composable
-        @ReadOnlyComposable get() = ListTokens.ListItemContainerShape.toShape()
+        @ReadOnlyComposable get() = ListTokens.ListItemContainerShape.value
 
     /** The container color of a list item */
     val containerColor: Color
         @Composable
-        @ReadOnlyComposable get() = ListTokens.ListItemContainerColor.toColor()
+        @ReadOnlyComposable get() = ListTokens.ListItemContainerColor.value
 
     /** The content color of a list item */
     val contentColor: Color
         @Composable
-        @ReadOnlyComposable get() = ListTokens.ListItemLabelTextColor.toColor()
+        @ReadOnlyComposable get() = ListTokens.ListItemLabelTextColor.value
 
     /**
      * Creates a [ListItemColors] that represents the default container and content colors used in a
@@ -417,17 +417,17 @@
      */
     @Composable
     fun colors(
-        containerColor: Color = ListTokens.ListItemContainerColor.toColor(),
-        headlineColor: Color = ListTokens.ListItemLabelTextColor.toColor(),
-        leadingIconColor: Color = ListTokens.ListItemLeadingIconColor.toColor(),
-        overlineColor: Color = ListTokens.ListItemOverlineColor.toColor(),
-        supportingColor: Color = ListTokens.ListItemSupportingTextColor.toColor(),
-        trailingIconColor: Color = ListTokens.ListItemTrailingIconColor.toColor(),
-        disabledHeadlineColor: Color = ListTokens.ListItemDisabledLabelTextColor.toColor()
+        containerColor: Color = ListTokens.ListItemContainerColor.value,
+        headlineColor: Color = ListTokens.ListItemLabelTextColor.value,
+        leadingIconColor: Color = ListTokens.ListItemLeadingIconColor.value,
+        overlineColor: Color = ListTokens.ListItemOverlineColor.value,
+        supportingColor: Color = ListTokens.ListItemSupportingTextColor.value,
+        trailingIconColor: Color = ListTokens.ListItemTrailingIconColor.value,
+        disabledHeadlineColor: Color = ListTokens.ListItemDisabledLabelTextColor.value
             .copy(alpha = ListTokens.ListItemDisabledLabelTextOpacity),
-        disabledLeadingIconColor: Color = ListTokens.ListItemDisabledLeadingIconColor.toColor()
+        disabledLeadingIconColor: Color = ListTokens.ListItemDisabledLeadingIconColor.value
             .copy(alpha = ListTokens.ListItemDisabledLeadingIconOpacity),
-        disabledTrailingIconColor: Color = ListTokens.ListItemDisabledTrailingIconColor.toColor()
+        disabledTrailingIconColor: Color = ListTokens.ListItemDisabledTrailingIconColor.value
             .copy(alpha = ListTokens.ListItemDisabledTrailingIconOpacity)
     ): ListItemColors =
         ListItemColors(
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt
index 3788a3b..11b15675 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt
@@ -124,7 +124,7 @@
             this.alpha = alpha
             transformOrigin = transformOriginState.value
         },
-        shape = MenuTokens.ContainerShape.toShape(),
+        shape = MenuTokens.ContainerShape.value,
         color = MaterialTheme.colorScheme.fromToken(MenuTokens.ContainerColor),
         tonalElevation = MenuTokens.ContainerElevation,
         shadowElevation = MenuTokens.ContainerElevation
@@ -233,15 +233,15 @@
      */
     @Composable
     fun itemColors(
-        textColor: Color = MenuTokens.ListItemLabelTextColor.toColor(),
-        leadingIconColor: Color = MenuTokens.ListItemLeadingIconColor.toColor(),
-        trailingIconColor: Color = MenuTokens.ListItemTrailingIconColor.toColor(),
+        textColor: Color = MenuTokens.ListItemLabelTextColor.value,
+        leadingIconColor: Color = MenuTokens.ListItemLeadingIconColor.value,
+        trailingIconColor: Color = MenuTokens.ListItemTrailingIconColor.value,
         disabledTextColor: Color =
-            MenuTokens.ListItemDisabledLabelTextColor.toColor()
+            MenuTokens.ListItemDisabledLabelTextColor.value
                 .copy(alpha = MenuTokens.ListItemDisabledLabelTextOpacity),
-        disabledLeadingIconColor: Color = MenuTokens.ListItemDisabledLeadingIconColor.toColor()
+        disabledLeadingIconColor: Color = MenuTokens.ListItemDisabledLeadingIconColor.value
             .copy(alpha = MenuTokens.ListItemDisabledLeadingIconOpacity),
-        disabledTrailingIconColor: Color = MenuTokens.ListItemDisabledTrailingIconColor.toColor()
+        disabledTrailingIconColor: Color = MenuTokens.ListItemDisabledTrailingIconColor.value
             .copy(alpha = MenuTokens.ListItemDisabledTrailingIconOpacity),
     ): MenuItemColors = MenuItemColors(
         textColor = textColor,
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationBar.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationBar.kt
index 6e55dea..a6c347d 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationBar.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationBar.kt
@@ -235,7 +235,7 @@
             Box(
                 Modifier
                     .layoutId(IndicatorRippleLayoutIdTag)
-                    .clip(NavigationBarTokens.ActiveIndicatorShape.toShape())
+                    .clip(NavigationBarTokens.ActiveIndicatorShape.value)
                     .indication(offsetInteractionSource, rememberRipple())
             )
         }
@@ -245,7 +245,7 @@
                     .layoutId(IndicatorLayoutIdTag)
                     .background(
                         color = colors.indicatorColor.copy(alpha = animationProgress),
-                        shape = NavigationBarTokens.ActiveIndicatorShape.toShape(),
+                        shape = NavigationBarTokens.ActiveIndicatorShape.value,
                     )
             )
         }
@@ -267,7 +267,7 @@
     val Elevation: Dp = NavigationBarTokens.ContainerElevation
 
     /** Default color for a navigation bar. */
-    val containerColor: Color @Composable get() = NavigationBarTokens.ContainerColor.toColor()
+    val containerColor: Color @Composable get() = NavigationBarTokens.ContainerColor.value
 
     /**
      * Default window insets to be used and consumed by navigation bar
@@ -296,11 +296,11 @@
      */
     @Composable
     fun colors(
-        selectedIconColor: Color = NavigationBarTokens.ActiveIconColor.toColor(),
-        selectedTextColor: Color = NavigationBarTokens.ActiveLabelTextColor.toColor(),
-        indicatorColor: Color = NavigationBarTokens.ActiveIndicatorColor.toColor(),
-        unselectedIconColor: Color = NavigationBarTokens.InactiveIconColor.toColor(),
-        unselectedTextColor: Color = NavigationBarTokens.InactiveLabelTextColor.toColor(),
+        selectedIconColor: Color = NavigationBarTokens.ActiveIconColor.value,
+        selectedTextColor: Color = NavigationBarTokens.ActiveLabelTextColor.value,
+        indicatorColor: Color = NavigationBarTokens.ActiveIndicatorColor.value,
+        unselectedIconColor: Color = NavigationBarTokens.InactiveIconColor.value,
+        unselectedTextColor: Color = NavigationBarTokens.InactiveLabelTextColor.value,
         disabledIconColor: Color = unselectedIconColor.copy(alpha = DisabledAlpha),
         disabledTextColor: Color = unselectedTextColor.copy(alpha = DisabledAlpha),
     ): NavigationBarItemColors = NavigationBarItemColors(
@@ -319,11 +319,11 @@
     )
     @Composable
     fun colors(
-        selectedIconColor: Color = NavigationBarTokens.ActiveIconColor.toColor(),
-        selectedTextColor: Color = NavigationBarTokens.ActiveLabelTextColor.toColor(),
-        indicatorColor: Color = NavigationBarTokens.ActiveIndicatorColor.toColor(),
-        unselectedIconColor: Color = NavigationBarTokens.InactiveIconColor.toColor(),
-        unselectedTextColor: Color = NavigationBarTokens.InactiveLabelTextColor.toColor(),
+        selectedIconColor: Color = NavigationBarTokens.ActiveIconColor.value,
+        selectedTextColor: Color = NavigationBarTokens.ActiveLabelTextColor.value,
+        indicatorColor: Color = NavigationBarTokens.ActiveIndicatorColor.value,
+        unselectedIconColor: Color = NavigationBarTokens.InactiveIconColor.value,
+        unselectedTextColor: Color = NavigationBarTokens.InactiveLabelTextColor.value,
     ): NavigationBarItemColors = NavigationBarItemColors(
         selectedIconColor = selectedIconColor,
         selectedTextColor = selectedTextColor,
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationDrawer.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationDrawer.kt
index 401b7c78..3f585fd 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationDrawer.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationDrawer.kt
@@ -606,14 +606,14 @@
     val DismissibleDrawerElevation = NavigationDrawerTokens.StandardContainerElevation
 
     /** Default shape for a navigation drawer. */
-    val shape: Shape @Composable get() = NavigationDrawerTokens.ContainerShape.toShape()
+    val shape: Shape @Composable get() = NavigationDrawerTokens.ContainerShape.value
 
     /** Default color of the scrim that obscures content when the drawer is open */
     val scrimColor: Color
-        @Composable get() = ScrimTokens.ContainerColor.toColor().copy(ScrimTokens.ContainerOpacity)
+        @Composable get() = ScrimTokens.ContainerColor.value.copy(ScrimTokens.ContainerOpacity)
 
     /** Default container color for a navigation drawer */
-    val containerColor: Color @Composable get() = NavigationDrawerTokens.ContainerColor.toColor()
+    val containerColor: Color @Composable get() = NavigationDrawerTokens.ContainerColor.value
 
     /** Default and maximum width of a navigation drawer */
     val MaximumDrawerWidth = NavigationDrawerTokens.ContainerWidth
@@ -655,7 +655,7 @@
     modifier: Modifier = Modifier,
     icon: (@Composable () -> Unit)? = null,
     badge: (@Composable () -> Unit)? = null,
-    shape: Shape = NavigationDrawerTokens.ActiveIndicatorShape.toShape(),
+    shape: Shape = NavigationDrawerTokens.ActiveIndicatorShape.value,
     colors: NavigationDrawerItemColors = NavigationDrawerItemDefaults.colors(),
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
 ) {
@@ -747,12 +747,12 @@
      */
     @Composable
     fun colors(
-        selectedContainerColor: Color = NavigationDrawerTokens.ActiveIndicatorColor.toColor(),
-        unselectedContainerColor: Color = NavigationDrawerTokens.ContainerColor.toColor(),
-        selectedIconColor: Color = NavigationDrawerTokens.ActiveIconColor.toColor(),
-        unselectedIconColor: Color = NavigationDrawerTokens.InactiveIconColor.toColor(),
-        selectedTextColor: Color = NavigationDrawerTokens.ActiveLabelTextColor.toColor(),
-        unselectedTextColor: Color = NavigationDrawerTokens.InactiveLabelTextColor.toColor(),
+        selectedContainerColor: Color = NavigationDrawerTokens.ActiveIndicatorColor.value,
+        unselectedContainerColor: Color = NavigationDrawerTokens.ContainerColor.value,
+        selectedIconColor: Color = NavigationDrawerTokens.ActiveIconColor.value,
+        unselectedIconColor: Color = NavigationDrawerTokens.InactiveIconColor.value,
+        selectedTextColor: Color = NavigationDrawerTokens.ActiveLabelTextColor.value,
+        unselectedTextColor: Color = NavigationDrawerTokens.InactiveLabelTextColor.value,
         selectedBadgeColor: Color = selectedTextColor,
         unselectedBadgeColor: Color = unselectedTextColor,
     ): NavigationDrawerItemColors = DefaultDrawerItemsColor(
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationRail.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationRail.kt
index 63a0e46..d5caf4e 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationRail.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/NavigationRail.kt
@@ -224,9 +224,9 @@
         }
 
         val indicatorShape = if (label != null) {
-            NavigationRailTokens.ActiveIndicatorShape.toShape()
+            NavigationRailTokens.ActiveIndicatorShape.value
         } else {
-            NavigationRailTokens.NoLabelActiveIndicatorShape.toShape()
+            NavigationRailTokens.NoLabelActiveIndicatorShape.value
         }
 
         // The indicator has a width-expansion animation which interferes with the timing of the
@@ -264,7 +264,7 @@
 /** Defaults used in [NavigationRail] */
 object NavigationRailDefaults {
     /** Default container color of a navigation rail. */
-    val ContainerColor: Color @Composable get() = NavigationRailTokens.ContainerColor.toColor()
+    val ContainerColor: Color @Composable get() = NavigationRailTokens.ContainerColor.value
 
     /**
      * Default window insets for navigation rail.
@@ -292,11 +292,11 @@
      */
     @Composable
     fun colors(
-        selectedIconColor: Color = NavigationRailTokens.ActiveIconColor.toColor(),
-        selectedTextColor: Color = NavigationRailTokens.ActiveLabelTextColor.toColor(),
-        indicatorColor: Color = NavigationRailTokens.ActiveIndicatorColor.toColor(),
-        unselectedIconColor: Color = NavigationRailTokens.InactiveIconColor.toColor(),
-        unselectedTextColor: Color = NavigationRailTokens.InactiveLabelTextColor.toColor(),
+        selectedIconColor: Color = NavigationRailTokens.ActiveIconColor.value,
+        selectedTextColor: Color = NavigationRailTokens.ActiveLabelTextColor.value,
+        indicatorColor: Color = NavigationRailTokens.ActiveIndicatorColor.value,
+        unselectedIconColor: Color = NavigationRailTokens.InactiveIconColor.value,
+        unselectedTextColor: Color = NavigationRailTokens.InactiveLabelTextColor.value,
         disabledIconColor: Color = unselectedIconColor.copy(alpha = DisabledAlpha),
         disabledTextColor: Color = unselectedTextColor.copy(alpha = DisabledAlpha),
     ): NavigationRailItemColors = NavigationRailItemColors(
@@ -315,11 +315,11 @@
     )
     @Composable
     fun colors(
-        selectedIconColor: Color = NavigationRailTokens.ActiveIconColor.toColor(),
-        selectedTextColor: Color = NavigationRailTokens.ActiveLabelTextColor.toColor(),
-        indicatorColor: Color = NavigationRailTokens.ActiveIndicatorColor.toColor(),
-        unselectedIconColor: Color = NavigationRailTokens.InactiveIconColor.toColor(),
-        unselectedTextColor: Color = NavigationRailTokens.InactiveLabelTextColor.toColor(),
+        selectedIconColor: Color = NavigationRailTokens.ActiveIconColor.value,
+        selectedTextColor: Color = NavigationRailTokens.ActiveLabelTextColor.value,
+        indicatorColor: Color = NavigationRailTokens.ActiveIndicatorColor.value,
+        unselectedIconColor: Color = NavigationRailTokens.InactiveIconColor.value,
+        unselectedTextColor: Color = NavigationRailTokens.InactiveLabelTextColor.value,
     ): NavigationRailItemColors = NavigationRailItemColors(
         selectedIconColor = selectedIconColor,
         selectedTextColor = selectedTextColor,
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ProgressIndicator.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ProgressIndicator.kt
index 2ed1ace..f2f40e4 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ProgressIndicator.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ProgressIndicator.kt
@@ -504,15 +504,15 @@
 object ProgressIndicatorDefaults {
     /** Default color for a linear progress indicator. */
     val linearColor: Color @Composable get() =
-        LinearProgressIndicatorTokens.ActiveIndicatorColor.toColor()
+        LinearProgressIndicatorTokens.ActiveIndicatorColor.value
 
     /** Default color for a circular progress indicator. */
     val circularColor: Color @Composable get() =
-        CircularProgressIndicatorTokens.ActiveIndicatorColor.toColor()
+        CircularProgressIndicatorTokens.ActiveIndicatorColor.value
 
     /** Default track color for a linear progress indicator. */
     val linearTrackColor: Color @Composable get() =
-        LinearProgressIndicatorTokens.TrackColor.toColor()
+        LinearProgressIndicatorTokens.TrackColor.value
 
     /** Default track color for a circular progress indicator. */
     val circularTrackColor: Color @Composable get() = Color.Transparent
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/RadioButton.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/RadioButton.kt
index 101aed1..d55064c 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/RadioButton.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/RadioButton.kt
@@ -143,13 +143,13 @@
      */
     @Composable
     fun colors(
-        selectedColor: Color = RadioButtonTokens.SelectedIconColor.toColor(),
-        unselectedColor: Color = RadioButtonTokens.UnselectedIconColor.toColor(),
+        selectedColor: Color = RadioButtonTokens.SelectedIconColor.value,
+        unselectedColor: Color = RadioButtonTokens.UnselectedIconColor.value,
         disabledSelectedColor: Color = RadioButtonTokens.DisabledSelectedIconColor
-            .toColor()
+            .value
             .copy(alpha = RadioButtonTokens.DisabledSelectedIconOpacity),
         disabledUnselectedColor: Color = RadioButtonTokens.DisabledUnselectedIconColor
-            .toColor()
+            .value
             .copy(alpha = RadioButtonTokens.DisabledUnselectedIconOpacity)
     ): RadioButtonColors = RadioButtonColors(
         selectedColor,
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SegmentedButton.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SegmentedButton.kt
index 1cd1101..8c6bb95 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SegmentedButton.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SegmentedButton.kt
@@ -16,9 +16,19 @@
 
 package androidx.compose.material3
 
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.Crossfade
+import androidx.compose.animation.ExitTransition
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.AnimationVector1D
+import androidx.compose.animation.core.VectorConverter
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.scaleIn
 import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.interaction.FocusInteraction
 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
@@ -28,9 +38,15 @@
 import androidx.compose.foundation.layout.defaultMinSize
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.selection.selectable
 import androidx.compose.foundation.selection.selectableGroup
+import androidx.compose.foundation.selection.toggleable
 import androidx.compose.foundation.shape.CornerBasedShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Check
+import androidx.compose.material3.tokens.MotionTokens
 import androidx.compose.material3.tokens.OutlinedSegmentedButtonTokens
 import androidx.compose.material3.tokens.OutlinedSegmentedButtonTokens.DisabledLabelTextColor
 import androidx.compose.material3.tokens.OutlinedSegmentedButtonTokens.DisabledLabelTextOpacity
@@ -43,9 +59,13 @@
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.State
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
@@ -53,40 +73,52 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.RectangleShape
 import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.TransformOrigin
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.layout.layout
+import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.fastMaxBy
+import kotlinx.coroutines.launch
 
 /**
  * <a href="https://m3.material.io/components/segmented-buttons/overview" class="external" target="_blank">Material Segmented Button</a>.
  * Segmented buttons help people select options, switch views, or sort elements.
  *
- * A default Segmented Button. Also known as Outlined Segmented Button.
+ * A default Toggleable Segmented Button. Also known as Outlined Segmented Button.
+ * See [Modifier.toggleable].
  *
- * This should typically be used inside of a [SegmentedButtonRow], see the corresponding
- * documentation for example usage.
+ * Toggleable segmented buttons should be used for cases where the selection is not mutually
+ * exclusive.
  *
+ * This should typically be used inside of a [MultiChoiceSegmentedButtonRow]
+ *
+ * For a sample showing Segmented button with only checked icons see:
+ * @sample androidx.compose.material3.samples.SegmentedButtonMultiSelectSample
+ *
+ * @param checked whether this button is checked or not
  * @param onCheckedChange callback to be invoked when the button is clicked.
  * therefore the change of checked state in requested.
  * @param modifier the [Modifier] to be applied to this button
  * @param enabled controls the enabled state of this button. When `false`, this component will not
  * respond to user input, and it will appear visually disabled and disabled to accessibility
  * services.
- * @param checked whether this button is checked or not
  * @param shape the shape for this button
- * @param border the border for this button, see [SegmentedButtonColors]
  * @param colors [SegmentedButtonColors] that will be used to resolve the colors used for this
+ * @param border the border for this button, see [SegmentedButtonColors]
  * Button in different states
  * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
  * for this button. You can create and pass in your own `remember`ed instance to observe
  * [Interaction]s and customize the appearance / behavior of this button in different states.
+ * @param icon the icon slot for this button, you can pass null in unchecked, in which case
+ * the content will displace to show the checked icon, or pass different icon lambdas for
+ * unchecked and checked in which case the icons will crossfade.
  * @param content content to be rendered inside this button
- *
- * @sample androidx.compose.material3.samples.SegmentedButtonSingleSelectSample
  */
 @Composable
 @ExperimentalMaterial3Api
-fun SegmentedButton(
+fun MultiChoiceSegmentedButtonRowScope.SegmentedButton(
     checked: Boolean,
     onCheckedChange: (Boolean) -> Unit,
     modifier: Modifier = Modifier,
@@ -95,39 +127,19 @@
     colors: SegmentedButtonColors = SegmentedButtonDefaults.colors(),
     border: SegmentedButtonBorder = SegmentedButtonDefaults.Border,
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
-    content: @Composable RowScope.() -> Unit,
+    icon: @Composable () -> Unit = { SegmentedButtonDefaults.SegmentedButtonIcon(checked) },
+    content: @Composable () -> Unit,
 ) {
-    var interactionCount: Int by remember { mutableIntStateOf(0) }
-    LaunchedEffect(interactionSource) {
-        interactionSource.interactions.collect { interaction ->
-            when (interaction) {
-                is PressInteraction.Press,
-                is FocusInteraction.Focus -> {
-                    interactionCount++
-                }
-
-                is PressInteraction.Release,
-                is FocusInteraction.Unfocus,
-                is PressInteraction.Cancel -> {
-                    interactionCount--
-                }
-            }
-        }
-    }
 
     val containerColor = colors.containerColor(enabled, checked)
     val contentColor = colors.contentColor(enabled, checked)
     val checkedState by rememberUpdatedState(checked)
+    val interactionCount by interactionSource.interactionCountAsState()
 
     Surface(
         modifier = modifier
-            .layout { measurable, constraints ->
-                val placeable = measurable.measure(constraints)
-                layout(placeable.width, placeable.height) {
-                    val zIndex = interactionCount + if (checkedState) CheckedZIndexFactor else 0f
-                    placeable.place(0, 0, zIndex)
-                }
-            }
+            .weight(1f)
+            .interactionZIndex(checkedState, interactionCount)
             .defaultMinSize(
                 minWidth = ButtonDefaults.MinWidth,
                 minHeight = ButtonDefaults.MinHeight
@@ -141,128 +153,313 @@
         border = border.borderStroke(enabled, checked, colors),
         interactionSource = interactionSource
     ) {
-        ProvideTextStyle(value = MaterialTheme.typography.labelLarge) {
-            Row(
-                Modifier.padding(ButtonDefaults.TextButtonContentPadding),
-                horizontalArrangement = Arrangement.Center,
-                verticalAlignment = Alignment.CenterVertically,
-                content = content
-            )
-        }
+        SegmentedButtonContent(icon, content)
     }
 }
 
 /**
+ * <a href="https://m3.material.io/components/segmented-buttons/overview" class="external" target="_blank">Material Segmented Button</a>.
+ * Segmented buttons help people select options, switch views, or sort elements.
  *
+ * A default Toggleable Segmented Button. Also known as Outlined Segmented Button.
+ * See [Modifier.selectable].
+ *
+ * Selectable segmented buttons should be used for cases where the selection is mutually
+ * exclusive, when only one button can be selected at a time.
+ *
+ * This should typically be used inside of a [SingleChoiceSegmentedButtonRow]
+ *
+ * For a sample showing Segmented button with only checked icons see:
+ * @sample androidx.compose.material3.samples.SegmentedButtonSingleSelectSample
+ *
+ * @param selected whether this button is selected or not
+ * @param onClick callback to be invoked when the button is clicked.
+ * therefore the change of checked state in requested.
+ * @param modifier the [Modifier] to be applied to this button
+ * @param enabled controls the enabled state of this button. When `false`, this component will not
+ * respond to user input, and it will appear visually disabled and disabled to accessibility
+ * services.
+ * @param shape the shape for this button
+ * @param colors [SegmentedButtonColors] that will be used to resolve the colors used for this
+ * @param border the border for this button, see [SegmentedButtonColors]
+ * Button in different states
+ * @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
+ * for this button. You can create and pass in your own `remember`ed instance to observe
+ * [Interaction]s and customize the appearance / behavior of this button in different states.
+ * @param icon the icon slot for this button, you can pass null in unchecked, in which case
+ * the content will displace to show the checked icon, or pass different icon lambdas for
+ * unchecked and checked in which case the icons will crossfade.
+ * @param content content to be rendered inside this button
+ */
+@Composable
+@ExperimentalMaterial3Api
+fun SingleChoiceSegmentedButtonRowScope.SegmentedButton(
+    selected: Boolean,
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    shape: Shape = RectangleShape,
+    colors: SegmentedButtonColors = SegmentedButtonDefaults.colors(),
+    border: SegmentedButtonBorder = SegmentedButtonDefaults.Border,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    icon: @Composable () -> Unit = { SegmentedButtonDefaults.SegmentedButtonIcon(selected) },
+    content: @Composable () -> Unit,
+) {
+    val containerColor = colors.containerColor(enabled, selected)
+    val contentColor = colors.contentColor(enabled, selected)
+    val checkedState by rememberUpdatedState(selected)
+    val interactionCount by interactionSource.interactionCountAsState()
+
+    Surface(
+        modifier = modifier
+            .weight(1f)
+            .interactionZIndex(checkedState, interactionCount)
+            .defaultMinSize(
+                minWidth = ButtonDefaults.MinWidth,
+                minHeight = ButtonDefaults.MinHeight
+            ),
+        selected = selected,
+        onClick = onClick,
+        enabled = enabled,
+        shape = shape,
+        color = containerColor,
+        contentColor = contentColor,
+        border = border.borderStroke(enabled, selected, colors),
+        interactionSource = interactionSource
+    ) {
+        SegmentedButtonContent(icon, content)
+    }
+}
+
+/**
  * <a href="https://m3.material.io/components/segmented-buttons/overview" class="external" target="_blank">Material Segmented Button</a>.
  *
  * A Layout to correctly position and size [SegmentedButton]s in a Row.
  * It handles overlapping items so that strokes of the item are correctly on top of each other.
+ * [SingleChoiceSegmentedButtonRow] is used when the selection only allows one value, for correct
+ * semantics.
+ *
+ * @sample androidx.compose.material3.samples.SegmentedButtonSingleSelectSample
  *
  * @param modifier the [Modifier] to be applied to this row
  * @param space the dimension of the overlap between buttons. Should be equal to the stroke width
  *  used on the items.
  * @param content the content of this Segmented Button Row, typically a sequence of
- * [SegmentedButtonRow]
- *
- * @sample androidx.compose.material3.samples.SegmentedButtonSingleSelectSample
+ * [SegmentedButton]s
  */
 @Composable
 @ExperimentalMaterial3Api
-fun SegmentedButtonRow(
+fun SingleChoiceSegmentedButtonRow(
     modifier: Modifier = Modifier,
     space: Dp = SegmentedButtonDefaults.Border.width,
-    content: @Composable () -> Unit
+    content: @Composable SingleChoiceSegmentedButtonRowScope.() -> Unit
 ) {
-    Layout(
+    Row(
+        modifier = modifier
+            .selectableGroup()
+            .height(OutlinedSegmentedButtonTokens.ContainerHeight)
+            .width(IntrinsicSize.Min),
+        horizontalArrangement = Arrangement.spacedBy(-space),
+        verticalAlignment = Alignment.CenterVertically
+    ) {
+        val scope = remember { SingleChoiceSegmentedButtonScopeWrapper(this) }
+        scope.content()
+    }
+}
+
+/**
+ * <a href="https://m3.material.io/components/segmented-buttons/overview" class="external" target="_blank">Material Segmented Button</a>.
+ *
+ * A Layout to correctly position, size, and add semantics to [SegmentedButton]s in a Row.
+ * It handles overlapping items so that strokes of the item are correctly on top of each other.
+ *
+ * [MultiChoiceSegmentedButtonRow] is used when the selection allows multiple value, for correct
+ * semantics.
+ *
+ * @sample androidx.compose.material3.samples.SegmentedButtonMultiSelectSample
+ *
+ * @param modifier the [Modifier] to be applied to this row
+ * @param space the dimension of the overlap between buttons. Should be equal to the stroke width
+ *  used on the items.
+ * @param content the content of this Segmented Button Row, typically a sequence of
+ * [SegmentedButton]s
+ *
+ */
+@Composable
+@ExperimentalMaterial3Api
+fun MultiChoiceSegmentedButtonRow(
+    modifier: Modifier = Modifier,
+    space: Dp = SegmentedButtonDefaults.Border.width,
+    content: @Composable MultiChoiceSegmentedButtonRowScope.() -> Unit
+) {
+    Row(
         modifier = modifier
             .height(OutlinedSegmentedButtonTokens.ContainerHeight)
-            .width(IntrinsicSize.Min)
-            .selectableGroup(),
-        content = content
-    ) { measurables, constraints ->
+            .width(IntrinsicSize.Min),
+        horizontalArrangement = Arrangement.spacedBy(-space),
+        verticalAlignment = Alignment.CenterVertically
+    ) {
+        val scope = remember { MultiChoiceSegmentedButtonScopeWrapper(this) }
+        scope.content()
+    }
+}
 
-        val width = measurables.fold(0) { curr, max ->
-            maxOf(curr, max.maxIntrinsicWidth(constraints.maxHeight))
-        }
+@ExperimentalMaterial3Api
+@Composable
+private fun SegmentedButtonContent(
+    icon: @Composable () -> Unit,
+    content: @Composable () -> Unit,
+) {
+    Row(
+        modifier = Modifier.padding(ButtonDefaults.TextButtonContentPadding),
+        horizontalArrangement = Arrangement.Center,
+        verticalAlignment = Alignment.CenterVertically
+    ) {
+        ProvideTextStyle(value = MaterialTheme.typography.labelLarge) {
+            var animatable by remember {
+                mutableStateOf<Animatable<Int, AnimationVector1D>?>(null)
+            }
 
-        val placeables = measurables.map {
-            it.measure(constraints.copy(minWidth = width))
-        }
+            val scope = rememberCoroutineScope()
 
-        layout(placeables.size * width, constraints.maxHeight) {
-            val itemCount = placeables.size
-            val startOffset = (itemCount - 1) * space.roundToPx() / 2
-            placeables.forEachIndexed { index, placeable ->
-                placeable.placeRelative(startOffset + index * (width - space.roundToPx()), 0)
+            Layout(listOf(icon, content)) { (iconMeasurables, contentMeasurables), constraints ->
+                val iconPlaceables = iconMeasurables.map { it.measure(constraints) }
+                val iconDesiredWidth = iconMeasurables.fold(0) { acc, it ->
+                    maxOf(acc, it.maxIntrinsicWidth(Constraints.Infinity))
+                }
+                val iconWidth = iconPlaceables.fastMaxBy { it.width }?.width ?: 0
+                val contentPlaceables = contentMeasurables.map { it.measure(constraints) }
+                val contentWidth = contentPlaceables.fastMaxBy { it.width }?.width
+                val width = maxOf(SegmentedButtonDefaults.IconSize.roundToPx(), iconDesiredWidth) +
+                    IconSpacing.roundToPx() +
+                    (contentWidth ?: 0)
+
+                val offsetX = if (iconWidth == 0) {
+                    -(SegmentedButtonDefaults.IconSize.roundToPx() + IconSpacing.roundToPx()) / 2
+                } else {
+                    iconDesiredWidth - SegmentedButtonDefaults.IconSize.roundToPx()
+                }
+
+                val anim = animatable ?: Animatable(offsetX, Int.VectorConverter)
+                    .also { animatable = it }
+
+                if (anim.targetValue != offsetX) {
+                    scope.launch {
+                        anim.animateTo(offsetX, tween(MotionTokens.DurationMedium3.toInt()))
+                    }
+                }
+
+                layout(width, constraints.maxHeight) {
+                    iconPlaceables.forEach {
+                        it.place(0, (constraints.maxHeight - it.height) / 2)
+                    }
+
+                    val contentOffsetX = SegmentedButtonDefaults.IconSize.roundToPx() +
+                        IconSpacing.roundToPx() + anim.value
+
+                    contentPlaceables.forEach {
+                        it.place(contentOffsetX, (constraints.maxHeight - it.height) / 2)
+                    }
+                }
             }
         }
     }
 }
 
+@Composable
+private fun InteractionSource.interactionCountAsState(): State<Int> {
+    val interactionCount = remember { mutableIntStateOf(0) }
+    LaunchedEffect(this) {
+        [email protected] { interaction ->
+            when (interaction) {
+                is PressInteraction.Press,
+                is FocusInteraction.Focus -> {
+                    interactionCount.intValue++
+                }
+
+                is PressInteraction.Release,
+                is FocusInteraction.Unfocus,
+                is PressInteraction.Cancel -> {
+                    interactionCount.intValue--
+                }
+            }
+        }
+    }
+
+    return interactionCount
+}
+
+/** Scope for the children of a [SingleChoiceSegmentedButtonRow] */
+@ExperimentalMaterial3Api
+interface SingleChoiceSegmentedButtonRowScope : RowScope
+
+/** Scope for the children of a [MultiChoiceSegmentedButtonRow] */
+@ExperimentalMaterial3Api
+interface MultiChoiceSegmentedButtonRowScope : RowScope
+
 /* Contains defaults to be used with [SegmentedButtonRow] and [SegmentedButton] */
 @ExperimentalMaterial3Api
+@Stable
 object SegmentedButtonDefaults {
     /**
      * Creates a [SegmentedButtonColors] that represents the different colors
-     * used in a [SegmentedButtonRow] in different states.
+     * used in a [SegmentedButton] in different states.
      *
-     * @param checkedContainerColor the color used for the container when enabled and checked
-     * @param checkedContentColor the color used for the content when enabled and checked
-     * @param checkedBorderColor the color used for the border when enabled and checked
-     * @param uncheckedContainerColor the color used for the container when enabled and unchecked
-     * @param uncheckedContentColor the color used for the content when enabled and unchecked
-     * @param uncheckedBorderColor the color used for the border when enabled and checked
-     * @param disabledCheckedContainerColor the color used for the container
-     * when disabled and checked
-     * @param disabledCheckedContentColor the color used for the content when disabled and checked
-     * @param disabledCheckedBorderColor the color used for the border when disabled and checked
-     * @param disabledUncheckedContainerColor the color used for the container
-     * when disabled and unchecked
-     * @param disabledUncheckedContentColor the color used for the content when disabled and
+     * @param activeContainerColor the color used for the container when enabled and active
+     * @param activeContentColor the color used for the content when enabled and active
+     * @param activeBorderColor the color used for the border when enabled and active
+     * @param inactiveContainerColor the color used for the container when enabled and inactive
+     * @param inactiveContentColor the color used for the content when enabled and inactive
+     * @param inactiveBorderColor the color used for the border when enabled and active
+     * @param disabledActiveContainerColor the color used for the container
+     * when disabled and active
+     * @param disabledActiveContentColor the color used for the content when disabled and active
+     * @param disabledActiveBorderColor the color used for the border when disabled and active
+     * @param disabledInactiveContainerColor the color used for the container
+     * when disabled and inactive
+     * @param disabledInactiveContentColor the color used for the content when disabled and
      * unchecked
-     * @param disabledUncheckedBorderColor the color used for the border when disabled and unchecked
+     * @param disabledInactiveBorderColor the color used for the border when disabled and inactive
      */
     @Composable
     fun colors(
-        checkedContainerColor: Color = SelectedContainerColor.toColor(),
-        checkedContentColor: Color = SelectedLabelTextColor.toColor(),
-        checkedBorderColor: Color = OutlineColor.toColor(),
-        uncheckedContainerColor: Color = MaterialTheme.colorScheme.surface,
-        uncheckedContentColor: Color = UnselectedLabelTextColor.toColor(),
-        uncheckedBorderColor: Color = checkedBorderColor,
-        disabledCheckedContainerColor: Color = checkedContainerColor,
-        disabledCheckedContentColor: Color = DisabledLabelTextColor.toColor()
+        activeContainerColor: Color = SelectedContainerColor.value,
+        activeContentColor: Color = SelectedLabelTextColor.value,
+        activeBorderColor: Color = OutlineColor.value,
+        inactiveContainerColor: Color = MaterialTheme.colorScheme.surface,
+        inactiveContentColor: Color = UnselectedLabelTextColor.value,
+        inactiveBorderColor: Color = activeBorderColor,
+        disabledActiveContainerColor: Color = activeContainerColor,
+        disabledActiveContentColor: Color = DisabledLabelTextColor.value
             .copy(alpha = DisabledLabelTextOpacity),
-        disabledCheckedBorderColor: Color = OutlineColor.toColor()
+        disabledActiveBorderColor: Color = OutlineColor.value
             .copy(alpha = DisabledOutlineOpacity),
-        disabledUncheckedContainerColor: Color = uncheckedContainerColor,
-        disabledUncheckedContentColor: Color = disabledCheckedContentColor,
-        disabledUncheckedBorderColor: Color = checkedBorderColor,
+        disabledInactiveContainerColor: Color = inactiveContainerColor,
+        disabledInactiveContentColor: Color = disabledActiveContentColor,
+        disabledInactiveBorderColor: Color = activeBorderColor,
     ): SegmentedButtonColors = SegmentedButtonColors(
-        checkedContainerColor = checkedContainerColor,
-        checkedContentColor = checkedContentColor,
-        checkedBorderColor = checkedBorderColor,
-        uncheckedContainerColor = uncheckedContainerColor,
-        uncheckedContentColor = uncheckedContentColor,
-        uncheckedBorderColor = uncheckedBorderColor,
-        disabledCheckedContainerColor = disabledCheckedContainerColor,
-        disabledCheckedContentColor = disabledCheckedContentColor,
-        disabledCheckedBorderColor = disabledCheckedBorderColor,
-        disabledUncheckedContainerColor = disabledUncheckedContainerColor,
-        disabledUncheckedContentColor = disabledUncheckedContentColor,
-        disabledUncheckedBorderColor = disabledUncheckedBorderColor,
-
-        )
+        activeContainerColor = activeContainerColor,
+        activeContentColor = activeContentColor,
+        activeBorderColor = activeBorderColor,
+        inactiveContainerColor = inactiveContainerColor,
+        inactiveContentColor = inactiveContentColor,
+        inactiveBorderColor = inactiveBorderColor,
+        disabledActiveContainerColor = disabledActiveContainerColor,
+        disabledActiveContentColor = disabledActiveContentColor,
+        disabledActiveBorderColor = disabledActiveBorderColor,
+        disabledInactiveContainerColor = disabledInactiveContainerColor,
+        disabledInactiveContentColor = disabledInactiveContentColor,
+        disabledInactiveBorderColor = disabledInactiveBorderColor
+    )
 
     /** The default [BorderStroke] factory used by [SegmentedButton]. */
     val Border = SegmentedButtonBorder(width = OutlinedSegmentedButtonTokens.OutlineWidth)
 
-    /** The default [Shape] for [SegmentedButton] insdie [SegmentedButtonRow]. */
+    /** The default [Shape] for [SegmentedButton]. */
     val Shape: CornerBasedShape
         @Composable
         @ReadOnlyComposable
-        get() = OutlinedSegmentedButtonTokens.Shape.toShape() as CornerBasedShape
+        get() = OutlinedSegmentedButtonTokens.Shape.value as CornerBasedShape
 
     /**
      * A shape constructor that the button in [position] should have when there are [count] buttons
@@ -283,9 +480,52 @@
     }
 
     /**
-     * Icon size to use for icons inside [SegmentedButtonRow] item
+     * Icon size to use for icons used in [SegmentedButton]
      */
     val IconSize = OutlinedSegmentedButtonTokens.IconSize
+
+    /** And icon to indicate the segmented button is checked or selected */
+    @Composable
+    fun ActiveIcon() {
+        Icon(
+            imageVector = Icons.Filled.Check,
+            contentDescription = null,
+            modifier = Modifier.size(IconSize)
+        )
+    }
+
+    /**
+     * The default implementation of icons for Segmented Buttons.
+     *
+     * @param active whether the button is activated or not.
+     * @param activeContent usually a checkmark icon of [IconSize] dimensions.
+     * @param inactiveContent typically an icon of [IconSize]. It shows only when the button is not
+     * checked.
+     */
+    @Composable
+    fun SegmentedButtonIcon(
+        active: Boolean,
+        activeContent: @Composable () -> Unit = { ActiveIcon() },
+        inactiveContent: (@Composable () -> Unit)? = null
+    ) {
+        if (inactiveContent == null) {
+            AnimatedVisibility(
+                visible = active,
+                exit = ExitTransition.None,
+                enter = fadeIn(tween(MotionTokens.DurationMedium3.toInt())) + scaleIn(
+                    initialScale = 0f,
+                    transformOrigin = TransformOrigin(0f, 1f),
+                    animationSpec = tween(MotionTokens.DurationMedium3.toInt()),
+                ),
+            ) {
+                activeContent()
+            }
+        } else {
+            Crossfade(targetState = active) {
+                if (it) activeContent() else inactiveContent()
+            }
+        }
+    }
 }
 
 /**
@@ -313,124 +553,142 @@
  * @constructor create an instance with arbitrary colors, see [SegmentedButtonDefaults] for a
  * factory method using the default material3 spec
  *
- * @param checkedContainerColor the color used for the container when enabled and checked
- * @param checkedContentColor the color used for the content when enabled and checked
- * @param checkedBorderColor the color used for the border when enabled and checked
- * @param uncheckedContainerColor the color used for the container when enabled and unchecked
- * @param uncheckedContentColor the color used for the content when enabled and unchecked
- * @param uncheckedBorderColor the color used for the border when enabled and checked
- * @param disabledCheckedContainerColor the color used for the container when disabled and checked
- * @param disabledCheckedContentColor the color used for the content when disabled and checked
- * @param disabledCheckedBorderColor the color used for the border when disabled and checked
- * @param disabledUncheckedContainerColor the color used for the container
- * when disabled and unchecked
- * @param disabledUncheckedContentColor the color used for the content when disabled and unchecked
- * @param disabledUncheckedBorderColor the color used for the border when disabled and unchecked
+ * @param activeContainerColor the color used for the container when enabled and active
+ * @param activeContentColor the color used for the content when enabled and active
+ * @param activeBorderColor the color used for the border when enabled and active
+ * @param inactiveContainerColor the color used for the container when enabled and inactive
+ * @param inactiveContentColor the color used for the content when enabled and inactive
+ * @param inactiveBorderColor the color used for the border when enabled and active
+ * @param disabledActiveContainerColor the color used for the container when disabled and active
+ * @param disabledActiveContentColor the color used for the content when disabled and active
+ * @param disabledActiveBorderColor the color used for the border when disabled and active
+ * @param disabledInactiveContainerColor the color used for the container
+ * when disabled and inactive
+ * @param disabledInactiveContentColor the color used for the content when disabled and inactive
+ * @param disabledInactiveBorderColor the color used for the border when disabled and inactive
  */
 @Immutable
 @ExperimentalMaterial3Api
 class SegmentedButtonColors constructor(
-    // enabled & checked
-    val checkedContainerColor: Color,
-    val checkedContentColor: Color,
-    val checkedBorderColor: Color,
-    // enabled & unchecked
-    val uncheckedContainerColor: Color,
-    val uncheckedContentColor: Color,
-    val uncheckedBorderColor: Color,
-    // disable & checked
-    val disabledCheckedContainerColor: Color,
-    val disabledCheckedContentColor: Color,
-    val disabledCheckedBorderColor: Color,
-    // disable & unchecked
-    val disabledUncheckedContainerColor: Color,
-    val disabledUncheckedContentColor: Color,
-    val disabledUncheckedBorderColor: Color,
-
-    ) {
+    // enabled & active
+    val activeContainerColor: Color,
+    val activeContentColor: Color,
+    val activeBorderColor: Color,
+    // enabled & inactive
+    val inactiveContainerColor: Color,
+    val inactiveContentColor: Color,
+    val inactiveBorderColor: Color,
+    // disable & active
+    val disabledActiveContainerColor: Color,
+    val disabledActiveContentColor: Color,
+    val disabledActiveBorderColor: Color,
+    // disable & inactive
+    val disabledInactiveContainerColor: Color,
+    val disabledInactiveContentColor: Color,
+    val disabledInactiveBorderColor: Color
+) {
     /**
      * Represents the color used for the SegmentedButton's border,
-     * depending on [enabled] and [checked].
+     * depending on [enabled] and [active].
      *
-     * @param enabled whether the [SegmentedButtonRow] is enabled or not
-     * @param checked whether the [SegmentedButtonRow] item is checked or not
+     * @param enabled whether the [SegmentedButton] is enabled or not
+     * @param active whether the [SegmentedButton] item is checked or not
      */
-    internal fun borderColor(enabled: Boolean, checked: Boolean): Color {
+    internal fun borderColor(enabled: Boolean, active: Boolean): Color {
         return when {
-            enabled && checked -> checkedBorderColor
-            enabled && !checked -> uncheckedBorderColor
-            !enabled && checked -> disabledCheckedContentColor
-            else -> disabledUncheckedContentColor
+            enabled && active -> activeBorderColor
+            enabled && !active -> inactiveBorderColor
+            !enabled && active -> disabledActiveContentColor
+            else -> disabledInactiveContentColor
         }
     }
 
     /**
      * Represents the content color passed to the items
      *
-     * @param enabled whether the [SegmentedButtonRow] is enabled or not
-     * @param checked whether the [SegmentedButtonRow] item is checked or not
+     * @param enabled whether the [SegmentedButton] is enabled or not
+     * @param checked whether the [SegmentedButton] item is checked or not
      */
     internal fun contentColor(enabled: Boolean, checked: Boolean): Color {
         return when {
-            enabled && checked -> checkedContentColor
-            enabled && !checked -> uncheckedContentColor
-            !enabled && checked -> disabledCheckedContentColor
-            else -> disabledUncheckedContentColor
+            enabled && checked -> activeContentColor
+            enabled && !checked -> inactiveContentColor
+            !enabled && checked -> disabledActiveContentColor
+            else -> disabledInactiveContentColor
         }
     }
 
     /**
      * Represents the container color passed to the items
      *
-     * @param enabled whether the [SegmentedButtonRow] is enabled or not
-     * @param checked whether the [SegmentedButtonRow] item is checked or not
+     * @param enabled whether the [SegmentedButton] is enabled or not
+     * @param active whether the [SegmentedButton] item is active or not
      */
-    internal fun containerColor(enabled: Boolean, checked: Boolean): Color {
+    internal fun containerColor(enabled: Boolean, active: Boolean): Color {
         return when {
-            enabled && checked -> checkedContainerColor
-            enabled && !checked -> uncheckedContainerColor
-            !enabled && checked -> disabledCheckedContainerColor
-            else -> disabledUncheckedContainerColor
+            enabled && active -> activeContainerColor
+            enabled && !active -> inactiveContainerColor
+            !enabled && active -> disabledActiveContainerColor
+            else -> disabledInactiveContainerColor
         }
     }
 
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
-        if (javaClass != other?.javaClass) return false
+        if (other === null) return false
+        if (this::class != other::class) return false
 
         other as SegmentedButtonColors
 
-        if (checkedBorderColor != other.checkedBorderColor) return false
-        if (checkedContentColor != other.checkedContentColor) return false
-        if (checkedContainerColor != other.checkedContainerColor) return false
-        if (uncheckedBorderColor != other.uncheckedBorderColor) return false
-        if (uncheckedContentColor != other.uncheckedContentColor) return false
-        if (uncheckedContainerColor != other.uncheckedContainerColor) return false
-        if (disabledCheckedBorderColor != other.disabledCheckedBorderColor) return false
-        if (disabledCheckedContentColor != other.disabledCheckedContentColor) return false
-        if (disabledCheckedContainerColor != other.disabledCheckedContainerColor) return false
-        if (disabledUncheckedBorderColor != other.disabledUncheckedBorderColor) return false
-        if (disabledUncheckedContentColor != other.disabledUncheckedContentColor) return false
-        if (disabledUncheckedContainerColor != other.disabledUncheckedContainerColor) return false
+        if (activeBorderColor != other.activeBorderColor) return false
+        if (activeContentColor != other.activeContentColor) return false
+        if (activeContainerColor != other.activeContainerColor) return false
+        if (inactiveBorderColor != other.inactiveBorderColor) return false
+        if (inactiveContentColor != other.inactiveContentColor) return false
+        if (inactiveContainerColor != other.inactiveContainerColor) return false
+        if (disabledActiveBorderColor != other.disabledActiveBorderColor) return false
+        if (disabledActiveContentColor != other.disabledActiveContentColor) return false
+        if (disabledActiveContainerColor != other.disabledActiveContainerColor) return false
+        if (disabledInactiveBorderColor != other.disabledInactiveBorderColor) return false
+        if (disabledInactiveContentColor != other.disabledInactiveContentColor) return false
+        if (disabledInactiveContainerColor != other.disabledInactiveContainerColor) return false
 
         return true
     }
 
     override fun hashCode(): Int {
-        var result = checkedBorderColor.hashCode()
-        result = 31 * result + checkedContentColor.hashCode()
-        result = 31 * result + checkedContainerColor.hashCode()
-        result = 31 * result + uncheckedBorderColor.hashCode()
-        result = 31 * result + uncheckedContentColor.hashCode()
-        result = 31 * result + uncheckedContainerColor.hashCode()
-        result = 31 * result + disabledCheckedBorderColor.hashCode()
-        result = 31 * result + disabledCheckedContentColor.hashCode()
-        result = 31 * result + disabledCheckedContainerColor.hashCode()
-        result = 31 * result + disabledUncheckedBorderColor.hashCode()
-        result = 31 * result + disabledUncheckedContentColor.hashCode()
-        result = 31 * result + disabledUncheckedContainerColor.hashCode()
+        var result = activeBorderColor.hashCode()
+        result = 31 * result + activeContentColor.hashCode()
+        result = 31 * result + activeContainerColor.hashCode()
+        result = 31 * result + inactiveBorderColor.hashCode()
+        result = 31 * result + inactiveContentColor.hashCode()
+        result = 31 * result + inactiveContainerColor.hashCode()
+        result = 31 * result + disabledActiveBorderColor.hashCode()
+        result = 31 * result + disabledActiveContentColor.hashCode()
+        result = 31 * result + disabledActiveContainerColor.hashCode()
+        result = 31 * result + disabledInactiveBorderColor.hashCode()
+        result = 31 * result + disabledInactiveContentColor.hashCode()
+        result = 31 * result + disabledInactiveContainerColor.hashCode()
         return result
     }
 }
 
+private fun Modifier.interactionZIndex(checked: Boolean, interactionCount: Int) =
+    this.layout { measurable, constraints ->
+        val placeable = measurable.measure(constraints)
+        layout(placeable.width, placeable.height) {
+            val zIndex = interactionCount + if (checked) CheckedZIndexFactor else 0f
+            placeable.place(0, 0, zIndex)
+        }
+    }
+
 private const val CheckedZIndexFactor = 5f
+private val IconSpacing = 8.dp
+
+@OptIn(ExperimentalMaterial3Api::class)
+private class SingleChoiceSegmentedButtonScopeWrapper(scope: RowScope) :
+    SingleChoiceSegmentedButtonRowScope, RowScope by scope
+
+@OptIn(ExperimentalMaterial3Api::class)
+private class MultiChoiceSegmentedButtonScopeWrapper(scope: RowScope) :
+    MultiChoiceSegmentedButtonRowScope, RowScope by scope
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Shapes.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Shapes.kt
index 2fed380..4edc870 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Shapes.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Shapes.kt
@@ -183,12 +183,14 @@
     }
 }
 
-/** Converts a shape token key to the local shape provided by the theme */
-@Composable
-@ReadOnlyComposable
-internal fun ShapeKeyTokens.toShape(): Shape {
-    return MaterialTheme.shapes.fromToken(this)
-}
+/**
+ * Converts a shape token key to the local shape provided by the theme
+ * The color is subscribed to [LocalShapes] changes
+ */
+internal val ShapeKeyTokens.value: Shape
+    @Composable
+    @ReadOnlyComposable
+    get() = MaterialTheme.shapes.fromToken(this)
 
 /** CompositionLocal used to specify the default shapes for the surfaces. */
 internal val LocalShapes = staticCompositionLocalOf { Shapes() }
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SheetDefaults.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SheetDefaults.kt
index d2e2d0d..e59e502 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SheetDefaults.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SheetDefaults.kt
@@ -38,8 +38,10 @@
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
 import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.semantics.contentDescription
 import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.Velocity
 import androidx.compose.ui.unit.dp
@@ -48,7 +50,37 @@
 /**
  * State of a sheet composable, such as [ModalBottomSheet]
  *
- * Contains states relating to it's swipe position as well as animations between state values.
+ * Contains states relating to its swipe position as well as animations between state values.
+ *
+ * @param skipPartiallyExpanded Whether the partially expanded state, if the sheet is large
+ * enough, should be skipped. If true, the sheet will always expand to the [Expanded] state and move
+ * to the [Hidden] state if available when hiding the sheet, either programmatically or by user
+ * interaction.
+ * @param initialValue The initial value of the state.
+ * @param density The density that this state can use to convert values to and from dp.
+ * @param confirmValueChange Optional callback invoked to confirm or veto a pending state change.
+ * @param skipHiddenState Whether the hidden state should be skipped. If true, the sheet will always
+ * expand to the [Expanded] state and move to the [PartiallyExpanded] if available, either
+ * programmatically or by user interaction.
+ */
+@ExperimentalMaterial3Api
+@Suppress("Deprecation")
+fun SheetState(
+    skipPartiallyExpanded: Boolean,
+    density: Density,
+    initialValue: SheetValue = Hidden,
+    confirmValueChange: (SheetValue) -> Boolean = { true },
+    skipHiddenState: Boolean = false,
+) = SheetState(
+    skipPartiallyExpanded, initialValue, confirmValueChange, skipHiddenState
+).also {
+    it.density = density
+}
+
+/**
+ * State of a sheet composable, such as [ModalBottomSheet]
+ *
+ * Contains states relating to its swipe position as well as animations between state values.
  *
  * @param skipPartiallyExpanded Whether the partially expanded state, if the sheet is large
  * enough, should be skipped. If true, the sheet will always expand to the [Expanded] state and move
@@ -62,7 +94,15 @@
  */
 @Stable
 @ExperimentalMaterial3Api
-class SheetState(
+class SheetState @Deprecated(
+    message = "This constructor is deprecated. " +
+        "Please use the constructor that provides a [Density]",
+    replaceWith = ReplaceWith(
+        "SheetState(" +
+            "skipPartiallyExpanded, LocalDensity.current, initialValue, " +
+            "confirmValueChange, skipHiddenState)"
+    )
+) constructor(
     internal val skipPartiallyExpanded: Boolean,
     initialValue: SheetValue = Hidden,
     confirmValueChange: (SheetValue) -> Boolean = { true },
@@ -237,16 +277,46 @@
         initialValue = initialValue,
         animationSpec = SwipeableV2Defaults.AnimationSpec,
         confirmValueChange = confirmValueChange,
+        positionalThreshold = { with(requireDensity()) { 56.dp.toPx() } },
+        velocityThreshold = { with(requireDensity()) { 125.dp.toPx() } }
     )
 
     internal val offset: Float? get() = swipeableState.offset
 
+    internal var density: Density? = null
+    private fun requireDensity() = requireNotNull(density) {
+        "SheetState did not have a density attached. Are you using SheetState with " +
+            "BottomSheetScaffold or ModalBottomSheet component?"
+    }
+
     companion object {
         /**
          * The default [Saver] implementation for [SheetState].
          */
         fun Saver(
             skipPartiallyExpanded: Boolean,
+            confirmValueChange: (SheetValue) -> Boolean,
+            density: Density
+        ) = Saver<SheetState, SheetValue>(
+            save = { it.currentValue },
+            restore = { savedValue ->
+                SheetState(skipPartiallyExpanded, density, savedValue, confirmValueChange)
+            }
+        )
+
+        /**
+         * The default [Saver] implementation for [SheetState].
+         */
+        @Deprecated(
+            message = "This function is deprecated. Please use the overload where Density is" +
+                " provided.",
+            replaceWith = ReplaceWith(
+                "Saver(skipPartiallyExpanded, confirmValueChange, LocalDensity.current)"
+            )
+        )
+        @Suppress("Deprecation")
+        fun Saver(
+            skipPartiallyExpanded: Boolean,
             confirmValueChange: (SheetValue) -> Boolean
         ) = Saver<SheetState, SheetValue>(
             save = { it.currentValue },
@@ -287,17 +357,17 @@
     /** The default shape for bottom sheets in a [Hidden] state. */
     val HiddenShape: Shape
         @Composable get() =
-        SheetBottomTokens.DockedMinimizedContainerShape.toShape()
+            SheetBottomTokens.DockedMinimizedContainerShape.value
 
     /** The default shape for a bottom sheets in [PartiallyExpanded] and [Expanded] states. */
     val ExpandedShape: Shape
         @Composable get() =
-        SheetBottomTokens.DockedContainerShape.toShape()
+            SheetBottomTokens.DockedContainerShape.value
 
     /** The default container color for a bottom sheet. */
     val ContainerColor: Color
         @Composable get() =
-        SheetBottomTokens.DockedContainerColor.toColor()
+            SheetBottomTokens.DockedContainerColor.value
 
     /** The default elevation for a bottom sheet. */
     val Elevation = SheetBottomTokens.DockedModalContainerElevation
@@ -305,7 +375,7 @@
     /** The default color of the scrim overlay for background content. */
     val ScrimColor: Color
         @Composable get() =
-        ScrimTokens.ContainerColor.toColor().copy(ScrimTokens.ContainerOpacity)
+            ScrimTokens.ContainerColor.value.copy(ScrimTokens.ContainerOpacity)
 
     /**
      * The default peek height used by [BottomSheetScaffold].
@@ -328,7 +398,7 @@
         width: Dp = SheetBottomTokens.DockedDragHandleWidth,
         height: Dp = SheetBottomTokens.DockedDragHandleHeight,
         shape: Shape = MaterialTheme.shapes.extraLarge,
-        color: Color = SheetBottomTokens.DockedDragHandleColor.toColor()
+        color: Color = SheetBottomTokens.DockedDragHandleColor.value
             .copy(SheetBottomTokens.DockedDragHandleOpacity),
     ) {
         val dragHandleDescription = getString(Strings.BottomSheetDragHandleDescription)
@@ -414,14 +484,23 @@
     initialValue: SheetValue = Hidden,
     skipHiddenState: Boolean = false,
 ): SheetState {
+
+    val density = LocalDensity.current
     return rememberSaveable(
         skipPartiallyExpanded, confirmValueChange,
         saver = SheetState.Saver(
             skipPartiallyExpanded = skipPartiallyExpanded,
-            confirmValueChange = confirmValueChange
+            confirmValueChange = confirmValueChange,
+            density = density
         )
     ) {
-        SheetState(skipPartiallyExpanded, initialValue, confirmValueChange, skipHiddenState)
+        SheetState(
+            skipPartiallyExpanded,
+            density,
+            initialValue,
+            confirmValueChange,
+            skipHiddenState
+        )
     }
 }
 
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt
index 107d849..32ea286 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.material3
 
+import androidx.annotation.IntRange
 import androidx.compose.foundation.Canvas
 import androidx.compose.foundation.MutatePriority
 import androidx.compose.foundation.MutatorMutex
@@ -153,7 +154,7 @@
     modifier: Modifier = Modifier,
     enabled: Boolean = true,
     valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
-    /*@IntRange(from = 0)*/
+    @IntRange(from = 0)
     steps: Int = 0,
     onValueChangeFinished: (() -> Unit)? = null,
     colors: SliderColors = SliderDefaults.colors(),
@@ -249,7 +250,7 @@
     onValueChangeFinished: (() -> Unit)? = null,
     colors: SliderColors = SliderDefaults.colors(),
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
-    /*@IntRange(from = 0)*/
+    @IntRange(from = 0)
     steps: Int = 0,
     thumb: @Composable (SliderState) -> Unit = {
         SliderDefaults.Thumb(
@@ -412,7 +413,7 @@
     modifier: Modifier = Modifier,
     enabled: Boolean = true,
     valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
-    /*@IntRange(from = 0)*/
+    @IntRange(from = 0)
     steps: Int = 0,
     onValueChangeFinished: (() -> Unit)? = null,
     colors: SliderColors = SliderDefaults.colors()
@@ -541,7 +542,7 @@
             rangeSliderState = rangeSliderState
         )
     },
-    /*@IntRange(from = 0)*/
+    @IntRange(from = 0)
     steps: Int = 0
 ) {
     val state = remember(
@@ -920,31 +921,31 @@
      */
     @Composable
     fun colors(
-        thumbColor: Color = SliderTokens.HandleColor.toColor(),
-        activeTrackColor: Color = SliderTokens.ActiveTrackColor.toColor(),
+        thumbColor: Color = SliderTokens.HandleColor.value,
+        activeTrackColor: Color = SliderTokens.ActiveTrackColor.value,
         activeTickColor: Color = SliderTokens.TickMarksActiveContainerColor
-            .toColor()
+            .value
             .copy(alpha = SliderTokens.TickMarksActiveContainerOpacity),
-        inactiveTrackColor: Color = SliderTokens.InactiveTrackColor.toColor(),
-        inactiveTickColor: Color = SliderTokens.TickMarksInactiveContainerColor.toColor()
+        inactiveTrackColor: Color = SliderTokens.InactiveTrackColor.value,
+        inactiveTickColor: Color = SliderTokens.TickMarksInactiveContainerColor.value
             .copy(alpha = SliderTokens.TickMarksInactiveContainerOpacity),
         disabledThumbColor: Color = SliderTokens.DisabledHandleColor
-            .toColor()
+            .value
             .copy(alpha = SliderTokens.DisabledHandleOpacity)
             .compositeOver(MaterialTheme.colorScheme.surface),
         disabledActiveTrackColor: Color =
             SliderTokens.DisabledActiveTrackColor
-                .toColor()
+                .value
                 .copy(alpha = SliderTokens.DisabledActiveTrackOpacity),
         disabledActiveTickColor: Color = SliderTokens.TickMarksDisabledContainerColor
-            .toColor()
+            .value
             .copy(alpha = SliderTokens.TickMarksDisabledContainerOpacity),
         disabledInactiveTrackColor: Color =
             SliderTokens.DisabledInactiveTrackColor
-                .toColor()
+                .value
                 .copy(alpha = SliderTokens.DisabledInactiveTrackOpacity),
 
-        disabledInactiveTickColor: Color = SliderTokens.TickMarksDisabledContainerColor.toColor()
+        disabledInactiveTickColor: Color = SliderTokens.TickMarksDisabledContainerColor.value
             .copy(alpha = SliderTokens.TickMarksDisabledContainerOpacity)
     ): SliderColors = SliderColors(
         thumbColor = thumbColor,
@@ -999,7 +1000,7 @@
         } else {
             ThumbDefaultElevation
         }
-        val shape = SliderTokens.HandleShape.toShape()
+        val shape = SliderTokens.HandleShape.value
 
         Spacer(
             modifier
@@ -1789,7 +1790,7 @@
 class SliderState(
     initialValue: Float = 0f,
     initialOnValueChange: ((Float) -> Unit)? = null,
-    /*@IntRange(from = 0)*/
+    @IntRange(from = 0)
     val steps: Int = 0,
     val valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
     var onValueChangeFinished: (() -> Unit)? = null
@@ -1909,7 +1910,7 @@
     initialActiveRangeStart: Float = 0f,
     initialActiveRangeEnd: Float = 1f,
     initialOnValueChange: ((FloatRange) -> Unit)? = null,
-    /*@IntRange(from = 0)*/
+    @IntRange(from = 0)
     val steps: Int = 0,
     val valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
     var onValueChangeFinished: (() -> Unit)? = null,
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Snackbar.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Snackbar.kt
index 1fc0e7e..fa89582 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Snackbar.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Snackbar.kt
@@ -406,22 +406,22 @@
  */
 object SnackbarDefaults {
     /** Default shape of a snackbar. */
-    val shape: Shape @Composable get() = SnackbarTokens.ContainerShape.toShape()
+    val shape: Shape @Composable get() = SnackbarTokens.ContainerShape.value
 
     /** Default color of a snackbar. */
-    val color: Color @Composable get() = SnackbarTokens.ContainerColor.toColor()
+    val color: Color @Composable get() = SnackbarTokens.ContainerColor.value
 
     /** Default content color of a snackbar. */
-    val contentColor: Color @Composable get() = SnackbarTokens.SupportingTextColor.toColor()
+    val contentColor: Color @Composable get() = SnackbarTokens.SupportingTextColor.value
 
     /** Default action color of a snackbar. */
-    val actionColor: Color @Composable get() = SnackbarTokens.ActionLabelTextColor.toColor()
+    val actionColor: Color @Composable get() = SnackbarTokens.ActionLabelTextColor.value
 
     /** Default action content color of a snackbar. */
-    val actionContentColor: Color @Composable get() = SnackbarTokens.ActionLabelTextColor.toColor()
+    val actionContentColor: Color @Composable get() = SnackbarTokens.ActionLabelTextColor.value
 
     /** Default dismiss action content color of a snackbar. */
-    val dismissActionContentColor: Color @Composable get() = SnackbarTokens.IconColor.toColor()
+    val dismissActionContentColor: Color @Composable get() = SnackbarTokens.IconColor.value
 }
 
 private val ContainerMaxWidth = 600.dp
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SwipeToDismiss.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SwipeToDismiss.kt
index bce5e38..4506e25 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SwipeToDismiss.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SwipeToDismiss.kt
@@ -23,14 +23,17 @@
 import androidx.compose.foundation.layout.offset
 import androidx.compose.material3.DismissDirection.EndToStart
 import androidx.compose.material3.DismissDirection.StartToEnd
+import androidx.compose.material3.DismissState.Companion.Saver
 import androidx.compose.material3.DismissValue.Default
 import androidx.compose.material3.DismissValue.DismissedToEnd
 import androidx.compose.material3.DismissValue.DismissedToStart
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.saveable.Saver
 import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.IntOffset
@@ -80,6 +83,7 @@
  * State of the [SwipeToDismiss] composable.
  *
  * @param initialValue The initial value of the state.
+ * @param density The density that this state can use to convert values to and from dp.
  * @param confirmValueChange Optional callback invoked to confirm or veto a pending state change.
  * @param positionalThreshold The positional threshold to be used when calculating the target state
  * while a swipe is in progress and when settling after the swipe ends. This is the distance from
@@ -87,17 +91,49 @@
  * subtracted from/to the origin offset. It should always be a positive value.
  */
 @ExperimentalMaterial3Api
-class DismissState(
+@Suppress("Deprecation", "PrimitiveInLambda")
+fun DismissState(
+    initialValue: DismissValue,
+    density: Density,
+    confirmValueChange: (DismissValue) -> Boolean = { true },
+    positionalThreshold: (totalDistance: Float) -> Float
+) = DismissState(
+    initialValue = initialValue,
+    confirmValueChange = confirmValueChange,
+    positionalThreshold = positionalThreshold
+).also {
+    it.density = density
+}
+
+/**
+ * State of the [SwipeToDismiss] composable.
+ *
+ * @param initialValue The initial value of the state.
+ * @param confirmValueChange Optional callback invoked to confirm or veto a pending state change.
+ * @param positionalThreshold The positional threshold to be used when calculating the target state
+ * while a swipe is in progress and when settling after the swipe ends. This is the distance from
+ * the start of a transition. It will be, depending on the direction of the interaction, added or
+ * subtracted from/to the origin offset. It should always be a positive value.
+ */
+@Suppress("PrimitiveInLambda")
+@ExperimentalMaterial3Api
+class DismissState @Deprecated(
+    message = "This constructor is deprecated. " +
+        "Please use the constructor that provides a [Density]",
+    replaceWith = ReplaceWith(
+        "DismissState(" +
+            "initialValue, LocalDensity.current, confirmValueChange, positionalThreshold)"
+    )
+) constructor(
     initialValue: DismissValue,
     confirmValueChange: (DismissValue) -> Boolean = { true },
-    positionalThreshold: Density.(totalDistance: Float) -> Float =
-        SwipeToDismissDefaults.FixedPositionalThreshold,
+    positionalThreshold: (totalDistance: Float) -> Float
 ) {
     internal val swipeableState = SwipeableV2State(
         initialValue = initialValue,
         confirmValueChange = confirmValueChange,
         positionalThreshold = positionalThreshold,
-        velocityThreshold = DismissThreshold
+        velocityThreshold = { with(requireDensity()) { DismissThreshold.toPx() } }
     )
 
     internal val offset: Float? get() = swipeableState.offset
@@ -132,8 +168,10 @@
      * If the composable is settled at the default state, then this will be null. Use this to
      * change the background of the [SwipeToDismiss] if you want different actions on each side.
      */
-    val dismissDirection: DismissDirection? get() =
-        if (offset == 0f || offset == null) null else if (offset!! > 0f) StartToEnd else EndToStart
+    val dismissDirection: DismissDirection?
+        get() = if (offset == 0f || offset == null)
+            null
+        else if (offset!! > 0f) StartToEnd else EndToStart
 
     /**
      * Whether the component has been dismissed in the given [direction].
@@ -173,19 +211,52 @@
         swipeableState.animateTo(targetValue = targetValue)
     }
 
+    internal var density: Density? = null
+    private fun requireDensity() = requireNotNull(density) {
+        "DismissState did not have a density attached. Are you using DismissState with " +
+            "the SwipeToDismiss component?"
+    }
+
     companion object {
+
         /**
          * The default [Saver] implementation for [DismissState].
          */
         fun Saver(
             confirmValueChange: (DismissValue) -> Boolean,
-            positionalThreshold: Density.(totalDistance: Float) -> Float,
+            positionalThreshold: (totalDistance: Float) -> Float,
+            density: Density
         ) =
             Saver<DismissState, DismissValue>(
                 save = { it.currentValue },
                 restore = {
                     DismissState(
-                        it, confirmValueChange, positionalThreshold)
+                        it, density, confirmValueChange, positionalThreshold
+                    )
+                }
+            )
+
+        /**
+         * The default [Saver] implementation for [DismissState].
+         */
+        @Deprecated(
+            message = "This function is deprecated. Please use the overload where Density is" +
+                " provided.",
+            replaceWith = ReplaceWith(
+                "Saver(confirmValueChange, positionalThreshold, LocalDensity.current)"
+            )
+        )
+        @Suppress("Deprecation")
+        fun Saver(
+            confirmValueChange: (DismissValue) -> Boolean,
+            positionalThreshold: (totalDistance: Float) -> Float,
+        ) =
+            Saver<DismissState, DismissValue>(
+                save = { it.currentValue },
+                restore = {
+                    DismissState(
+                        it, confirmValueChange, positionalThreshold
+                    )
                 }
             )
     }
@@ -201,17 +272,24 @@
  * the start of a transition. It will be, depending on the direction of the interaction, added or
  * subtracted from/to the origin offset. It should always be a positive value.
  */
+@Suppress("PrimitiveInLambda")
 @Composable
 @ExperimentalMaterial3Api
 fun rememberDismissState(
     initialValue: DismissValue = Default,
     confirmValueChange: (DismissValue) -> Boolean = { true },
-    positionalThreshold: Density.(totalDistance: Float) -> Float =
-        SwipeToDismissDefaults.FixedPositionalThreshold,
+    positionalThreshold: (totalDistance: Float) -> Float =
+        SwipeToDismissDefaults.fixedPositionalThreshold,
 ): DismissState {
+    val density = LocalDensity.current
     return rememberSaveable(
-        saver = DismissState.Saver(confirmValueChange, positionalThreshold)) {
-        DismissState(initialValue, confirmValueChange, positionalThreshold)
+        saver = DismissState.Saver(
+            confirmValueChange = confirmValueChange,
+            density = density,
+            positionalThreshold = positionalThreshold
+        )
+    ) {
+        DismissState(initialValue, density, confirmValueChange, positionalThreshold)
     }
 }
 
@@ -236,6 +314,13 @@
     modifier: Modifier = Modifier,
     directions: Set<DismissDirection> = setOf(EndToStart, StartToEnd),
 ) {
+
+    // b/278692145 Remove this once deprecated methods without density are removed
+    val density = LocalDensity.current
+    SideEffect {
+        state.density = density
+    }
+
     val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
 
     Box(
@@ -257,23 +342,27 @@
                     Default -> 0f
                 }
             }
-        ) {
-            Row(
-                content = background,
-                modifier = Modifier.matchParentSize()
-            )
-            Row(
-                content = dismissContent,
-                modifier = Modifier.offset { IntOffset(state.requireOffset().roundToInt(), 0) }
-            )
-        }
+    ) {
+        Row(
+            content = background,
+            modifier = Modifier.matchParentSize()
+        )
+        Row(
+            content = dismissContent,
+            modifier = Modifier.offset { IntOffset(state.requireOffset().roundToInt(), 0) }
+        )
+    }
 }
 
 /** Contains default values for [SwipeToDismiss] and [DismissState]. */
+@Suppress("PrimitiveInLambda")
 @ExperimentalMaterial3Api
 object SwipeToDismissDefaults {
     /** Default positional threshold of 56.dp for [DismissState]. */
-    val FixedPositionalThreshold: Density.(totalDistance: Float) -> Float = { _ -> 56.dp.toPx() }
+    val fixedPositionalThreshold: (totalDistance: Float) -> Float
+        @Composable get() = with(LocalDensity.current) {
+            { 56.dp.toPx() }
+        }
 }
 
 private val DismissThreshold = 125.dp
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Swipeable.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Swipeable.kt
index 3155cfd..f06b0e3 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Swipeable.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Swipeable.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.material3
 
+import androidx.annotation.FloatRange
 import androidx.compose.animation.core.Animatable
 import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.SpringSpec
@@ -211,7 +212,7 @@
 
     private suspend fun snapInternalToOffset(target: Float) {
         draggableState.drag {
-            dragBy(target - absoluteOffset.value)
+            dragBy(target - absoluteOffset.floatValue)
         }
     }
 
@@ -244,8 +245,8 @@
         get() {
             // TODO(calintat): Track current velocity (b/149549482) and use that here.
             val target = animationTarget.value ?: computeTarget(
-                offset = offset.value,
-                lastValue = anchors.getOffset(currentValue) ?: offset.value,
+                offset = offset.floatValue,
+                lastValue = anchors.getOffset(currentValue) ?: offset.floatValue,
                 anchors = anchors.keys,
                 thresholds = thresholds,
                 velocity = 0f,
@@ -362,7 +363,7 @@
         latestNonEmptyAnchorsFlow.collect { anchors ->
             val lastAnchor = anchors.getOffset(currentValue)!!
             val targetValue = computeTarget(
-                offset = offset.value,
+                offset = offset.floatValue,
                 lastValue = lastAnchor,
                 anchors = anchors.keys,
                 thresholds = thresholds,
@@ -431,7 +432,7 @@
 internal class SwipeProgress<T>(
     val from: T,
     val to: T,
-    /*@FloatRange(from = 0.0, to = 1.0)*/
+    @FloatRange(from = 0.0, to = 1.0)
     val fraction: Float
 ) {
     override fun equals(other: Any?): Boolean {
@@ -536,7 +537,7 @@
  * the new anchor. The target anchor is calculated based on the provided positional [thresholds].
  *
  * Swiping is constrained between the minimum and maximum anchors. If the user attempts to swipe
- * past these bounds, a resistance effect will be applied by default. The amount of resistance at
+ * past these bounds, a resistance effect will bfe applied by default. The amount of resistance at
  * each edge is specified by the [resistance] config. To disable all resistance, set it to `null`.
  *
  * @param T The type of the state.
@@ -650,7 +651,7 @@
 @Immutable
 @ExperimentalMaterial3Api
 internal data class FractionalThreshold(
-    /*@FloatRange(from = 0.0, to = 1.0)*/
+    @FloatRange(from = 0.0, to = 1.0)
     private val fraction: Float
 ) : ThresholdConfig {
     override fun Density.computeThreshold(fromValue: Float, toValue: Float): Float {
@@ -682,11 +683,11 @@
  */
 @Immutable
 internal class ResistanceConfig(
-    /*@FloatRange(from = 0.0, fromInclusive = false)*/
+    @FloatRange(from = 0.0, fromInclusive = false)
     val basis: Float,
-    /*@FloatRange(from = 0.0)*/
+    @FloatRange(from = 0.0)
     val factorAtMin: Float = StandardResistanceFactor,
-    /*@FloatRange(from = 0.0)*/
+    @FloatRange(from = 0.0)
     val factorAtMax: Float = StandardResistanceFactor
 ) {
     fun computeResistance(overflow: Float): Float {
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SwipeableV2.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SwipeableV2.kt
index 8db36b9..dfead31 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SwipeableV2.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SwipeableV2.kt
@@ -18,6 +18,7 @@
 
 package androidx.compose.material3
 
+import androidx.annotation.FloatRange
 import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.SpringSpec
 import androidx.compose.animation.core.animate
@@ -38,17 +39,8 @@
 import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.layout.LayoutModifier
-import androidx.compose.ui.layout.Measurable
-import androidx.compose.ui.layout.MeasureResult
-import androidx.compose.ui.layout.MeasureScope
-import androidx.compose.ui.layout.OnRemeasuredModifier
-import androidx.compose.ui.platform.InspectorInfo
-import androidx.compose.ui.platform.InspectorValueInfo
-import androidx.compose.ui.platform.debugInspectorInfo
-import androidx.compose.ui.unit.Constraints
-import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.layout.onSizeChanged
+import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.dp
 import kotlin.math.abs
@@ -111,37 +103,27 @@
     possibleValues: Set<T>,
     anchorChangeHandler: AnchorChangeHandler<T>? = null,
     calculateAnchor: (value: T, layoutSize: IntSize) -> Float?,
-) = this.then(SwipeAnchorsModifier(
-    onDensityChanged = { state.density = it },
-    onSizeChanged = { layoutSize ->
-        val previousAnchors = state.anchors
-        val newAnchors = mutableMapOf<T, Float>()
-        possibleValues.forEach {
-            val anchorValue = calculateAnchor(it, layoutSize)
-            if (anchorValue != null) {
-                newAnchors[it] = anchorValue
-            }
+) = onSizeChanged { layoutSize ->
+    val previousAnchors = state.anchors
+    val newAnchors = mutableMapOf<T, Float>()
+    possibleValues.forEach {
+        val anchorValue = calculateAnchor(it, layoutSize)
+        if (anchorValue != null) {
+            newAnchors[it] = anchorValue
         }
-        if (previousAnchors != newAnchors) {
-            val previousTarget = state.targetValue
-            val stateRequiresCleanup = state.updateAnchors(newAnchors)
-            if (stateRequiresCleanup) {
-                anchorChangeHandler?.onAnchorsChanged(
-                    previousTarget,
-                    previousAnchors,
-                    newAnchors
-                )
-            }
-        }
-    },
-    inspectorInfo = debugInspectorInfo {
-        name = "swipeAnchors"
-        properties["state"] = state
-        properties["possibleValues"] = possibleValues
-        properties["anchorChangeHandler"] = anchorChangeHandler
-        properties["calculateAnchor"] = calculateAnchor
     }
-))
+    if (previousAnchors != newAnchors) {
+        val previousTarget = state.targetValue
+        val stateRequiresCleanup = state.updateAnchors(newAnchors)
+        if (stateRequiresCleanup) {
+            anchorChangeHandler?.onAnchorsChanged(
+                previousTarget,
+                previousAnchors,
+                newAnchors
+            )
+        }
+    }
+}
 
 /**
  * State of the [swipeableV2] modifier.
@@ -153,24 +135,23 @@
  * @param initialValue The initial value of the state.
  * @param animationSpec The default animation that will be used to animate to a new state.
  * @param confirmValueChange Optional callback invoked to confirm or veto a pending state change.
- * @param positionalThreshold The positional threshold to be used when calculating the target state
- * while a swipe is in progress and when settling after the swipe ends. This is the distance from
- * the start of a transition. It will be, depending on the direction of the interaction, added or
- * subtracted from/to the origin offset. It should always be a positive value. See the
- * [fractionalPositionalThreshold] and [fixedPositionalThreshold] methods.
- * @param velocityThreshold The velocity threshold (in dp per second) that the end velocity has to
+ * @param positionalThreshold The positional threshold, in px, to be used when calculating the
+ * target state while a swipe is in progress and when settling after the swipe ends. This is the
+ * distance from the start of a transition. It will be, depending on the direction of the
+ * interaction, added or subtracted from/to the origin offset. It should always be a positive value.
+ * @param velocityThreshold The velocity threshold (in px per second) that the end velocity has to
  * exceed in order to animate to the next state, even if the [positionalThreshold] has not been
  * reached.
  */
+@Suppress("PrimitiveInLambda")
 @Stable
 @ExperimentalMaterial3Api
 internal class SwipeableV2State<T>(
     initialValue: T,
+    internal val positionalThreshold: (totalDistance: Float) -> Float,
+    internal val velocityThreshold: () -> Float,
     internal val animationSpec: AnimationSpec<Float> = SwipeableV2Defaults.AnimationSpec,
     internal val confirmValueChange: (newValue: T) -> Boolean = { true },
-    internal val positionalThreshold: Density.(totalDistance: Float) -> Float =
-        SwipeableV2Defaults.PositionalThreshold,
-    internal val velocityThreshold: Dp = SwipeableV2Defaults.VelocityThreshold,
 ) {
 
     private val swipeMutex = InternalMutatorMutex()
@@ -250,7 +231,7 @@
      * The fraction of the progress going from [currentValue] to [targetValue], within [0f..1f]
      * bounds.
      */
-    /*@FloatRange(from = 0f, to = 1f)*/
+    @get:FloatRange(from = 0.0, to = 1.0)
     val progress: Float by derivedStateOf {
         val a = anchors[currentValue] ?: 0f
         val b = anchors[targetValue] ?: 0f
@@ -287,8 +268,6 @@
 
     internal var anchors by mutableStateOf(emptyMap<T, Float>())
 
-    internal var density: Density? = null
-
     /**
      * Update the anchors.
      * If the previous set of anchors was empty, attempt to update the offset to match the initial
@@ -417,8 +396,7 @@
     ): T {
         val currentAnchors = anchors
         val currentAnchor = currentAnchors[currentValue]
-        val currentDensity = requireDensity()
-        val velocityThresholdPx = with(currentDensity) { velocityThreshold.toPx() }
+        val velocityThresholdPx = velocityThreshold()
         return if (currentAnchor == offset || currentAnchor == null) {
             currentValue
         } else if (currentAnchor < offset) {
@@ -428,7 +406,7 @@
             } else {
                 val upper = currentAnchors.closestAnchor(offset, true)
                 val distance = abs(currentAnchors.getValue(upper) - currentAnchor)
-                val relativeThreshold = abs(positionalThreshold(currentDensity, distance))
+                val relativeThreshold = abs(positionalThreshold(distance))
                 val absoluteThreshold = abs(currentAnchor + relativeThreshold)
                 if (offset < absoluteThreshold) currentValue else upper
             }
@@ -439,7 +417,7 @@
             } else {
                 val lower = currentAnchors.closestAnchor(offset, false)
                 val distance = abs(currentAnchor - currentAnchors.getValue(lower))
-                val relativeThreshold = abs(positionalThreshold(currentDensity, distance))
+                val relativeThreshold = abs(positionalThreshold(distance))
                 val absoluteThreshold = abs(currentAnchor - relativeThreshold)
                 if (offset < 0) {
                     // For negative offsets, larger absolute thresholds are closer to lower anchors
@@ -452,11 +430,6 @@
         }
     }
 
-    private fun requireDensity() = requireNotNull(density) {
-        "SwipeableState did not have a density attached. Are you using Modifier.swipeable with " +
-            "this=$this SwipeableState?"
-    }
-
     private suspend fun swipe(
         swipePriority: MutatePriority = MutatePriority.Default,
         action: suspend () -> Unit
@@ -490,8 +463,8 @@
         fun <T : Any> Saver(
             animationSpec: AnimationSpec<Float>,
             confirmValueChange: (T) -> Boolean,
-            positionalThreshold: Density.(distance: Float) -> Float,
-            velocityThreshold: Dp
+            positionalThreshold: (distance: Float) -> Float,
+            velocityThreshold: () -> Float
         ) = Saver<SwipeableV2State<T>, T>(
             save = { it.currentValue },
             restore = {
@@ -514,6 +487,7 @@
  * @param animationSpec The default animation that will be used to animate to a new value.
  * @param confirmValueChange Optional callback invoked to confirm or veto a pending value change.
  */
+@Suppress("PrimitiveInLambda")
 @Composable
 @ExperimentalMaterial3Api
 internal fun <T : Any> rememberSwipeableV2State(
@@ -521,51 +495,32 @@
     animationSpec: AnimationSpec<Float> = SwipeableV2Defaults.AnimationSpec,
     confirmValueChange: (newValue: T) -> Boolean = { true }
 ): SwipeableV2State<T> {
+    val positionalThreshold = SwipeableV2Defaults.positionalThreshold
+    val velocityThreshold = SwipeableV2Defaults.velocityThreshold
+
     return rememberSaveable(
-        initialValue, animationSpec, confirmValueChange,
+        initialValue, animationSpec, confirmValueChange, positionalThreshold, velocityThreshold,
         saver = SwipeableV2State.Saver(
             animationSpec = animationSpec,
             confirmValueChange = confirmValueChange,
-            positionalThreshold = SwipeableV2Defaults.PositionalThreshold,
-            velocityThreshold = SwipeableV2Defaults.VelocityThreshold
+            positionalThreshold = positionalThreshold,
+            velocityThreshold = velocityThreshold
         ),
     ) {
         SwipeableV2State(
             initialValue = initialValue,
             animationSpec = animationSpec,
             confirmValueChange = confirmValueChange,
-            positionalThreshold = SwipeableV2Defaults.PositionalThreshold,
-            velocityThreshold = SwipeableV2Defaults.VelocityThreshold
+            positionalThreshold = positionalThreshold,
+            velocityThreshold = velocityThreshold
         )
     }
 }
 
 /**
- * Expresses a fixed positional threshold of [threshold] dp. This will be the distance from an
- * anchor that needs to be reached for [SwipeableV2State] to settle to the next closest anchor.
- *
- * @see [fractionalPositionalThreshold] for a fractional positional threshold
- */
-@ExperimentalMaterial3Api
-internal fun fixedPositionalThreshold(threshold: Dp): Density.(distance: Float) -> Float = {
-    threshold.toPx()
-}
-
-/**
- * Expresses a relative positional threshold of the [fraction] of the distance to the closest anchor
- * in the current direction. This will be the distance from an anchor that needs to be reached for
- * [SwipeableV2State] to settle to the next closest anchor.
- *
- * @see [fixedPositionalThreshold] for a fixed positional threshold
- */
-@ExperimentalMaterial3Api
-internal fun fractionalPositionalThreshold(
-    fraction: Float
-): Density.(distance: Float) -> Float = { distance -> distance * fraction }
-
-/**
  * Contains useful defaults for [swipeableV2] and [SwipeableV2State].
  */
+@Suppress("PrimitiveInLambda")
 @Stable
 @ExperimentalMaterial3Api
 internal object SwipeableV2Defaults {
@@ -579,14 +534,17 @@
      * The default velocity threshold (1.8 dp per millisecond) used by [rememberSwipeableV2State].
      */
     @ExperimentalMaterial3Api
-    val VelocityThreshold: Dp = 125.dp
+    val velocityThreshold: () -> Float
+        @Composable get() = with(LocalDensity.current) { { 125.dp.toPx() } }
 
     /**
      * The default positional threshold (56 dp) used by [rememberSwipeableV2State]
      */
     @ExperimentalMaterial3Api
-    val PositionalThreshold: Density.(totalDistance: Float) -> Float =
-        fixedPositionalThreshold(56.dp)
+    val positionalThreshold: (totalDistance: Float) -> Float
+        @Composable get() = with(LocalDensity.current) {
+            { 56.dp.toPx() }
+        }
 
     /**
      * A [AnchorChangeHandler] implementation that attempts to reconcile an in-progress animation
@@ -646,37 +604,6 @@
     )
 }
 
-@Stable
-private class SwipeAnchorsModifier(
-    private val onDensityChanged: (density: Density) -> Unit,
-    private val onSizeChanged: (layoutSize: IntSize) -> Unit,
-    inspectorInfo: InspectorInfo.() -> Unit,
-) : LayoutModifier, OnRemeasuredModifier, InspectorValueInfo(inspectorInfo) {
-
-    private var lastDensity: Float = -1f
-    private var lastFontScale: Float = -1f
-
-    override fun MeasureScope.measure(
-        measurable: Measurable,
-        constraints: Constraints
-    ): MeasureResult {
-        if (density != lastDensity || fontScale != lastFontScale) {
-            onDensityChanged(Density(density, fontScale))
-            lastDensity = density
-            lastFontScale = fontScale
-        }
-        val placeable = measurable.measure(constraints)
-        return layout(placeable.width, placeable.height) { placeable.place(0, 0) }
-    }
-
-    override fun onRemeasured(size: IntSize) {
-        onSizeChanged(size)
-    }
-
-    override fun toString() = "SwipeAnchorsModifierImpl(updateDensity=$onDensityChanged, " +
-        "onSizeChanged=$onSizeChanged)"
-}
-
 private fun <T> Map<T, Float>.closestAnchor(
     offset: Float = 0f,
     searchUpwards: Boolean = false
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 98ac45c..a8de2ab 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
@@ -162,7 +162,7 @@
             colors = colors,
             thumbValue = offset.asState(),
             interactionSource = interactionSource,
-            thumbShape = SwitchTokens.HandleShape.toShape(),
+            thumbShape = SwitchTokens.HandleShape.value,
             uncheckedThumbDiameter = uncheckedThumbDiameter,
             minBound = thumbPaddingStart,
             maxBound = ThumbPathLength,
@@ -208,7 +208,7 @@
         thumbValue.value
     }
 
-    val trackShape = SwitchTokens.TrackShape.toShape()
+    val trackShape = SwitchTokens.TrackShape.value
     val modifier = Modifier
         .align(Alignment.Center)
         .width(SwitchWidth)
@@ -282,35 +282,35 @@
      */
     @Composable
     fun colors(
-        checkedThumbColor: Color = SwitchTokens.SelectedHandleColor.toColor(),
-        checkedTrackColor: Color = SwitchTokens.SelectedTrackColor.toColor(),
+        checkedThumbColor: Color = SwitchTokens.SelectedHandleColor.value,
+        checkedTrackColor: Color = SwitchTokens.SelectedTrackColor.value,
         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 = SwitchTokens.DisabledSelectedHandleColor.toColor()
+        checkedIconColor: Color = SwitchTokens.SelectedIconColor.value,
+        uncheckedThumbColor: Color = SwitchTokens.UnselectedHandleColor.value,
+        uncheckedTrackColor: Color = SwitchTokens.UnselectedTrackColor.value,
+        uncheckedBorderColor: Color = SwitchTokens.UnselectedFocusTrackOutlineColor.value,
+        uncheckedIconColor: Color = SwitchTokens.UnselectedIconColor.value,
+        disabledCheckedThumbColor: Color = SwitchTokens.DisabledSelectedHandleColor.value
             .copy(alpha = SwitchTokens.DisabledSelectedHandleOpacity)
             .compositeOver(MaterialTheme.colorScheme.surface),
-        disabledCheckedTrackColor: Color = SwitchTokens.DisabledSelectedTrackColor.toColor()
+        disabledCheckedTrackColor: Color = SwitchTokens.DisabledSelectedTrackColor.value
             .copy(alpha = SwitchTokens.DisabledTrackOpacity)
             .compositeOver(MaterialTheme.colorScheme.surface),
         disabledCheckedBorderColor: Color = Color.Transparent,
-        disabledCheckedIconColor: Color = SwitchTokens.DisabledSelectedIconColor.toColor()
+        disabledCheckedIconColor: Color = SwitchTokens.DisabledSelectedIconColor.value
             .copy(alpha = SwitchTokens.DisabledSelectedIconOpacity)
             .compositeOver(MaterialTheme.colorScheme.surface),
-        disabledUncheckedThumbColor: Color = SwitchTokens.DisabledUnselectedHandleColor.toColor()
+        disabledUncheckedThumbColor: Color = SwitchTokens.DisabledUnselectedHandleColor.value
             .copy(alpha = SwitchTokens.DisabledUnselectedHandleOpacity)
             .compositeOver(MaterialTheme.colorScheme.surface),
-        disabledUncheckedTrackColor: Color = SwitchTokens.DisabledUnselectedTrackColor.toColor()
+        disabledUncheckedTrackColor: Color = SwitchTokens.DisabledUnselectedTrackColor.value
             .copy(alpha = SwitchTokens.DisabledTrackOpacity)
             .compositeOver(MaterialTheme.colorScheme.surface),
         disabledUncheckedBorderColor: Color =
-            SwitchTokens.DisabledUnselectedTrackOutlineColor.toColor()
+            SwitchTokens.DisabledUnselectedTrackOutlineColor.value
                 .copy(alpha = SwitchTokens.DisabledTrackOpacity)
                 .compositeOver(MaterialTheme.colorScheme.surface),
-        disabledUncheckedIconColor: Color = SwitchTokens.DisabledUnselectedIconColor.toColor()
+        disabledUncheckedIconColor: Color = SwitchTokens.DisabledUnselectedIconColor.value
             .copy(alpha = SwitchTokens.DisabledUnselectedIconOpacity)
             .compositeOver(MaterialTheme.colorScheme.surface),
     ): SwitchColors = SwitchColors(
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TabRow.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TabRow.kt
index 6dbce24..26dabf2 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TabRow.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TabRow.kt
@@ -411,12 +411,12 @@
     /** Default container color of a tab row. */
     val containerColor: Color
         @Composable get() =
-            PrimaryNavigationTabTokens.ContainerColor.toColor()
+            PrimaryNavigationTabTokens.ContainerColor.value
 
     /** Default content color of a tab row. */
     val contentColor: Color
         @Composable get() =
-            PrimaryNavigationTabTokens.ActiveLabelTextColor.toColor()
+            PrimaryNavigationTabTokens.ActiveLabelTextColor.value
 
     /**
      * Default indicator, which will be positioned at the bottom of the [TabRow], on top of the
@@ -462,7 +462,7 @@
         modifier: Modifier = Modifier,
         width: Dp = 24.dp,
         height: Dp = PrimaryNavigationTabTokens.ActiveIndicatorHeight,
-        color: Color = PrimaryNavigationTabTokens.ActiveIndicatorColor.toColor(),
+        color: Color = PrimaryNavigationTabTokens.ActiveIndicatorColor.value,
         shape: Shape = PrimaryNavigationTabTokens.ActiveIndicatorShape
     ) {
         Spacer(
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TextFieldDefaults.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TextFieldDefaults.kt
index 4c3ae02..c176e55 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TextFieldDefaults.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TextFieldDefaults.kt
@@ -55,7 +55,7 @@
 @Immutable
 object TextFieldDefaults {
     /** Default shape for a [TextField]. */
-    val shape: Shape @Composable get() = FilledTextFieldTokens.ContainerShape.toShape()
+    val shape: Shape @Composable get() = FilledTextFieldTokens.ContainerShape.value
 
     /**
      * The default min width applied to a [TextField].
@@ -245,58 +245,58 @@
      */
     @Composable
     fun colors(
-        focusedTextColor: Color = FilledTextFieldTokens.FocusInputColor.toColor(),
-        unfocusedTextColor: Color = FilledTextFieldTokens.InputColor.toColor(),
-        disabledTextColor: Color = FilledTextFieldTokens.DisabledInputColor.toColor()
+        focusedTextColor: Color = FilledTextFieldTokens.FocusInputColor.value,
+        unfocusedTextColor: Color = FilledTextFieldTokens.InputColor.value,
+        disabledTextColor: Color = FilledTextFieldTokens.DisabledInputColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledInputOpacity),
-        errorTextColor: Color = FilledTextFieldTokens.ErrorInputColor.toColor(),
-        focusedContainerColor: Color = FilledTextFieldTokens.ContainerColor.toColor(),
-        unfocusedContainerColor: Color = FilledTextFieldTokens.ContainerColor.toColor(),
-        disabledContainerColor: Color = FilledTextFieldTokens.ContainerColor.toColor(),
-        errorContainerColor: Color = FilledTextFieldTokens.ContainerColor.toColor(),
-        cursorColor: Color = FilledTextFieldTokens.CaretColor.toColor(),
-        errorCursorColor: Color = FilledTextFieldTokens.ErrorFocusCaretColor.toColor(),
+        errorTextColor: Color = FilledTextFieldTokens.ErrorInputColor.value,
+        focusedContainerColor: Color = FilledTextFieldTokens.ContainerColor.value,
+        unfocusedContainerColor: Color = FilledTextFieldTokens.ContainerColor.value,
+        disabledContainerColor: Color = FilledTextFieldTokens.ContainerColor.value,
+        errorContainerColor: Color = FilledTextFieldTokens.ContainerColor.value,
+        cursorColor: Color = FilledTextFieldTokens.CaretColor.value,
+        errorCursorColor: Color = FilledTextFieldTokens.ErrorFocusCaretColor.value,
         selectionColors: TextSelectionColors = LocalTextSelectionColors.current,
-        focusedIndicatorColor: Color = FilledTextFieldTokens.FocusActiveIndicatorColor.toColor(),
-        unfocusedIndicatorColor: Color = FilledTextFieldTokens.ActiveIndicatorColor.toColor(),
-        disabledIndicatorColor: Color = FilledTextFieldTokens.DisabledActiveIndicatorColor.toColor()
+        focusedIndicatorColor: Color = FilledTextFieldTokens.FocusActiveIndicatorColor.value,
+        unfocusedIndicatorColor: Color = FilledTextFieldTokens.ActiveIndicatorColor.value,
+        disabledIndicatorColor: Color = FilledTextFieldTokens.DisabledActiveIndicatorColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledActiveIndicatorOpacity),
-        errorIndicatorColor: Color = FilledTextFieldTokens.ErrorActiveIndicatorColor.toColor(),
-        focusedLeadingIconColor: Color = FilledTextFieldTokens.FocusLeadingIconColor.toColor(),
-        unfocusedLeadingIconColor: Color = FilledTextFieldTokens.LeadingIconColor.toColor(),
-        disabledLeadingIconColor: Color = FilledTextFieldTokens.DisabledLeadingIconColor.toColor()
+        errorIndicatorColor: Color = FilledTextFieldTokens.ErrorActiveIndicatorColor.value,
+        focusedLeadingIconColor: Color = FilledTextFieldTokens.FocusLeadingIconColor.value,
+        unfocusedLeadingIconColor: Color = FilledTextFieldTokens.LeadingIconColor.value,
+        disabledLeadingIconColor: Color = FilledTextFieldTokens.DisabledLeadingIconColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledLeadingIconOpacity),
-        errorLeadingIconColor: Color = FilledTextFieldTokens.ErrorLeadingIconColor.toColor(),
-        focusedTrailingIconColor: Color = FilledTextFieldTokens.FocusTrailingIconColor.toColor(),
-        unfocusedTrailingIconColor: Color = FilledTextFieldTokens.TrailingIconColor.toColor(),
-        disabledTrailingIconColor: Color = FilledTextFieldTokens.DisabledTrailingIconColor.toColor()
+        errorLeadingIconColor: Color = FilledTextFieldTokens.ErrorLeadingIconColor.value,
+        focusedTrailingIconColor: Color = FilledTextFieldTokens.FocusTrailingIconColor.value,
+        unfocusedTrailingIconColor: Color = FilledTextFieldTokens.TrailingIconColor.value,
+        disabledTrailingIconColor: Color = FilledTextFieldTokens.DisabledTrailingIconColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledTrailingIconOpacity),
-        errorTrailingIconColor: Color = FilledTextFieldTokens.ErrorTrailingIconColor.toColor(),
-        focusedLabelColor: Color = FilledTextFieldTokens.FocusLabelColor.toColor(),
-        unfocusedLabelColor: Color = FilledTextFieldTokens.LabelColor.toColor(),
-        disabledLabelColor: Color = FilledTextFieldTokens.DisabledLabelColor.toColor()
+        errorTrailingIconColor: Color = FilledTextFieldTokens.ErrorTrailingIconColor.value,
+        focusedLabelColor: Color = FilledTextFieldTokens.FocusLabelColor.value,
+        unfocusedLabelColor: Color = FilledTextFieldTokens.LabelColor.value,
+        disabledLabelColor: Color = FilledTextFieldTokens.DisabledLabelColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledLabelOpacity),
-        errorLabelColor: Color = FilledTextFieldTokens.ErrorLabelColor.toColor(),
-        focusedPlaceholderColor: Color = FilledTextFieldTokens.InputPlaceholderColor.toColor(),
-        unfocusedPlaceholderColor: Color = FilledTextFieldTokens.InputPlaceholderColor.toColor(),
-        disabledPlaceholderColor: Color = FilledTextFieldTokens.DisabledInputColor.toColor()
+        errorLabelColor: Color = FilledTextFieldTokens.ErrorLabelColor.value,
+        focusedPlaceholderColor: Color = FilledTextFieldTokens.InputPlaceholderColor.value,
+        unfocusedPlaceholderColor: Color = FilledTextFieldTokens.InputPlaceholderColor.value,
+        disabledPlaceholderColor: Color = FilledTextFieldTokens.DisabledInputColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledInputOpacity),
-        errorPlaceholderColor: Color = FilledTextFieldTokens.InputPlaceholderColor.toColor(),
-        focusedSupportingTextColor: Color = FilledTextFieldTokens.FocusSupportingColor.toColor(),
-        unfocusedSupportingTextColor: Color = FilledTextFieldTokens.SupportingColor.toColor(),
-        disabledSupportingTextColor: Color = FilledTextFieldTokens.DisabledSupportingColor.toColor()
+        errorPlaceholderColor: Color = FilledTextFieldTokens.InputPlaceholderColor.value,
+        focusedSupportingTextColor: Color = FilledTextFieldTokens.FocusSupportingColor.value,
+        unfocusedSupportingTextColor: Color = FilledTextFieldTokens.SupportingColor.value,
+        disabledSupportingTextColor: Color = FilledTextFieldTokens.DisabledSupportingColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledSupportingOpacity),
-        errorSupportingTextColor: Color = FilledTextFieldTokens.ErrorSupportingColor.toColor(),
-        focusedPrefixColor: Color = FilledTextFieldTokens.InputPrefixColor.toColor(),
-        unfocusedPrefixColor: Color = FilledTextFieldTokens.InputPrefixColor.toColor(),
-        disabledPrefixColor: Color = FilledTextFieldTokens.InputPrefixColor.toColor()
+        errorSupportingTextColor: Color = FilledTextFieldTokens.ErrorSupportingColor.value,
+        focusedPrefixColor: Color = FilledTextFieldTokens.InputPrefixColor.value,
+        unfocusedPrefixColor: Color = FilledTextFieldTokens.InputPrefixColor.value,
+        disabledPrefixColor: Color = FilledTextFieldTokens.InputPrefixColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledInputOpacity),
-        errorPrefixColor: Color = FilledTextFieldTokens.InputPrefixColor.toColor(),
-        focusedSuffixColor: Color = FilledTextFieldTokens.InputSuffixColor.toColor(),
-        unfocusedSuffixColor: Color = FilledTextFieldTokens.InputSuffixColor.toColor(),
-        disabledSuffixColor: Color = FilledTextFieldTokens.InputSuffixColor.toColor()
+        errorPrefixColor: Color = FilledTextFieldTokens.InputPrefixColor.value,
+        focusedSuffixColor: Color = FilledTextFieldTokens.InputSuffixColor.value,
+        unfocusedSuffixColor: Color = FilledTextFieldTokens.InputSuffixColor.value,
+        disabledSuffixColor: Color = FilledTextFieldTokens.InputSuffixColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledInputOpacity),
-        errorSuffixColor: Color = FilledTextFieldTokens.InputSuffixColor.toColor(),
+        errorSuffixColor: Color = FilledTextFieldTokens.InputSuffixColor.value,
     ): TextFieldColors =
         TextFieldColors(
             focusedTextColor = focusedTextColor,
@@ -536,7 +536,7 @@
         isError: Boolean,
         interactionSource: InteractionSource,
         colors: TextFieldColors,
-        shape: Shape = OutlinedTextFieldTokens.ContainerShape.toShape(),
+        shape: Shape = OutlinedTextFieldTokens.ContainerShape.value,
         focusedBorderThickness: Dp = OutlinedTextFieldDefaults.FocusedBorderThickness,
         unfocusedBorderThickness: Dp = OutlinedTextFieldDefaults.UnfocusedBorderThickness
     ) = OutlinedTextFieldDefaults.ContainerBox(
@@ -669,56 +669,56 @@
     @ExperimentalMaterial3Api
     @Composable
     fun textFieldColors(
-        focusedTextColor: Color = FilledTextFieldTokens.FocusInputColor.toColor(),
-        unfocusedTextColor: Color = FilledTextFieldTokens.InputColor.toColor(),
-        disabledTextColor: Color = FilledTextFieldTokens.DisabledInputColor.toColor()
+        focusedTextColor: Color = FilledTextFieldTokens.FocusInputColor.value,
+        unfocusedTextColor: Color = FilledTextFieldTokens.InputColor.value,
+        disabledTextColor: Color = FilledTextFieldTokens.DisabledInputColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledInputOpacity),
-        errorTextColor: Color = FilledTextFieldTokens.ErrorInputColor.toColor(),
-        containerColor: Color = FilledTextFieldTokens.ContainerColor.toColor(),
-        errorContainerColor: Color = FilledTextFieldTokens.ContainerColor.toColor(),
-        cursorColor: Color = FilledTextFieldTokens.CaretColor.toColor(),
-        errorCursorColor: Color = FilledTextFieldTokens.ErrorFocusCaretColor.toColor(),
+        errorTextColor: Color = FilledTextFieldTokens.ErrorInputColor.value,
+        containerColor: Color = FilledTextFieldTokens.ContainerColor.value,
+        errorContainerColor: Color = FilledTextFieldTokens.ContainerColor.value,
+        cursorColor: Color = FilledTextFieldTokens.CaretColor.value,
+        errorCursorColor: Color = FilledTextFieldTokens.ErrorFocusCaretColor.value,
         selectionColors: TextSelectionColors = LocalTextSelectionColors.current,
-        focusedIndicatorColor: Color = FilledTextFieldTokens.FocusActiveIndicatorColor.toColor(),
-        unfocusedIndicatorColor: Color = FilledTextFieldTokens.ActiveIndicatorColor.toColor(),
-        disabledIndicatorColor: Color = FilledTextFieldTokens.DisabledActiveIndicatorColor.toColor()
+        focusedIndicatorColor: Color = FilledTextFieldTokens.FocusActiveIndicatorColor.value,
+        unfocusedIndicatorColor: Color = FilledTextFieldTokens.ActiveIndicatorColor.value,
+        disabledIndicatorColor: Color = FilledTextFieldTokens.DisabledActiveIndicatorColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledActiveIndicatorOpacity),
-        errorIndicatorColor: Color = FilledTextFieldTokens.ErrorActiveIndicatorColor.toColor(),
-        focusedLeadingIconColor: Color = FilledTextFieldTokens.FocusLeadingIconColor.toColor(),
-        unfocusedLeadingIconColor: Color = FilledTextFieldTokens.LeadingIconColor.toColor(),
-        disabledLeadingIconColor: Color = FilledTextFieldTokens.DisabledLeadingIconColor.toColor()
+        errorIndicatorColor: Color = FilledTextFieldTokens.ErrorActiveIndicatorColor.value,
+        focusedLeadingIconColor: Color = FilledTextFieldTokens.FocusLeadingIconColor.value,
+        unfocusedLeadingIconColor: Color = FilledTextFieldTokens.LeadingIconColor.value,
+        disabledLeadingIconColor: Color = FilledTextFieldTokens.DisabledLeadingIconColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledLeadingIconOpacity),
-        errorLeadingIconColor: Color = FilledTextFieldTokens.ErrorLeadingIconColor.toColor(),
-        focusedTrailingIconColor: Color = FilledTextFieldTokens.FocusTrailingIconColor.toColor(),
-        unfocusedTrailingIconColor: Color = FilledTextFieldTokens.TrailingIconColor.toColor(),
-        disabledTrailingIconColor: Color = FilledTextFieldTokens.DisabledTrailingIconColor.toColor()
+        errorLeadingIconColor: Color = FilledTextFieldTokens.ErrorLeadingIconColor.value,
+        focusedTrailingIconColor: Color = FilledTextFieldTokens.FocusTrailingIconColor.value,
+        unfocusedTrailingIconColor: Color = FilledTextFieldTokens.TrailingIconColor.value,
+        disabledTrailingIconColor: Color = FilledTextFieldTokens.DisabledTrailingIconColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledTrailingIconOpacity),
-        errorTrailingIconColor: Color = FilledTextFieldTokens.ErrorTrailingIconColor.toColor(),
-        focusedLabelColor: Color = FilledTextFieldTokens.FocusLabelColor.toColor(),
-        unfocusedLabelColor: Color = FilledTextFieldTokens.LabelColor.toColor(),
-        disabledLabelColor: Color = FilledTextFieldTokens.DisabledLabelColor.toColor()
+        errorTrailingIconColor: Color = FilledTextFieldTokens.ErrorTrailingIconColor.value,
+        focusedLabelColor: Color = FilledTextFieldTokens.FocusLabelColor.value,
+        unfocusedLabelColor: Color = FilledTextFieldTokens.LabelColor.value,
+        disabledLabelColor: Color = FilledTextFieldTokens.DisabledLabelColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledLabelOpacity),
-        errorLabelColor: Color = FilledTextFieldTokens.ErrorLabelColor.toColor(),
-        focusedPlaceholderColor: Color = FilledTextFieldTokens.InputPlaceholderColor.toColor(),
-        unfocusedPlaceholderColor: Color = FilledTextFieldTokens.InputPlaceholderColor.toColor(),
-        disabledPlaceholderColor: Color = FilledTextFieldTokens.DisabledInputColor.toColor()
+        errorLabelColor: Color = FilledTextFieldTokens.ErrorLabelColor.value,
+        focusedPlaceholderColor: Color = FilledTextFieldTokens.InputPlaceholderColor.value,
+        unfocusedPlaceholderColor: Color = FilledTextFieldTokens.InputPlaceholderColor.value,
+        disabledPlaceholderColor: Color = FilledTextFieldTokens.DisabledInputColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledInputOpacity),
-        errorPlaceholderColor: Color = FilledTextFieldTokens.InputPlaceholderColor.toColor(),
-        focusedSupportingTextColor: Color = FilledTextFieldTokens.FocusSupportingColor.toColor(),
-        unfocusedSupportingTextColor: Color = FilledTextFieldTokens.SupportingColor.toColor(),
-        disabledSupportingTextColor: Color = FilledTextFieldTokens.DisabledSupportingColor.toColor()
+        errorPlaceholderColor: Color = FilledTextFieldTokens.InputPlaceholderColor.value,
+        focusedSupportingTextColor: Color = FilledTextFieldTokens.FocusSupportingColor.value,
+        unfocusedSupportingTextColor: Color = FilledTextFieldTokens.SupportingColor.value,
+        disabledSupportingTextColor: Color = FilledTextFieldTokens.DisabledSupportingColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledSupportingOpacity),
-        errorSupportingTextColor: Color = FilledTextFieldTokens.ErrorSupportingColor.toColor(),
-        focusedPrefixColor: Color = FilledTextFieldTokens.InputPrefixColor.toColor(),
-        unfocusedPrefixColor: Color = FilledTextFieldTokens.InputPrefixColor.toColor(),
-        disabledPrefixColor: Color = FilledTextFieldTokens.InputPrefixColor.toColor()
+        errorSupportingTextColor: Color = FilledTextFieldTokens.ErrorSupportingColor.value,
+        focusedPrefixColor: Color = FilledTextFieldTokens.InputPrefixColor.value,
+        unfocusedPrefixColor: Color = FilledTextFieldTokens.InputPrefixColor.value,
+        disabledPrefixColor: Color = FilledTextFieldTokens.InputPrefixColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledInputOpacity),
-        errorPrefixColor: Color = FilledTextFieldTokens.InputPrefixColor.toColor(),
-        focusedSuffixColor: Color = FilledTextFieldTokens.InputSuffixColor.toColor(),
-        unfocusedSuffixColor: Color = FilledTextFieldTokens.InputSuffixColor.toColor(),
-        disabledSuffixColor: Color = FilledTextFieldTokens.InputSuffixColor.toColor()
+        errorPrefixColor: Color = FilledTextFieldTokens.InputPrefixColor.value,
+        focusedSuffixColor: Color = FilledTextFieldTokens.InputSuffixColor.value,
+        unfocusedSuffixColor: Color = FilledTextFieldTokens.InputSuffixColor.value,
+        disabledSuffixColor: Color = FilledTextFieldTokens.InputSuffixColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledInputOpacity),
-        errorSuffixColor: Color = FilledTextFieldTokens.InputSuffixColor.toColor(),
+        errorSuffixColor: Color = FilledTextFieldTokens.InputSuffixColor.value,
     ): TextFieldColors = colors(
         focusedTextColor = focusedTextColor,
         unfocusedTextColor = unfocusedTextColor,
@@ -819,56 +819,56 @@
     @ExperimentalMaterial3Api
     @Composable
     fun outlinedTextFieldColors(
-        focusedTextColor: Color = OutlinedTextFieldTokens.FocusInputColor.toColor(),
-        unfocusedTextColor: Color = OutlinedTextFieldTokens.InputColor.toColor(),
-        disabledTextColor: Color = OutlinedTextFieldTokens.DisabledInputColor.toColor()
+        focusedTextColor: Color = OutlinedTextFieldTokens.FocusInputColor.value,
+        unfocusedTextColor: Color = OutlinedTextFieldTokens.InputColor.value,
+        disabledTextColor: Color = OutlinedTextFieldTokens.DisabledInputColor.value
             .copy(alpha = OutlinedTextFieldTokens.DisabledInputOpacity),
-        errorTextColor: Color = OutlinedTextFieldTokens.ErrorInputColor.toColor(),
+        errorTextColor: Color = OutlinedTextFieldTokens.ErrorInputColor.value,
         containerColor: Color = Color.Transparent,
         errorContainerColor: Color = Color.Transparent,
-        cursorColor: Color = OutlinedTextFieldTokens.CaretColor.toColor(),
-        errorCursorColor: Color = OutlinedTextFieldTokens.ErrorFocusCaretColor.toColor(),
+        cursorColor: Color = OutlinedTextFieldTokens.CaretColor.value,
+        errorCursorColor: Color = OutlinedTextFieldTokens.ErrorFocusCaretColor.value,
         selectionColors: TextSelectionColors = LocalTextSelectionColors.current,
-        focusedBorderColor: Color = OutlinedTextFieldTokens.FocusOutlineColor.toColor(),
-        unfocusedBorderColor: Color = OutlinedTextFieldTokens.OutlineColor.toColor(),
-        disabledBorderColor: Color = OutlinedTextFieldTokens.DisabledOutlineColor.toColor()
+        focusedBorderColor: Color = OutlinedTextFieldTokens.FocusOutlineColor.value,
+        unfocusedBorderColor: Color = OutlinedTextFieldTokens.OutlineColor.value,
+        disabledBorderColor: Color = OutlinedTextFieldTokens.DisabledOutlineColor.value
             .copy(alpha = OutlinedTextFieldTokens.DisabledOutlineOpacity),
-        errorBorderColor: Color = OutlinedTextFieldTokens.ErrorOutlineColor.toColor(),
-        focusedLeadingIconColor: Color = OutlinedTextFieldTokens.FocusLeadingIconColor.toColor(),
-        unfocusedLeadingIconColor: Color = OutlinedTextFieldTokens.LeadingIconColor.toColor(),
-        disabledLeadingIconColor: Color = OutlinedTextFieldTokens.DisabledLeadingIconColor.toColor()
+        errorBorderColor: Color = OutlinedTextFieldTokens.ErrorOutlineColor.value,
+        focusedLeadingIconColor: Color = OutlinedTextFieldTokens.FocusLeadingIconColor.value,
+        unfocusedLeadingIconColor: Color = OutlinedTextFieldTokens.LeadingIconColor.value,
+        disabledLeadingIconColor: Color = OutlinedTextFieldTokens.DisabledLeadingIconColor.value
             .copy(alpha = OutlinedTextFieldTokens.DisabledLeadingIconOpacity),
-        errorLeadingIconColor: Color = OutlinedTextFieldTokens.ErrorLeadingIconColor.toColor(),
-        focusedTrailingIconColor: Color = OutlinedTextFieldTokens.FocusTrailingIconColor.toColor(),
-        unfocusedTrailingIconColor: Color = OutlinedTextFieldTokens.TrailingIconColor.toColor(),
+        errorLeadingIconColor: Color = OutlinedTextFieldTokens.ErrorLeadingIconColor.value,
+        focusedTrailingIconColor: Color = OutlinedTextFieldTokens.FocusTrailingIconColor.value,
+        unfocusedTrailingIconColor: Color = OutlinedTextFieldTokens.TrailingIconColor.value,
         disabledTrailingIconColor: Color = OutlinedTextFieldTokens.DisabledTrailingIconColor
-            .toColor().copy(alpha = OutlinedTextFieldTokens.DisabledTrailingIconOpacity),
-        errorTrailingIconColor: Color = OutlinedTextFieldTokens.ErrorTrailingIconColor.toColor(),
-        focusedLabelColor: Color = OutlinedTextFieldTokens.FocusLabelColor.toColor(),
-        unfocusedLabelColor: Color = OutlinedTextFieldTokens.LabelColor.toColor(),
-        disabledLabelColor: Color = OutlinedTextFieldTokens.DisabledLabelColor.toColor()
+            .value.copy(alpha = OutlinedTextFieldTokens.DisabledTrailingIconOpacity),
+        errorTrailingIconColor: Color = OutlinedTextFieldTokens.ErrorTrailingIconColor.value,
+        focusedLabelColor: Color = OutlinedTextFieldTokens.FocusLabelColor.value,
+        unfocusedLabelColor: Color = OutlinedTextFieldTokens.LabelColor.value,
+        disabledLabelColor: Color = OutlinedTextFieldTokens.DisabledLabelColor.value
             .copy(alpha = OutlinedTextFieldTokens.DisabledLabelOpacity),
-        errorLabelColor: Color = OutlinedTextFieldTokens.ErrorLabelColor.toColor(),
-        focusedPlaceholderColor: Color = OutlinedTextFieldTokens.InputPlaceholderColor.toColor(),
-        unfocusedPlaceholderColor: Color = OutlinedTextFieldTokens.InputPlaceholderColor.toColor(),
-        disabledPlaceholderColor: Color = OutlinedTextFieldTokens.DisabledInputColor.toColor()
+        errorLabelColor: Color = OutlinedTextFieldTokens.ErrorLabelColor.value,
+        focusedPlaceholderColor: Color = OutlinedTextFieldTokens.InputPlaceholderColor.value,
+        unfocusedPlaceholderColor: Color = OutlinedTextFieldTokens.InputPlaceholderColor.value,
+        disabledPlaceholderColor: Color = OutlinedTextFieldTokens.DisabledInputColor.value
             .copy(alpha = OutlinedTextFieldTokens.DisabledInputOpacity),
-        errorPlaceholderColor: Color = OutlinedTextFieldTokens.InputPlaceholderColor.toColor(),
-        focusedSupportingTextColor: Color = OutlinedTextFieldTokens.FocusSupportingColor.toColor(),
-        unfocusedSupportingTextColor: Color = OutlinedTextFieldTokens.SupportingColor.toColor(),
+        errorPlaceholderColor: Color = OutlinedTextFieldTokens.InputPlaceholderColor.value,
+        focusedSupportingTextColor: Color = OutlinedTextFieldTokens.FocusSupportingColor.value,
+        unfocusedSupportingTextColor: Color = OutlinedTextFieldTokens.SupportingColor.value,
         disabledSupportingTextColor: Color = OutlinedTextFieldTokens.DisabledSupportingColor
-            .toColor().copy(alpha = OutlinedTextFieldTokens.DisabledSupportingOpacity),
-        errorSupportingTextColor: Color = OutlinedTextFieldTokens.ErrorSupportingColor.toColor(),
-        focusedPrefixColor: Color = OutlinedTextFieldTokens.InputPrefixColor.toColor(),
-        unfocusedPrefixColor: Color = OutlinedTextFieldTokens.InputPrefixColor.toColor(),
-        disabledPrefixColor: Color = OutlinedTextFieldTokens.InputPrefixColor.toColor()
+            .value.copy(alpha = OutlinedTextFieldTokens.DisabledSupportingOpacity),
+        errorSupportingTextColor: Color = OutlinedTextFieldTokens.ErrorSupportingColor.value,
+        focusedPrefixColor: Color = OutlinedTextFieldTokens.InputPrefixColor.value,
+        unfocusedPrefixColor: Color = OutlinedTextFieldTokens.InputPrefixColor.value,
+        disabledPrefixColor: Color = OutlinedTextFieldTokens.InputPrefixColor.value
             .copy(alpha = OutlinedTextFieldTokens.DisabledInputOpacity),
-        errorPrefixColor: Color = OutlinedTextFieldTokens.InputPrefixColor.toColor(),
-        focusedSuffixColor: Color = OutlinedTextFieldTokens.InputSuffixColor.toColor(),
-        unfocusedSuffixColor: Color = OutlinedTextFieldTokens.InputSuffixColor.toColor(),
-        disabledSuffixColor: Color = OutlinedTextFieldTokens.InputSuffixColor.toColor()
+        errorPrefixColor: Color = OutlinedTextFieldTokens.InputPrefixColor.value,
+        focusedSuffixColor: Color = OutlinedTextFieldTokens.InputSuffixColor.value,
+        unfocusedSuffixColor: Color = OutlinedTextFieldTokens.InputSuffixColor.value,
+        disabledSuffixColor: Color = OutlinedTextFieldTokens.InputSuffixColor.value
             .copy(alpha = OutlinedTextFieldTokens.DisabledInputOpacity),
-        errorSuffixColor: Color = OutlinedTextFieldTokens.InputSuffixColor.toColor(),
+        errorSuffixColor: Color = OutlinedTextFieldTokens.InputSuffixColor.value,
     ): TextFieldColors = OutlinedTextFieldDefaults.colors(
         focusedTextColor = focusedTextColor,
         unfocusedTextColor = unfocusedTextColor,
@@ -1058,51 +1058,51 @@
     @ExperimentalMaterial3Api
     @Composable
     fun textFieldColors(
-        textColor: Color = FilledTextFieldTokens.InputColor.toColor(),
-        disabledTextColor: Color = FilledTextFieldTokens.DisabledInputColor.toColor()
+        textColor: Color = FilledTextFieldTokens.InputColor.value,
+        disabledTextColor: Color = FilledTextFieldTokens.DisabledInputColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledInputOpacity),
-        containerColor: Color = FilledTextFieldTokens.ContainerColor.toColor(),
-        cursorColor: Color = FilledTextFieldTokens.CaretColor.toColor(),
-        errorCursorColor: Color = FilledTextFieldTokens.ErrorFocusCaretColor.toColor(),
+        containerColor: Color = FilledTextFieldTokens.ContainerColor.value,
+        cursorColor: Color = FilledTextFieldTokens.CaretColor.value,
+        errorCursorColor: Color = FilledTextFieldTokens.ErrorFocusCaretColor.value,
         selectionColors: TextSelectionColors = LocalTextSelectionColors.current,
-        focusedIndicatorColor: Color = FilledTextFieldTokens.FocusActiveIndicatorColor.toColor(),
-        unfocusedIndicatorColor: Color = FilledTextFieldTokens.ActiveIndicatorColor.toColor(),
-        disabledIndicatorColor: Color = FilledTextFieldTokens.DisabledActiveIndicatorColor.toColor()
+        focusedIndicatorColor: Color = FilledTextFieldTokens.FocusActiveIndicatorColor.value,
+        unfocusedIndicatorColor: Color = FilledTextFieldTokens.ActiveIndicatorColor.value,
+        disabledIndicatorColor: Color = FilledTextFieldTokens.DisabledActiveIndicatorColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledActiveIndicatorOpacity),
-        errorIndicatorColor: Color = FilledTextFieldTokens.ErrorActiveIndicatorColor.toColor(),
-        focusedLeadingIconColor: Color = FilledTextFieldTokens.FocusLeadingIconColor.toColor(),
-        unfocusedLeadingIconColor: Color = FilledTextFieldTokens.LeadingIconColor.toColor(),
-        disabledLeadingIconColor: Color = FilledTextFieldTokens.DisabledLeadingIconColor.toColor()
+        errorIndicatorColor: Color = FilledTextFieldTokens.ErrorActiveIndicatorColor.value,
+        focusedLeadingIconColor: Color = FilledTextFieldTokens.FocusLeadingIconColor.value,
+        unfocusedLeadingIconColor: Color = FilledTextFieldTokens.LeadingIconColor.value,
+        disabledLeadingIconColor: Color = FilledTextFieldTokens.DisabledLeadingIconColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledLeadingIconOpacity),
-        errorLeadingIconColor: Color = FilledTextFieldTokens.ErrorLeadingIconColor.toColor(),
-        focusedTrailingIconColor: Color = FilledTextFieldTokens.FocusTrailingIconColor.toColor(),
-        unfocusedTrailingIconColor: Color = FilledTextFieldTokens.TrailingIconColor.toColor(),
-        disabledTrailingIconColor: Color = FilledTextFieldTokens.DisabledTrailingIconColor.toColor()
+        errorLeadingIconColor: Color = FilledTextFieldTokens.ErrorLeadingIconColor.value,
+        focusedTrailingIconColor: Color = FilledTextFieldTokens.FocusTrailingIconColor.value,
+        unfocusedTrailingIconColor: Color = FilledTextFieldTokens.TrailingIconColor.value,
+        disabledTrailingIconColor: Color = FilledTextFieldTokens.DisabledTrailingIconColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledTrailingIconOpacity),
-        errorTrailingIconColor: Color = FilledTextFieldTokens.ErrorTrailingIconColor.toColor(),
-        focusedLabelColor: Color = FilledTextFieldTokens.FocusLabelColor.toColor(),
-        unfocusedLabelColor: Color = FilledTextFieldTokens.LabelColor.toColor(),
-        disabledLabelColor: Color = FilledTextFieldTokens.DisabledLabelColor.toColor()
+        errorTrailingIconColor: Color = FilledTextFieldTokens.ErrorTrailingIconColor.value,
+        focusedLabelColor: Color = FilledTextFieldTokens.FocusLabelColor.value,
+        unfocusedLabelColor: Color = FilledTextFieldTokens.LabelColor.value,
+        disabledLabelColor: Color = FilledTextFieldTokens.DisabledLabelColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledLabelOpacity),
-        errorLabelColor: Color = FilledTextFieldTokens.ErrorLabelColor.toColor(),
-        placeholderColor: Color = FilledTextFieldTokens.InputPlaceholderColor.toColor(),
-        disabledPlaceholderColor: Color = FilledTextFieldTokens.DisabledInputColor.toColor()
+        errorLabelColor: Color = FilledTextFieldTokens.ErrorLabelColor.value,
+        placeholderColor: Color = FilledTextFieldTokens.InputPlaceholderColor.value,
+        disabledPlaceholderColor: Color = FilledTextFieldTokens.DisabledInputColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledInputOpacity),
-        focusedSupportingTextColor: Color = FilledTextFieldTokens.FocusSupportingColor.toColor(),
-        unfocusedSupportingTextColor: Color = FilledTextFieldTokens.SupportingColor.toColor(),
-        disabledSupportingTextColor: Color = FilledTextFieldTokens.DisabledSupportingColor.toColor()
+        focusedSupportingTextColor: Color = FilledTextFieldTokens.FocusSupportingColor.value,
+        unfocusedSupportingTextColor: Color = FilledTextFieldTokens.SupportingColor.value,
+        disabledSupportingTextColor: Color = FilledTextFieldTokens.DisabledSupportingColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledSupportingOpacity),
-        errorSupportingTextColor: Color = FilledTextFieldTokens.ErrorSupportingColor.toColor(),
-        focusedPrefixColor: Color = FilledTextFieldTokens.InputPrefixColor.toColor(),
-        unfocusedPrefixColor: Color = FilledTextFieldTokens.InputPrefixColor.toColor(),
-        disabledPrefixColor: Color = FilledTextFieldTokens.InputPrefixColor.toColor()
+        errorSupportingTextColor: Color = FilledTextFieldTokens.ErrorSupportingColor.value,
+        focusedPrefixColor: Color = FilledTextFieldTokens.InputPrefixColor.value,
+        unfocusedPrefixColor: Color = FilledTextFieldTokens.InputPrefixColor.value,
+        disabledPrefixColor: Color = FilledTextFieldTokens.InputPrefixColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledInputOpacity),
-        errorPrefixColor: Color = FilledTextFieldTokens.InputPrefixColor.toColor(),
-        focusedSuffixColor: Color = FilledTextFieldTokens.InputSuffixColor.toColor(),
-        unfocusedSuffixColor: Color = FilledTextFieldTokens.InputSuffixColor.toColor(),
-        disabledSuffixColor: Color = FilledTextFieldTokens.InputSuffixColor.toColor()
+        errorPrefixColor: Color = FilledTextFieldTokens.InputPrefixColor.value,
+        focusedSuffixColor: Color = FilledTextFieldTokens.InputSuffixColor.value,
+        unfocusedSuffixColor: Color = FilledTextFieldTokens.InputSuffixColor.value,
+        disabledSuffixColor: Color = FilledTextFieldTokens.InputSuffixColor.value
             .copy(alpha = FilledTextFieldTokens.DisabledInputOpacity),
-        errorSuffixColor: Color = FilledTextFieldTokens.InputSuffixColor.toColor(),
+        errorSuffixColor: Color = FilledTextFieldTokens.InputSuffixColor.value,
     ): TextFieldColors = colors(
         focusedTextColor = textColor,
         unfocusedTextColor = textColor,
@@ -1153,51 +1153,51 @@
     @ExperimentalMaterial3Api
     @Composable
     fun outlinedTextFieldColors(
-        textColor: Color = OutlinedTextFieldTokens.InputColor.toColor(),
-        disabledTextColor: Color = OutlinedTextFieldTokens.DisabledInputColor.toColor()
+        textColor: Color = OutlinedTextFieldTokens.InputColor.value,
+        disabledTextColor: Color = OutlinedTextFieldTokens.DisabledInputColor.value
             .copy(alpha = OutlinedTextFieldTokens.DisabledInputOpacity),
         containerColor: Color = Color.Transparent,
-        cursorColor: Color = OutlinedTextFieldTokens.CaretColor.toColor(),
-        errorCursorColor: Color = OutlinedTextFieldTokens.ErrorFocusCaretColor.toColor(),
+        cursorColor: Color = OutlinedTextFieldTokens.CaretColor.value,
+        errorCursorColor: Color = OutlinedTextFieldTokens.ErrorFocusCaretColor.value,
         selectionColors: TextSelectionColors = LocalTextSelectionColors.current,
-        focusedBorderColor: Color = OutlinedTextFieldTokens.FocusOutlineColor.toColor(),
-        unfocusedBorderColor: Color = OutlinedTextFieldTokens.OutlineColor.toColor(),
-        disabledBorderColor: Color = OutlinedTextFieldTokens.DisabledOutlineColor.toColor()
+        focusedBorderColor: Color = OutlinedTextFieldTokens.FocusOutlineColor.value,
+        unfocusedBorderColor: Color = OutlinedTextFieldTokens.OutlineColor.value,
+        disabledBorderColor: Color = OutlinedTextFieldTokens.DisabledOutlineColor.value
             .copy(alpha = OutlinedTextFieldTokens.DisabledOutlineOpacity),
-        errorBorderColor: Color = OutlinedTextFieldTokens.ErrorOutlineColor.toColor(),
-        focusedLeadingIconColor: Color = OutlinedTextFieldTokens.FocusLeadingIconColor.toColor(),
-        unfocusedLeadingIconColor: Color = OutlinedTextFieldTokens.LeadingIconColor.toColor(),
-        disabledLeadingIconColor: Color = OutlinedTextFieldTokens.DisabledLeadingIconColor.toColor()
+        errorBorderColor: Color = OutlinedTextFieldTokens.ErrorOutlineColor.value,
+        focusedLeadingIconColor: Color = OutlinedTextFieldTokens.FocusLeadingIconColor.value,
+        unfocusedLeadingIconColor: Color = OutlinedTextFieldTokens.LeadingIconColor.value,
+        disabledLeadingIconColor: Color = OutlinedTextFieldTokens.DisabledLeadingIconColor.value
             .copy(alpha = OutlinedTextFieldTokens.DisabledLeadingIconOpacity),
-        errorLeadingIconColor: Color = OutlinedTextFieldTokens.ErrorLeadingIconColor.toColor(),
-        focusedTrailingIconColor: Color = OutlinedTextFieldTokens.FocusTrailingIconColor.toColor(),
-        unfocusedTrailingIconColor: Color = OutlinedTextFieldTokens.TrailingIconColor.toColor(),
+        errorLeadingIconColor: Color = OutlinedTextFieldTokens.ErrorLeadingIconColor.value,
+        focusedTrailingIconColor: Color = OutlinedTextFieldTokens.FocusTrailingIconColor.value,
+        unfocusedTrailingIconColor: Color = OutlinedTextFieldTokens.TrailingIconColor.value,
         disabledTrailingIconColor: Color = OutlinedTextFieldTokens.DisabledTrailingIconColor
-            .toColor().copy(alpha = OutlinedTextFieldTokens.DisabledTrailingIconOpacity),
-        errorTrailingIconColor: Color = OutlinedTextFieldTokens.ErrorTrailingIconColor.toColor(),
-        focusedLabelColor: Color = OutlinedTextFieldTokens.FocusLabelColor.toColor(),
-        unfocusedLabelColor: Color = OutlinedTextFieldTokens.LabelColor.toColor(),
-        disabledLabelColor: Color = OutlinedTextFieldTokens.DisabledLabelColor.toColor()
+            .value.copy(alpha = OutlinedTextFieldTokens.DisabledTrailingIconOpacity),
+        errorTrailingIconColor: Color = OutlinedTextFieldTokens.ErrorTrailingIconColor.value,
+        focusedLabelColor: Color = OutlinedTextFieldTokens.FocusLabelColor.value,
+        unfocusedLabelColor: Color = OutlinedTextFieldTokens.LabelColor.value,
+        disabledLabelColor: Color = OutlinedTextFieldTokens.DisabledLabelColor.value
             .copy(alpha = OutlinedTextFieldTokens.DisabledLabelOpacity),
-        errorLabelColor: Color = OutlinedTextFieldTokens.ErrorLabelColor.toColor(),
-        placeholderColor: Color = OutlinedTextFieldTokens.InputPlaceholderColor.toColor(),
-        disabledPlaceholderColor: Color = OutlinedTextFieldTokens.DisabledInputColor.toColor()
+        errorLabelColor: Color = OutlinedTextFieldTokens.ErrorLabelColor.value,
+        placeholderColor: Color = OutlinedTextFieldTokens.InputPlaceholderColor.value,
+        disabledPlaceholderColor: Color = OutlinedTextFieldTokens.DisabledInputColor.value
             .copy(alpha = OutlinedTextFieldTokens.DisabledInputOpacity),
-        focusedSupportingTextColor: Color = OutlinedTextFieldTokens.FocusSupportingColor.toColor(),
-        unfocusedSupportingTextColor: Color = OutlinedTextFieldTokens.SupportingColor.toColor(),
+        focusedSupportingTextColor: Color = OutlinedTextFieldTokens.FocusSupportingColor.value,
+        unfocusedSupportingTextColor: Color = OutlinedTextFieldTokens.SupportingColor.value,
         disabledSupportingTextColor: Color = OutlinedTextFieldTokens.DisabledSupportingColor
-            .toColor().copy(alpha = OutlinedTextFieldTokens.DisabledSupportingOpacity),
-        errorSupportingTextColor: Color = OutlinedTextFieldTokens.ErrorSupportingColor.toColor(),
-        focusedPrefixColor: Color = OutlinedTextFieldTokens.InputPrefixColor.toColor(),
-        unfocusedPrefixColor: Color = OutlinedTextFieldTokens.InputPrefixColor.toColor(),
-        disabledPrefixColor: Color = OutlinedTextFieldTokens.InputPrefixColor.toColor()
+            .value.copy(alpha = OutlinedTextFieldTokens.DisabledSupportingOpacity),
+        errorSupportingTextColor: Color = OutlinedTextFieldTokens.ErrorSupportingColor.value,
+        focusedPrefixColor: Color = OutlinedTextFieldTokens.InputPrefixColor.value,
+        unfocusedPrefixColor: Color = OutlinedTextFieldTokens.InputPrefixColor.value,
+        disabledPrefixColor: Color = OutlinedTextFieldTokens.InputPrefixColor.value
             .copy(alpha = OutlinedTextFieldTokens.DisabledInputOpacity),
-        errorPrefixColor: Color = OutlinedTextFieldTokens.InputPrefixColor.toColor(),
-        focusedSuffixColor: Color = OutlinedTextFieldTokens.InputSuffixColor.toColor(),
-        unfocusedSuffixColor: Color = OutlinedTextFieldTokens.InputSuffixColor.toColor(),
-        disabledSuffixColor: Color = OutlinedTextFieldTokens.InputSuffixColor.toColor()
+        errorPrefixColor: Color = OutlinedTextFieldTokens.InputPrefixColor.value,
+        focusedSuffixColor: Color = OutlinedTextFieldTokens.InputSuffixColor.value,
+        unfocusedSuffixColor: Color = OutlinedTextFieldTokens.InputSuffixColor.value,
+        disabledSuffixColor: Color = OutlinedTextFieldTokens.InputSuffixColor.value
             .copy(alpha = OutlinedTextFieldTokens.DisabledInputOpacity),
-        errorSuffixColor: Color = OutlinedTextFieldTokens.InputSuffixColor.toColor(),
+        errorSuffixColor: Color = OutlinedTextFieldTokens.InputSuffixColor.value,
     ): TextFieldColors = OutlinedTextFieldDefaults.colors(
         focusedTextColor = textColor,
         unfocusedTextColor = textColor,
@@ -1345,7 +1345,7 @@
 @Immutable
 object OutlinedTextFieldDefaults {
     /** Default shape for an [OutlinedTextField]. */
-    val shape: Shape @Composable get() = OutlinedTextFieldTokens.ContainerShape.toShape()
+    val shape: Shape @Composable get() = OutlinedTextFieldTokens.ContainerShape.value
 
     /**
      * The default min width applied to an [OutlinedTextField].
@@ -1392,7 +1392,7 @@
         isError: Boolean,
         interactionSource: InteractionSource,
         colors: TextFieldColors,
-        shape: Shape = OutlinedTextFieldTokens.ContainerShape.toShape(),
+        shape: Shape = OutlinedTextFieldTokens.ContainerShape.value,
         focusedBorderThickness: Dp = FocusedBorderThickness,
         unfocusedBorderThickness: Dp = UnfocusedBorderThickness
     ) {
@@ -1477,58 +1477,58 @@
      */
     @Composable
     fun colors(
-        focusedTextColor: Color = OutlinedTextFieldTokens.FocusInputColor.toColor(),
-        unfocusedTextColor: Color = OutlinedTextFieldTokens.InputColor.toColor(),
-        disabledTextColor: Color = OutlinedTextFieldTokens.DisabledInputColor.toColor()
+        focusedTextColor: Color = OutlinedTextFieldTokens.FocusInputColor.value,
+        unfocusedTextColor: Color = OutlinedTextFieldTokens.InputColor.value,
+        disabledTextColor: Color = OutlinedTextFieldTokens.DisabledInputColor.value
             .copy(alpha = OutlinedTextFieldTokens.DisabledInputOpacity),
-        errorTextColor: Color = OutlinedTextFieldTokens.ErrorInputColor.toColor(),
+        errorTextColor: Color = OutlinedTextFieldTokens.ErrorInputColor.value,
         focusedContainerColor: Color = Color.Transparent,
         unfocusedContainerColor: Color = Color.Transparent,
         disabledContainerColor: Color = Color.Transparent,
         errorContainerColor: Color = Color.Transparent,
-        cursorColor: Color = OutlinedTextFieldTokens.CaretColor.toColor(),
-        errorCursorColor: Color = OutlinedTextFieldTokens.ErrorFocusCaretColor.toColor(),
+        cursorColor: Color = OutlinedTextFieldTokens.CaretColor.value,
+        errorCursorColor: Color = OutlinedTextFieldTokens.ErrorFocusCaretColor.value,
         selectionColors: TextSelectionColors = LocalTextSelectionColors.current,
-        focusedBorderColor: Color = OutlinedTextFieldTokens.FocusOutlineColor.toColor(),
-        unfocusedBorderColor: Color = OutlinedTextFieldTokens.OutlineColor.toColor(),
-        disabledBorderColor: Color = OutlinedTextFieldTokens.DisabledOutlineColor.toColor()
+        focusedBorderColor: Color = OutlinedTextFieldTokens.FocusOutlineColor.value,
+        unfocusedBorderColor: Color = OutlinedTextFieldTokens.OutlineColor.value,
+        disabledBorderColor: Color = OutlinedTextFieldTokens.DisabledOutlineColor.value
             .copy(alpha = OutlinedTextFieldTokens.DisabledOutlineOpacity),
-        errorBorderColor: Color = OutlinedTextFieldTokens.ErrorOutlineColor.toColor(),
-        focusedLeadingIconColor: Color = OutlinedTextFieldTokens.FocusLeadingIconColor.toColor(),
-        unfocusedLeadingIconColor: Color = OutlinedTextFieldTokens.LeadingIconColor.toColor(),
-        disabledLeadingIconColor: Color = OutlinedTextFieldTokens.DisabledLeadingIconColor.toColor()
+        errorBorderColor: Color = OutlinedTextFieldTokens.ErrorOutlineColor.value,
+        focusedLeadingIconColor: Color = OutlinedTextFieldTokens.FocusLeadingIconColor.value,
+        unfocusedLeadingIconColor: Color = OutlinedTextFieldTokens.LeadingIconColor.value,
+        disabledLeadingIconColor: Color = OutlinedTextFieldTokens.DisabledLeadingIconColor.value
             .copy(alpha = OutlinedTextFieldTokens.DisabledLeadingIconOpacity),
-        errorLeadingIconColor: Color = OutlinedTextFieldTokens.ErrorLeadingIconColor.toColor(),
-        focusedTrailingIconColor: Color = OutlinedTextFieldTokens.FocusTrailingIconColor.toColor(),
-        unfocusedTrailingIconColor: Color = OutlinedTextFieldTokens.TrailingIconColor.toColor(),
+        errorLeadingIconColor: Color = OutlinedTextFieldTokens.ErrorLeadingIconColor.value,
+        focusedTrailingIconColor: Color = OutlinedTextFieldTokens.FocusTrailingIconColor.value,
+        unfocusedTrailingIconColor: Color = OutlinedTextFieldTokens.TrailingIconColor.value,
         disabledTrailingIconColor: Color = OutlinedTextFieldTokens.DisabledTrailingIconColor
-            .toColor().copy(alpha = OutlinedTextFieldTokens.DisabledTrailingIconOpacity),
-        errorTrailingIconColor: Color = OutlinedTextFieldTokens.ErrorTrailingIconColor.toColor(),
-        focusedLabelColor: Color = OutlinedTextFieldTokens.FocusLabelColor.toColor(),
-        unfocusedLabelColor: Color = OutlinedTextFieldTokens.LabelColor.toColor(),
-        disabledLabelColor: Color = OutlinedTextFieldTokens.DisabledLabelColor.toColor()
+            .value.copy(alpha = OutlinedTextFieldTokens.DisabledTrailingIconOpacity),
+        errorTrailingIconColor: Color = OutlinedTextFieldTokens.ErrorTrailingIconColor.value,
+        focusedLabelColor: Color = OutlinedTextFieldTokens.FocusLabelColor.value,
+        unfocusedLabelColor: Color = OutlinedTextFieldTokens.LabelColor.value,
+        disabledLabelColor: Color = OutlinedTextFieldTokens.DisabledLabelColor.value
             .copy(alpha = OutlinedTextFieldTokens.DisabledLabelOpacity),
-        errorLabelColor: Color = OutlinedTextFieldTokens.ErrorLabelColor.toColor(),
-        focusedPlaceholderColor: Color = OutlinedTextFieldTokens.InputPlaceholderColor.toColor(),
-        unfocusedPlaceholderColor: Color = OutlinedTextFieldTokens.InputPlaceholderColor.toColor(),
-        disabledPlaceholderColor: Color = OutlinedTextFieldTokens.DisabledInputColor.toColor()
+        errorLabelColor: Color = OutlinedTextFieldTokens.ErrorLabelColor.value,
+        focusedPlaceholderColor: Color = OutlinedTextFieldTokens.InputPlaceholderColor.value,
+        unfocusedPlaceholderColor: Color = OutlinedTextFieldTokens.InputPlaceholderColor.value,
+        disabledPlaceholderColor: Color = OutlinedTextFieldTokens.DisabledInputColor.value
             .copy(alpha = OutlinedTextFieldTokens.DisabledInputOpacity),
-        errorPlaceholderColor: Color = OutlinedTextFieldTokens.InputPlaceholderColor.toColor(),
-        focusedSupportingTextColor: Color = OutlinedTextFieldTokens.FocusSupportingColor.toColor(),
-        unfocusedSupportingTextColor: Color = OutlinedTextFieldTokens.SupportingColor.toColor(),
+        errorPlaceholderColor: Color = OutlinedTextFieldTokens.InputPlaceholderColor.value,
+        focusedSupportingTextColor: Color = OutlinedTextFieldTokens.FocusSupportingColor.value,
+        unfocusedSupportingTextColor: Color = OutlinedTextFieldTokens.SupportingColor.value,
         disabledSupportingTextColor: Color = OutlinedTextFieldTokens.DisabledSupportingColor
-            .toColor().copy(alpha = OutlinedTextFieldTokens.DisabledSupportingOpacity),
-        errorSupportingTextColor: Color = OutlinedTextFieldTokens.ErrorSupportingColor.toColor(),
-        focusedPrefixColor: Color = OutlinedTextFieldTokens.InputPrefixColor.toColor(),
-        unfocusedPrefixColor: Color = OutlinedTextFieldTokens.InputPrefixColor.toColor(),
-        disabledPrefixColor: Color = OutlinedTextFieldTokens.InputPrefixColor.toColor()
+            .value.copy(alpha = OutlinedTextFieldTokens.DisabledSupportingOpacity),
+        errorSupportingTextColor: Color = OutlinedTextFieldTokens.ErrorSupportingColor.value,
+        focusedPrefixColor: Color = OutlinedTextFieldTokens.InputPrefixColor.value,
+        unfocusedPrefixColor: Color = OutlinedTextFieldTokens.InputPrefixColor.value,
+        disabledPrefixColor: Color = OutlinedTextFieldTokens.InputPrefixColor.value
             .copy(alpha = OutlinedTextFieldTokens.DisabledInputOpacity),
-        errorPrefixColor: Color = OutlinedTextFieldTokens.InputPrefixColor.toColor(),
-        focusedSuffixColor: Color = OutlinedTextFieldTokens.InputSuffixColor.toColor(),
-        unfocusedSuffixColor: Color = OutlinedTextFieldTokens.InputSuffixColor.toColor(),
-        disabledSuffixColor: Color = OutlinedTextFieldTokens.InputSuffixColor.toColor()
+        errorPrefixColor: Color = OutlinedTextFieldTokens.InputPrefixColor.value,
+        focusedSuffixColor: Color = OutlinedTextFieldTokens.InputSuffixColor.value,
+        unfocusedSuffixColor: Color = OutlinedTextFieldTokens.InputSuffixColor.value,
+        disabledSuffixColor: Color = OutlinedTextFieldTokens.InputSuffixColor.value
             .copy(alpha = OutlinedTextFieldTokens.DisabledInputOpacity),
-        errorSuffixColor: Color = OutlinedTextFieldTokens.InputSuffixColor.toColor(),
+        errorSuffixColor: Color = OutlinedTextFieldTokens.InputSuffixColor.value,
     ): TextFieldColors =
         TextFieldColors(
             focusedTextColor = focusedTextColor,
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TimePicker.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TimePicker.kt
index dded021..3f6e81e 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TimePicker.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TimePicker.kt
@@ -132,12 +132,13 @@
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.semantics.clearAndSetSemantics
 import androidx.compose.ui.semantics.contentDescription
-import androidx.compose.ui.semantics.isContainer
+import androidx.compose.ui.semantics.isTraversalGroup
 import androidx.compose.ui.semantics.onClick
 import androidx.compose.ui.semantics.role
 import androidx.compose.ui.semantics.selectableGroup
 import androidx.compose.ui.semantics.selected
 import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.traversalIndex
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.text.input.TextFieldValue
@@ -273,27 +274,27 @@
      */
     @Composable
     fun colors(
-        clockDialColor: Color = ClockDialColor.toColor(),
-        clockDialSelectedContentColor: Color = ClockDialSelectedLabelTextColor.toColor(),
-        clockDialUnselectedContentColor: Color = ClockDialUnselectedLabelTextColor.toColor(),
-        selectorColor: Color = ClockDialSelectorHandleContainerColor.toColor(),
-        containerColor: Color = ContainerColor.toColor(),
-        periodSelectorBorderColor: Color = PeriodSelectorOutlineColor.toColor(),
+        clockDialColor: Color = ClockDialColor.value,
+        clockDialSelectedContentColor: Color = ClockDialSelectedLabelTextColor.value,
+        clockDialUnselectedContentColor: Color = ClockDialUnselectedLabelTextColor.value,
+        selectorColor: Color = ClockDialSelectorHandleContainerColor.value,
+        containerColor: Color = ContainerColor.value,
+        periodSelectorBorderColor: Color = PeriodSelectorOutlineColor.value,
         periodSelectorSelectedContainerColor: Color =
-            PeriodSelectorSelectedContainerColor.toColor(),
+            PeriodSelectorSelectedContainerColor.value,
         periodSelectorUnselectedContainerColor: Color = Color.Transparent,
         periodSelectorSelectedContentColor: Color =
-            PeriodSelectorSelectedLabelTextColor.toColor(),
+            PeriodSelectorSelectedLabelTextColor.value,
         periodSelectorUnselectedContentColor: Color =
-            PeriodSelectorUnselectedLabelTextColor.toColor(),
+            PeriodSelectorUnselectedLabelTextColor.value,
         timeSelectorSelectedContainerColor: Color =
-            TimeSelectorSelectedContainerColor.toColor(),
+            TimeSelectorSelectedContainerColor.value,
         timeSelectorUnselectedContainerColor: Color =
-            TimeSelectorUnselectedContainerColor.toColor(),
+            TimeSelectorUnselectedContainerColor.value,
         timeSelectorSelectedContentColor: Color =
-            TimeSelectorSelectedLabelTextColor.toColor(),
+            TimeSelectorSelectedLabelTextColor.value,
         timeSelectorUnselectedContentColor: Color =
-            TimeSelectorUnselectedLabelTextColor.toColor(),
+            TimeSelectorUnselectedLabelTextColor.value,
     ) = TimePickerColors(
         clockDialColor = clockDialColor,
         clockDialSelectedContentColor = clockDialSelectedContentColor,
@@ -404,7 +405,8 @@
 
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
-        if (javaClass != other?.javaClass) return false
+        if (other === null) return false
+        if (this::class != other::class) return false
 
         other as TimePickerColors
 
@@ -694,7 +696,10 @@
     colors: TimePickerColors = TimePickerDefaults.colors(),
     autoSwitchToMinute: Boolean
 ) {
-    Column(modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally) {
+    Column(
+        modifier = modifier.semantics { isTraversalGroup = true },
+        horizontalAlignment = Alignment.CenterHorizontally
+    ) {
         VerticalClockDisplay(state, colors)
         Spacer(modifier = Modifier.height(ClockDisplayBottomMargin))
         ClockFace(state, colors, autoSwitchToMinute)
@@ -916,7 +921,7 @@
             val spacerPlaceable = spacer.measure(
                 constraints.copy(
                     minWidth = 0,
-                    maxWidth = TimePickerTokens.PeriodSelectorOutlineWidth.toPx().roundToInt(),
+                    maxWidth = TimePickerTokens.PeriodSelectorOutlineWidth.roundToPx(),
                 )
             )
 
@@ -935,7 +940,7 @@
         }
     }
 
-    val shape = PeriodSelectorContainerShape.toShape() as CornerBasedShape
+    val shape = PeriodSelectorContainerShape.value as CornerBasedShape
 
     PeriodToggleImpl(
         modifier = modifier,
@@ -959,7 +964,7 @@
             val spacerPlaceable = spacer.measure(
                 constraints.copy(
                     minHeight = 0,
-                    maxHeight = TimePickerTokens.PeriodSelectorOutlineWidth.toPx().roundToInt()
+                    maxHeight = TimePickerTokens.PeriodSelectorOutlineWidth.roundToPx()
                 )
             )
 
@@ -978,7 +983,7 @@
         }
     }
 
-    val shape = PeriodSelectorContainerShape.toShape() as CornerBasedShape
+    val shape = PeriodSelectorContainerShape.value as CornerBasedShape
 
     PeriodToggleImpl(
         modifier = modifier,
@@ -1004,26 +1009,22 @@
         colors.periodSelectorBorderColor
     )
 
-    val shape = PeriodSelectorContainerShape.toShape() as CornerBasedShape
+    val shape = PeriodSelectorContainerShape.value as CornerBasedShape
     val contentDescription = getString(Strings.TimePickerPeriodToggle)
     Layout(
         modifier = modifier
             .semantics {
-                @Suppress("DEPRECATION")
-                isContainer = true
+                isTraversalGroup = true
                 this.contentDescription = contentDescription
             }
             .selectableGroup()
-            .then(modifier)
             .border(border = borderStroke, shape = shape),
         measurePolicy = measurePolicy,
         content = {
             ToggleItem(
                 checked = !state.isAfternoonToggle,
                 shape = startShape,
-                onClick = {
-                    state.isAfternoonToggle = false
-                },
+                onClick = { state.isAfternoonToggle = false },
                 colors = colors,
             ) { Text(text = getString(string = Strings.TimePickerAM)) }
             Spacer(
@@ -1031,15 +1032,13 @@
                     .layoutId("Spacer")
                     .zIndex(SeparatorZIndex)
                     .fillMaxSize()
-                    .background(color = PeriodSelectorOutlineColor.toColor())
+                    .background(color = PeriodSelectorOutlineColor.value)
             )
             ToggleItem(
                 checked =
                 state.isAfternoonToggle,
                 shape = endShape,
-                onClick = {
-                    state.isAfternoonToggle = true
-                },
+                onClick = { state.isAfternoonToggle = true },
                 colors = colors,
             ) { Text(getString(string = Strings.TimePickerPM)) }
         }
@@ -1075,13 +1074,12 @@
 
 @Composable
 private fun DisplaySeparator(modifier: Modifier) {
-    val style = copyAndSetFontPadding(
-        style = LocalTextStyle.current.copy(
-            textAlign = TextAlign.Center,
-            lineHeightStyle = LineHeightStyle(
-                alignment = LineHeightStyle.Alignment.Center, trim = LineHeightStyle.Trim.Both
-            )
-        ), includeFontPadding = false
+    val style = LocalTextStyle.current.copy(
+        textAlign = TextAlign.Center,
+        lineHeightStyle = LineHeightStyle(
+            alignment = LineHeightStyle.Alignment.Center,
+            trim = LineHeightStyle.Trim.Both
+        )
     )
 
     Box(
@@ -1090,7 +1088,7 @@
     ) {
         Text(
             text = ":",
-            color = TimeFieldSeparatorColor.toColor(),
+            color = TimeFieldSeparatorColor.value,
             style = style
         )
     }
@@ -1132,7 +1130,7 @@
             }
         },
         selected = selected,
-        shape = TimeSelectorContainerShape.toShape(),
+        shape = TimeSelectorContainerShape.value,
         color = containerColor,
     ) {
         val valueContentDescription =
@@ -1162,11 +1160,7 @@
         modifier = Modifier
             .background(shape = CircleShape, color = colors.clockDialColor)
             .size(ClockDialContainerSize)
-            .semantics {
-                @Suppress("DEPRECATION")
-                isContainer = false
-                selectableGroup()
-            },
+            .semantics { selectableGroup() },
         targetState = state.values,
         animationSpec = tween(durationMillis = MotionTokens.DurationMedium3.toInt())
     ) { screen ->
@@ -1180,13 +1174,20 @@
             CompositionLocalProvider(
                 LocalContentColor provides colors.clockDialContentColor(false)
             ) {
-                repeat(screen.size) {
+                repeat(screen.size) { index ->
                     val outerValue = if (!state.is24hour || state.selection == Selection.Minute) {
-                        screen[it]
+                        screen[index]
                     } else {
-                        screen[it] % 12
+                        screen[index] % 12
                     }
-                    ClockText(state = state, value = outerValue, autoSwitchToMinute)
+                    ClockText(
+                        modifier = Modifier.semantics {
+                            traversalIndex = index.toFloat()
+                        },
+                        state = state,
+                        value = outerValue,
+                        autoSwitchToMinute = autoSwitchToMinute
+                    )
                 }
 
                 if (state.selection == Selection.Hour && state.is24hour) {
@@ -1197,9 +1198,16 @@
                             .background(shape = CircleShape, color = Color.Transparent),
                         radius = InnerCircleRadius
                     ) {
-                        repeat(ExtraHours.size) {
-                            val innerValue = ExtraHours[it]
-                            ClockText(state = state, value = innerValue, autoSwitchToMinute)
+                        repeat(ExtraHours.size) { index ->
+                            val innerValue = ExtraHours[index]
+                            ClockText(
+                                modifier = Modifier.semantics {
+                                    traversalIndex = 12 + index.toFloat()
+                                },
+                                state = state,
+                                value = innerValue,
+                                autoSwitchToMinute = autoSwitchToMinute
+                            )
                         }
                     }
                 }
@@ -1315,11 +1323,13 @@
 }
 
 @Composable
-private fun ClockText(state: TimePickerState, value: Int, autoSwitchToMinute: Boolean) {
-    val style = MaterialTheme.typography.fromToken(ClockDialLabelTextFont).let {
-        copyAndSetFontPadding(style = it, false)
-    }
-
+private fun ClockText(
+    modifier: Modifier,
+    state: TimePickerState,
+    value: Int,
+    autoSwitchToMinute: Boolean
+) {
+    val style = MaterialTheme.typography.fromToken(ClockDialLabelTextFont)
     val maxDist = with(LocalDensity.current) { MaxDistance.toPx() }
     var center by remember { mutableStateOf(Offset.Zero) }
     val scope = rememberCoroutineScope()
@@ -1339,7 +1349,7 @@
 
     Box(
         contentAlignment = Alignment.Center,
-        modifier = Modifier
+        modifier = modifier
             .minimumInteractiveComponentSize()
             .size(MinimumInteractiveSize)
             .onGloballyPositioned { center = it.boundsInParent().center }
@@ -1491,7 +1501,7 @@
                             enabled = true,
                             isError = false,
                             interactionSource = interactionSource,
-                            shape = TimeInputTokens.TimeFieldContainerShape.toShape(),
+                            shape = TimeInputTokens.TimeFieldContainerShape.value,
                             colors = textFieldColors,
                         )
                     }
@@ -1510,7 +1520,7 @@
                     Strings.TimePickerMinute
                 }
             ),
-            color = TimeInputTokens.TimeFieldSupportingTextColor.toColor(),
+            color = TimeInputTokens.TimeFieldSupportingTextColor.value,
             style = MaterialTheme
                 .typography
                 .fromToken(TimeInputTokens.TimeFieldSupportingTextFont)
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Tooltip.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Tooltip.kt
index 38a5b2b..7052341 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Tooltip.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Tooltip.kt
@@ -24,7 +24,6 @@
 import androidx.compose.animation.core.updateTransition
 import androidx.compose.foundation.MutatePriority
 import androidx.compose.foundation.MutatorMutex
-import androidx.compose.foundation.focusable
 import androidx.compose.foundation.gestures.awaitEachGesture
 import androidx.compose.foundation.gestures.awaitFirstDown
 import androidx.compose.foundation.gestures.waitForUpOrCancellation
@@ -384,25 +383,25 @@
      * The default [Shape] for a [PlainTooltipBox]'s container.
      */
     val plainTooltipContainerShape: Shape
-        @Composable get() = PlainTooltipTokens.ContainerShape.toShape()
+        @Composable get() = PlainTooltipTokens.ContainerShape.value
 
     /**
      * The default [Color] for a [PlainTooltipBox]'s container.
      */
     val plainTooltipContainerColor: Color
-        @Composable get() = PlainTooltipTokens.ContainerColor.toColor()
+        @Composable get() = PlainTooltipTokens.ContainerColor.value
 
     /**
      * The default [Color] for the content within the [PlainTooltipBox].
      */
     val plainTooltipContentColor: Color
-        @Composable get() = PlainTooltipTokens.SupportingTextColor.toColor()
+        @Composable get() = PlainTooltipTokens.SupportingTextColor.value
 
     /**
      * The default [Shape] for a [RichTooltipBox]'s container.
      */
     val richTooltipContainerShape: Shape @Composable get() =
-        RichTooltipTokens.ContainerShape.toShape()
+        RichTooltipTokens.ContainerShape.value
 
     /**
      * Method to create a [RichTooltipColors] for [RichTooltipBox]
@@ -410,10 +409,10 @@
      */
     @Composable
     fun richTooltipColors(
-        containerColor: Color = RichTooltipTokens.ContainerColor.toColor(),
-        contentColor: Color = RichTooltipTokens.SupportingTextColor.toColor(),
-        titleContentColor: Color = RichTooltipTokens.SubheadColor.toColor(),
-        actionContentColor: Color = RichTooltipTokens.ActionLabelTextColor.toColor(),
+        containerColor: Color = RichTooltipTokens.ContainerColor.value,
+        contentColor: Color = RichTooltipTokens.SupportingTextColor.value,
+        titleContentColor: Color = RichTooltipTokens.SubheadColor.value,
+        actionContentColor: Color = RichTooltipTokens.ActionLabelTextColor.value,
     ): RichTooltipColors =
         RichTooltipColors(
             containerColor = containerColor,
@@ -802,6 +801,8 @@
     )
 }
 
+@Composable
+@ExperimentalMaterial3Api
 internal expect fun TooltipPopup(
     popupPositionProvider: PopupPositionProvider,
     onDismissRequest: () -> Unit,
diff --git a/compose/runtime/runtime-lint/build.gradle b/compose/runtime/runtime-lint/build.gradle
index 467bc1d..ac9b31a 100644
--- a/compose/runtime/runtime-lint/build.gradle
+++ b/compose/runtime/runtime-lint/build.gradle
@@ -26,7 +26,7 @@
 BundleInsideHelper.forInsideLintJar(project)
 
 dependencies {
-    compileOnly(libs.androidLintMinComposeApi)
+    compileOnly(libs.androidLintApi)
     compileOnly(libs.kotlinStdlib)
     bundleInside(projectOrArtifact(":compose:lint:common"))
 
diff --git a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/AutoboxingStateCreationDetector.kt b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/AutoboxingStateCreationDetector.kt
index f225ede..369aff4 100644
--- a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/AutoboxingStateCreationDetector.kt
+++ b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/AutoboxingStateCreationDetector.kt
@@ -21,7 +21,6 @@
 import androidx.compose.lint.Name
 import androidx.compose.lint.Names
 import androidx.compose.lint.isInPackageName
-import androidx.compose.lint.resolveCall
 import com.android.tools.lint.detector.api.Category
 import com.android.tools.lint.detector.api.Detector
 import com.android.tools.lint.detector.api.Implementation
@@ -34,12 +33,13 @@
 import com.android.tools.lint.detector.api.SourceCodeScanner
 import com.intellij.psi.PsiMethod
 import java.util.EnumSet
-import org.jetbrains.kotlin.js.descriptorUtils.getJetTypeFqName
+import org.jetbrains.kotlin.analysis.api.analyze
+import org.jetbrains.kotlin.analysis.api.calls.singleFunctionCallOrNull
+import org.jetbrains.kotlin.psi.KtElement
 import org.jetbrains.kotlin.psi.KtTypeArgumentList
 import org.jetbrains.kotlin.psi.KtValueArgumentList
 import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
 import org.jetbrains.uast.UCallExpression
-import org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression
 import org.jetbrains.uast.skipParenthesizedExprDown
 
 /**
@@ -66,8 +66,7 @@
     override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
         if (!method.isInPackageName(Names.Runtime.PackageName)) return
 
-        val replacement = getSuggestedReplacementName(node as KotlinUFunctionCallExpression)
-            ?: return
+        val replacement = getSuggestedReplacementName(node) ?: return
 
         context.report(
             issue = AutoboxingStateCreation,
@@ -129,26 +128,33 @@
     }
 
     private fun getSuggestedReplacementName(
-        invocation: KotlinUFunctionCallExpression
+        invocation: UCallExpression
     ): Name? {
         if (!usesStructuralEqualityPolicy(invocation)) return null
 
-        val resolvedCall = invocation.resolveCall() ?: return null
-        val stateType = resolvedCall.typeArguments.asIterable().single().value
-        return when {
-            stateType.isMarkedNullable -> null
-            else -> replacements[stateType.getJetTypeFqName(true)]
+        val sourcePsi = invocation.sourcePsi as? KtElement ?: return null
+        analyze(sourcePsi) {
+            val resolvedCall = sourcePsi.resolveCall()?.singleFunctionCallOrNull() ?: return null
+            val stateType = resolvedCall.typeArgumentsMapping.asIterable().single().value
+            return when {
+                stateType.isMarkedNullable -> null
+                else -> {
+                    // NB: use expanded class symbol for type alias
+                    val fqName = stateType.expandedClassSymbol?.classIdIfNonLocal?.asFqNameString()
+                    replacements[fqName]
+                }
+            }
         }
     }
 
     private fun usesStructuralEqualityPolicy(
-        invocation: KotlinUFunctionCallExpression
+        invocation: UCallExpression
     ): Boolean {
         val policyExpr = invocation.valueArguments.getOrNull(MUTATION_POLICY_PARAM_IDX)
             ?.skipParenthesizedExprDown()
             ?: return true // No argument passed; we're using the default policy
 
-        val policyMethod = (policyExpr as? KotlinUFunctionCallExpression)?.resolve()
+        val policyMethod = (policyExpr as? UCallExpression)?.resolve()
             ?: return false // Argument isn't a direct function call. Assume it's a more complex
                             // policy, or isn't always the structural equality policy.
 
diff --git a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/AutoboxingStateValuePropertyDetector.kt b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/AutoboxingStateValuePropertyDetector.kt
index d5e2643..ddb9810 100644
--- a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/AutoboxingStateValuePropertyDetector.kt
+++ b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/AutoboxingStateValuePropertyDetector.kt
@@ -16,6 +16,8 @@
 
 package androidx.compose.runtime.lint
 
+import com.android.tools.lint.detector.api.AnnotationInfo
+import com.android.tools.lint.detector.api.AnnotationUsageInfo
 import com.android.tools.lint.detector.api.AnnotationUsageType
 import com.android.tools.lint.detector.api.Category
 import com.android.tools.lint.detector.api.Detector
@@ -27,7 +29,6 @@
 import com.android.tools.lint.detector.api.Severity
 import com.android.tools.lint.detector.api.SourceCodeScanner
 import com.android.tools.lint.detector.api.UastLintUtils
-import com.intellij.psi.PsiMethod
 import java.util.EnumSet
 import org.jetbrains.uast.UAnnotation
 import org.jetbrains.uast.UElement
@@ -49,26 +50,21 @@
         return listOf("androidx.compose.runtime.snapshots.AutoboxingStateValueProperty")
     }
 
+    override fun isApplicableAnnotationUsage(type: AnnotationUsageType): Boolean {
+        return type == AnnotationUsageType.FIELD_REFERENCE
+    }
+
     override fun visitAnnotationUsage(
         context: JavaContext,
-        usage: UElement,
-        type: AnnotationUsageType,
-        annotation: UAnnotation,
-        qualifiedName: String,
-        method: PsiMethod?,
-        annotations: List<UAnnotation>,
-        allMemberAnnotations: List<UAnnotation>,
-        allClassAnnotations: List<UAnnotation>,
-        allPackageAnnotations: List<UAnnotation>
+        element: UElement,
+        annotationInfo: AnnotationInfo,
+        usageInfo: AnnotationUsageInfo
     ) {
-        if (type != AnnotationUsageType.FIELD_REFERENCE) {
-            return
-        }
+        val resolvedPropertyName = element.identifier ?: "<unknown identifier>"
+        val preferredPropertyName =
+            annotationInfo.annotation.preferredPropertyName ?: "<unknown replacement>"
 
-        val resolvedPropertyName = usage.identifier ?: "<unknown identifier>"
-        val preferredPropertyName = annotation.preferredPropertyName ?: "<unknown replacement>"
-
-        val accessKind = when (usage.resolvedName?.takeWhile { it.isLowerCase() }) {
+        val accessKind = when (element.resolvedName?.takeWhile { it.isLowerCase() }) {
             "get" -> "Reading"
             "set" -> "Assigning"
             else -> "Accessing"
@@ -76,8 +72,8 @@
 
         context.report(
             AutoboxingStateValueProperty,
-            usage,
-            context.getLocation(usage),
+            element,
+            context.getLocation(element),
             "$accessKind `$resolvedPropertyName` will cause an autoboxing operation. " +
                 "Use `$preferredPropertyName` to avoid unnecessary allocations.",
             createPropertyReplacementQuickFix(
diff --git a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/MutableCollectionMutableStateDetector.kt b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/MutableCollectionMutableStateDetector.kt
index 6d2d7f1..1b650c1 100644
--- a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/MutableCollectionMutableStateDetector.kt
+++ b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/MutableCollectionMutableStateDetector.kt
@@ -30,12 +30,12 @@
 import com.android.tools.lint.detector.api.SourceCodeScanner
 import com.intellij.psi.PsiMethod
 import java.util.EnumSet
-import org.jetbrains.kotlin.descriptors.containingPackage
+import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
+import org.jetbrains.kotlin.analysis.api.analyze
+import org.jetbrains.kotlin.analysis.api.types.KtNonErrorClassType
+import org.jetbrains.kotlin.analysis.api.types.KtType
 import org.jetbrains.kotlin.psi.KtExpression
-import org.jetbrains.kotlin.types.KotlinType
-import org.jetbrains.kotlin.types.typeUtil.supertypes
 import org.jetbrains.uast.UCallExpression
-import org.jetbrains.uast.kotlin.KotlinUastResolveProviderService
 
 /**
  * [Detector] that checks `mutableStateOf` calls to warn if the type is a mutable collection, as
@@ -54,29 +54,28 @@
 
         val expression = node.sourcePsi as? KtExpression ?: return
 
-        // PsiType will return the underlying JVM type for kotlin collections, so instead we need to
-        // use the KotlinType to preserve the actual Kotlin type declared in source - that way we
+        // [PsiType] will return the underlying JVM type for kotlin collections, so instead we need
+        // to use the [KtType] to preserve the actual Kotlin type declared in source - that way we
         // can disambiguate between MutableList and the underlying java.util.List that it will be
         // converted to.
-        val service = expression.project.getService(KotlinUastResolveProviderService::class.java)
-        val bindingContext = service.getBindingContext(expression)
-        val expressionType = bindingContext.getType(expression)
+        analyze(expression) {
+            val expressionType = expression.getKtType() as? KtNonErrorClassType ?: return
+            // expressionType will be MutableState<Foo>, so unwrap the argument to get the type we
+            // care about. We do this instead of looking at the inner expression type, to account
+            // for cases such as mutableStateOf<List<Int>>(mutableListOf(1)) or
+            // val foo: MutableState<List<Int>> = mutableStateOf(mutableListOf(1)) - the inner
+            // expression type is mutable but because the type of the mutableStateOf expression is
+            // not, we don't want to report a warning.
+            val type = expressionType.ownTypeArguments.firstOrNull()?.type ?: return
 
-        // expressionType will be MutableState<Foo>, so unwrap the argument to get the type we care
-        // about. We do this instead of looking at the inner expression type, to account for cases
-        // such as mutableStateOf<List<Int>>(mutableListOf(1)) or
-        // val foo: MutableState<List<Int>> = mutableStateOf(mutableListOf(1)) - the inner
-        // expression type is mutable but because the type of the mutableStateOf expression is not,
-        // we don't want to report a warning.
-        val type = expressionType?.arguments?.firstOrNull()?.type ?: return
-
-        if (type.isMutableCollection()) {
-            context.report(
-                MutableCollectionMutableState,
-                node,
-                context.getNameLocation(node),
-                "Creating a MutableState object with a mutable collection type"
-            )
+            if (isMutableCollection(type)) {
+                context.report(
+                    MutableCollectionMutableState,
+                    node,
+                    context.getNameLocation(node),
+                    "Creating a MutableState object with a mutable collection type"
+                )
+            }
         }
     }
 
@@ -115,7 +114,7 @@
  * - [java.util.Collection]
  * - [java.util.Map]
  */
-private fun KotlinType.isMutableCollection(): Boolean {
+private fun KtAnalysisSession.isMutableCollection(ktType: KtType): Boolean {
     // MutableCollection::class.qualifiedName == Collection::class.qualifiedName, so using hardcoded
     // strings instead
     val kotlinImmutableTypes = listOf(
@@ -134,21 +133,19 @@
     )
 
     // Check `this`
-    if (kotlinMutableTypes.any { it == fqn }) return true
-    if (kotlinImmutableTypes.any { it == fqn }) return false
-    if (javaMutableTypes.any { it == fqn }) return true
+    if (kotlinMutableTypes.any { it == fqn(ktType) }) return true
+    if (kotlinImmutableTypes.any { it == fqn(ktType) }) return false
+    if (javaMutableTypes.any { it == fqn(ktType) }) return true
 
     // Check supertypes
-    val supertypes = supertypes()
-    if (supertypes.any { type -> kotlinMutableTypes.any { it == type.fqn } }) return true
-    if (supertypes.any { type -> kotlinImmutableTypes.any { it == type.fqn } }) return false
-    if (supertypes.any { type -> javaMutableTypes.any { it == type.fqn } }) return true
+    val supertypes = ktType.getAllSuperTypes()
+    if (supertypes.any { type -> kotlinMutableTypes.any { it == fqn(type) } }) return true
+    if (supertypes.any { type -> kotlinImmutableTypes.any { it == fqn(type) } }) return false
+    if (supertypes.any { type -> javaMutableTypes.any { it == fqn(type) } }) return true
 
     return false
 }
 
-private val KotlinType.fqn: String? get() {
-    val descriptor = constructor.declarationDescriptor ?: return null
-    val packageName = descriptor.containingPackage()?.asString() ?: return null
-    return packageName + "." + descriptor.name.asString()
+private fun KtAnalysisSession.fqn(ktType: KtType): String? {
+    return ktType.expandedClassSymbol?.classIdIfNonLocal?.asFqNameString()
 }
diff --git a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/OpaqueUnitKeyDetector.kt b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/OpaqueUnitKeyDetector.kt
index c950082..fb7a05b 100644
--- a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/OpaqueUnitKeyDetector.kt
+++ b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/OpaqueUnitKeyDetector.kt
@@ -217,7 +217,7 @@
     }
 
     private fun UExpression.isUnitLiteral(): Boolean {
-        val expr = skipParenthesizedExprDown() ?: this
+        val expr = skipParenthesizedExprDown()
         if (expr !is USimpleNameReferenceExpression) return false
 
         return (expr.tryResolveUDeclaration() as? UClass)?.qualifiedName == FqUnitName
diff --git a/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/ApiLintVersionsTest.kt b/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/ApiLintVersionsTest.kt
index ad0785c..282fc5d 100644
--- a/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/ApiLintVersionsTest.kt
+++ b/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/ApiLintVersionsTest.kt
@@ -34,6 +34,6 @@
 
         val registry = RuntimeIssueRegistry()
         assertThat(registry.api).isEqualTo(CURRENT_API)
-        assertThat(registry.minApi).isEqualTo(10)
+        assertThat(registry.minApi).isEqualTo(14)
     }
 }
diff --git a/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/AutoboxingStateValuePropertyDetectorTest.kt b/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/AutoboxingStateValuePropertyDetectorTest.kt
index 5ff264d..5bac151 100644
--- a/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/AutoboxingStateValuePropertyDetectorTest.kt
+++ b/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/AutoboxingStateValuePropertyDetectorTest.kt
@@ -16,8 +16,9 @@
 
 package androidx.compose.runtime.lint
 
-import androidx.compose.lint.test.Stubs
+import androidx.compose.lint.test.bytecodeStub
 import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestFile
 import com.android.tools.lint.detector.api.Detector
 import com.android.tools.lint.detector.api.Issue
 import org.junit.Test
@@ -36,34 +37,31 @@
     @Test
     fun testReadAutoboxingPropertyAsVariableAssignment() {
         lint().files(
-            AutoboxingStateValuePropertyStub,
-            StateWithAutoboxingPropertyStub,
-            Stubs.SnapshotState,
             kotlin(
                 """
                     package androidx.compose.runtime.lint.test
 
-                    import androidx.compose.runtime.sandbox.MutableIntState
-                    import androidx.compose.runtime.getValue
-                    import androidx.compose.runtime.setValue
+                    import androidx.compose.runtime.mutableIntStateOf
 
                     fun valueAssignment() {
-                        val state = MutableIntState()
+                        val state = mutableIntStateOf(4)
                         val value = state.value
                     }
                 """.trimIndent()
-            )
+            ),
+            AutoboxingStateValuePropertyStub,
+            MinimalSnapshotStateStub
         ).run().expect(
             """
-src/androidx/compose/runtime/lint/test/test.kt:9: Warning: Reading value will cause an autoboxing operation. Use intValue to avoid unnecessary allocations. [AutoboxingStateValueProperty]
+src/androidx/compose/runtime/lint/test/test.kt:7: Warning: Reading value will cause an autoboxing operation. Use intValue to avoid unnecessary allocations. [AutoboxingStateValueProperty]
     val value = state.value
                       ~~~~~
 0 errors, 1 warnings
             """
         ).expectFixDiffs(
             """
-Fix for src/androidx/compose/runtime/lint/test/test.kt line 9: Replace with `intValue`:
-@@ -9 +9
+Fix for src/androidx/compose/runtime/lint/test/test.kt line 7: Replace with `intValue`:
+@@ -7 +7
 -     val value = state.value
 +     val value = state.intValue
             """
@@ -73,34 +71,31 @@
     @Test
     fun testTrivialAssignAutoboxingProperty() {
         lint().files(
-            AutoboxingStateValuePropertyStub,
-            StateWithAutoboxingPropertyStub,
-            Stubs.SnapshotState,
             kotlin(
                 """
                     package androidx.compose.runtime.lint.test
 
-                    import androidx.compose.runtime.sandbox.MutableIntState
-                    import androidx.compose.runtime.getValue
-                    import androidx.compose.runtime.setValue
+                    import androidx.compose.runtime.mutableIntStateOf
 
                     fun valueAssignment() {
-                        val state = MutableIntState()
+                        val state = mutableIntStateOf(0)
                         state.value = 42
                     }
                 """.trimIndent()
-            )
+            ),
+            AutoboxingStateValuePropertyStub,
+            MinimalSnapshotStateStub
         ).run().expect(
             """
-src/androidx/compose/runtime/lint/test/test.kt:9: Warning: Assigning value will cause an autoboxing operation. Use intValue to avoid unnecessary allocations. [AutoboxingStateValueProperty]
+src/androidx/compose/runtime/lint/test/test.kt:7: Warning: Assigning value will cause an autoboxing operation. Use intValue to avoid unnecessary allocations. [AutoboxingStateValueProperty]
     state.value = 42
           ~~~~~
 0 errors, 1 warnings
             """
         ).expectFixDiffs(
             """
-Fix for src/androidx/compose/runtime/lint/test/test.kt line 9: Replace with `intValue`:
-@@ -9 +9
+Fix for src/androidx/compose/runtime/lint/test/test.kt line 7: Replace with `intValue`:
+@@ -7 +7
 -     state.value = 42
 +     state.intValue = 42
             """
@@ -108,53 +103,193 @@
     }
 
     companion object {
-        private val StateWithAutoboxingPropertyStub = kotlin(
-            """
-            package androidx.compose.runtime.sandbox
-
-            import androidx.compose.runtime.snapshots.AutoboxingStateValueProperty
-
-            class MutableIntState {
-
-                @AutoboxingStateValueProperty(
-                    preferredPropertyName = "intValue",
-                    delegatePackageDirective = "androidx.compose.runtime.sandbox"
-                )
-                var value: Int?
-                    get() = intValue
-                    set(value) = value?.let { intValue = it }
-
-                var intValue: Int = 0
-            }
-
-            @Suppress("NOTHING_TO_INLINE")
-            inline operator fun MutableIntState.getValue(
-                thisObj: Any?,
-                property: KProperty<*>
-            ): Int = intValue
-
-            @Suppress("NOTHING_TO_INLINE")
-            inline operator fun MutableIntState.setValue(
-                thisObj: Any?,
-                property: KProperty<*>,
-                value: Int
-            ) {
-                intValue = value
-            }
-            """
-        )
-
-        private val AutoboxingStateValuePropertyStub = kotlin(
-            """
+        private val AutoboxingStateValuePropertyStub = bytecodeStub(
+            filename = "AutoboxingStateValueProperty.kt",
+            filepath = "androidx/compose/runtime/snapshots",
+            checksum = 0x2c564988,
+            source = """
                 package androidx.compose.runtime.snapshots
 
                 @Retention(AnnotationRetention.BINARY)
-                @Target(AnnotationTarget.PROPERTY)
+                @Target(AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.PROPERTY_GETTER)
                 annotation class AutoboxingStateValueProperty(
-                    val preferredPropertyName: String,
-                    val delegatePackageDirective: String
+                    val preferredPropertyName: String
                 )
+            """,
             """
+                META-INF/main.kotlin_module:
+                H4sIAAAAAAAA/2NgYGBmYGBgBGIOBijg0uaSSMxLKcrPTKnQS87PLcgvTtUr
+                Ks0rycxNFeIPzkssKM7ILwkuSSxJ9S7h0uFSwqVYLyczr0SvJLW4RIgtBEh6
+                lygxaDEAAJG2tCtzAAAA
+            """,
+            """
+                androidx/compose/runtime/snapshots/AutoboxingStateValueProperty.class:
+                H4sIAAAAAAAA/6VSTXMSQRB9s3ytqGETEyXESBIjiRcXU960yiKKShUJ1O5W
+                qlIcrIEdccOyg7sDwo2b/8Of4cGicvRHWc4EAxyoePDS/ba7X/fr3vn1+8dP
+                AC9wSPCaBm7IPXdotni3xyNmhv1AeF1mRgHtRZ+5iMxSX/AmH3pB2xZUsDPq
+                91k95D0WilEKhMC4oANq+jRom7XmBWuJFGIE+XmUBgGXVI8HZmkGU0gQbPRC
+                9omFIXOvO57SLiNYP3xanfNtEcrpLwm2qx0ufC9Y7GgxwQKFZD4xUOIIDpbU
+                zScvMpLHldOSdU6QW0JxaNhmQlatUN/nX5k7DUQE+zcOmPEydatWL1vO+Ue7
+                7DhlazHy/m9kp7r0UIsiC/8oqXPfa43U/m+qJdtWd1pKmOnaW54v+6wrOzqj
+                HlOnOSk7H2pvCVavlz1hgrpUUJnUuoOYfEREGV0ZEJCOjA899VWUyH1O8Goy
+                NtJaVktrxlZ6MpZuRTr98puWnYyPtCI51teShpbTijErM/WX35PJXFzXjLjq
+                cURQqv7nI5Vypbr8TSXPOoIgbfN+2GLvPF8+oU1rOuPMi7ymz+Y/NypIXYjL
+                jkm1tVSZgi6RhsKVfYID6b8ggVuyJs1wG3dwV8KVBjSGDAxlVrE2zd7DOjYU
+                bIAw3McDZCV1s4FYBbkKtip4iG0J8aiCPHZkVYRd7DWQiPA4wn6E1JXV/wB4
+                ImM31wMAAA==
+            """
+        )
+
+        private val MinimalSnapshotStateStub: TestFile = bytecodeStub(
+            filename = "SnapshotState.kt",
+            filepath = "androidx/compose/runtime",
+            checksum = 0x575bf828,
+            source = """
+            package androidx.compose.runtime
+
+            import androidx.compose.runtime.snapshots.AutoboxingStateValueProperty
+
+            fun mutableIntStateOf(initialValue: Int): MutableIntState =
+                throw UnsupportedOperationException("Stub!")
+
+            interface State<T> {
+                val value: T
+            }
+
+            interface MutableState<T> : State<T> {
+                override var value: T
+            }
+
+            interface IntState : State<Int> {
+                @get:AutoboxingStateValueProperty("intValue")
+                override val value: Int
+                    get() = intValue
+
+                val intValue: Int
+            }
+
+            interface MutableIntState : IntState, MutableState<Int> {
+                @get:AutoboxingStateValueProperty("intValue")
+                @set:AutoboxingStateValueProperty("intValue")
+                override var value: Int
+                    @Suppress("AutoBoxing") get() = intValue
+                    set(value) { intValue = value }
+
+                override var intValue: Int
+            }
+            """,
+            """
+                META-INF/main.kotlin_module:
+                H4sIAAAAAAAA/2NgYGBmYGBgBGIOBijg0uaSSMxLKcrPTKnQS87PLcgvTtUr
+                Ks0rycxNFeIPzkssKM7ILwkuSSxJ9S7h0uFSwqVYLyczr0SvJLW4RIgtBEh6
+                lygxaDEAAJG2tCtzAAAA
+            """,
+            """
+                androidx/compose/runtime/IntState$DefaultImpls.class:
+                H4sIAAAAAAAA/4VTy27TQBQ9E7d1SAxpS1sIhUJpgKSgGiQWSF1FRUiWTFoR
+                lA2rSTJJJ7FnovE4Cn/FEljwAXwU4tpJKVBQFnMfZ+6c+7K///j6DcBLPGM4
+                4qpvtOzP/J6OJzoRvkmVlbHwA2XblltRey0GPI1sEE+ixAVjWB/xKfcjrob+
+                aXcketaFw1AcCtvhUSoYXtXDpbTHjfCShkAxFOaYofn/l4nik+Rc28RvplZ3
+                9UyqYU6VZz0zeiKM/Ugc2xMjBsIY0b8AWzymsopSXVR4EGoz9EfCdg2XKvG5
+                UpqopCa7pW0rjSIi2l/ahYsyQ5k6D35RO/VG4OE6bpThocKwcaVNFxsM7jQL
+                Px0w7NSDf83Cw01slbCJbYbVmj2XSVb28rlSxnCsbSSV/1ZY3ueWE1aIpw7t
+                nGWimAkwsHFm0OoKM5lZzxm835dNm24vZp5TH40tw8qJ7lOXlVAq0UrjrjDv
+                eTciZDPUPR51uJGZvwB3383LC9RUJpKg5uWgGWp/355xQ5uywvwR5gVKCXMS
+                8SQR5JbaOjU98UZmCaoLis4VerxAASuYt3oNq1iDg1r+3VPrpEuHX7DO8Bk7
+                n8gr4BHJtfxmC49JevMo3MJt0k/oVCiK/gAUEyIsEVCn42ZsmeOgkcfTPOnt
+                Q1RxmPMe4CnpPcKrFHPnA5wAuwHuBriHvQD38SDA/k8Blj2alAMAAA==
+            """,
+            """
+                androidx/compose/runtime/IntState.class:
+                H4sIAAAAAAAA/4WS304TQRTGv9n+2xaEpYJCUQRBLTdsJV6YYEyIBrNJraRN
+                kISraTutW7YzzcxsU+94Ch/ACx/CC0O49KGMZ1tQomIvZuacM+f7zZmZ8/3H
+                128AnqHMsMFlW6uwPfJbqj9QRvg6ljbsCz+QtmG5FTkwhjfVHh9yP+Ky679r
+                9kTL7lVvVI5lL64pCCW6Qu+93GPw/gTlkGZY+z8shyyD2xX2iEexYFgqb/8D
+                z7B/c1FG8oH5oKzx92OrmmoUyu6YPUYeajUQ2n4kxtJAi47QWrSvgjXepzPd
+                UF4dv1lVuuv3hG1qHkrjcykVoUJFdk3ZWhxFBJqheoNfmlR5O2BYqJ4qG4XS
+                fyssb3PLKc/pD1P0HSyZ3GQCAzul+ChMvApZ7acMh+dnXsFZdibDcwuOm3U7
+                y+dnu24xXXQqToXVi16qRNbxxZf0xedstpR2016mvuhlk+jz98cXn67iOc9N
+                uLvJZaa2ABW5PuW7KYXKzgwnl92Zitx6LTo8jmzQH0QmhwcMs9cj1CeNyw8b
+                p++cWobV+gQSyGFowmYk9n+/O+kDKYV+FXFjBLn5RtiV3Maayik0VKxb4iCM
+                yFm5pBz9xaAec5Cha+To2R3qShd58jbJm6edAtkzBrO4haRl5yhIBrZoTrbW
+                KWUDK3iUiJHC4/H6EE9oPaB9j9ALJ0gFKAa4HWARS2TiToC7WD4BM6QtnSBv
+                sGpwz+A+HWUwZzBvsGbgGuR/AkgwZTK5AwAA
+            """,
+            """
+                androidx/compose/runtime/MutableIntState$DefaultImpls.class:
+                H4sIAAAAAAAA/5VT205TQRRd0wKF9igXBUUEVKq2VXu84BMmpsGYTFIKEdMX
+                n6btUAZOZ5o5cxr8K30TH/wAP8q457RcBE3wYe4ra+21956fv77/ALCOdYbX
+                QnesUZ2jsG16fRPL0CbaqZ4MtxInWpHk2u064WTxndwTSeR4rx/FOTCGmQMx
+                EGEkdDfcbh3ItsshyzDZla4pokQyvC3Vr8q+Ua6fsdGl7Eq7wVD7N0GsRT/e
+                Ny4Oa4kzLXOkdDelSsV3rOlL6z4Tx3zfyj1preycXDZEj6KbVPok0LW6sd3w
+                QLqWFUrHodDaEJUytG8Y10iiiIhKVzWTQ4GhQHngpwrZUpkHuIbrBQSYZpi9
+                5DaHWYbcwMO39xgWSvxvKQlwAzfzmMM8w3jR7auYoXL1LJPr+LQ81f8oDy83
+                yVJ83tIYRdgMsIS73tMyxTMYPjBO/uqHxkVKh1vSiY5wgqQzvUGW2o75adJP
+                IOyh31DbZI6U3z1nCM43GnXZ7qjQaSDVQ0fKm6ZDOtN1pWUj6bWk/eiDZZir
+                m7aImsIqfx5dLn0YeuJ6oGJFV7Wz6jIUL77uCEvt4aT9AxZwraXdjEQcSzrm
+                d01i2/K98gKLI4rmJXq8QAZj3imtUxjHBLJ4mn49sk5rvnKMGYZvWPiSYp7R
+                PJG+LKNKczBE4RZu0xrSmCYU/T6qIxHmfcZo5DybP0xhEXdGGm8ImfGpriwd
+                YyWDrxcUVlKFhSFmpOB3q7hH716rQOuplteZyox0qGTkzUdGlaMoK6T7MuV/
+                glcpN8N9wjz4hCzHGkeR4yEecTxGiaP8GxKjsqaBBAAA
+            """,
+            """
+                androidx/compose/runtime/MutableIntState.class:
+                H4sIAAAAAAAA/41T3U4TQRg9sy3d7VJxKaAF/ONPWlS2NpqYYIxEY7JJqaZN
+                gISrKR3qwna27sw2eMdT+ABe+BBeGMKlD2X8trRAVISLmfl+zpzzzcw3P399
+                /wHgGVYZily2otBvHbq7YacbKuFGsdR+R7gbsebNQHhSNzTXwgRj+FTd5z3u
+                Bly23ffNfbGr16qXEgx3/gcy0OjDXl7gpq2iLaK1V2sMzp+SJtIMc1fKmsgw
+                LF1L2oTFYLWF3uRBLBimiqV/FMOwfvlJlORd9THUyl2PddgMD33Z7nP3KT9E
+                YVdE+jNxTHUjsSeiSLSGwRrvkKbly6H8QjWM2u6+0M2I+1K5XMqQqPyQ7Fqo
+                a3EQEJGlzupNF73SJsMoncA7Y0kVSx7F1MXYePUg1IEv3Q2heYtrTjxGp5ei
+                bmDJZCUTGNgBxQ/9xCuT1XrKII+PJm2jYJwPx7INK2vtFY6PKkaZVax8Om+U
+                U2WjXnDSM2Rtn3wbO/mayc2krREnM5+2TMeqLzrZJFfJnK4vtl5vbZ98GeJs
+                Z5RwOcdKVCsMK1f2zlmXJfd2dS8yLF+vHQlJFzHSO72459ctY/Gt2ONxoL1O
+                N1AmlhhyFyPUzo1Bp/ThqweaYbZ+yuXJnq98Ils/f3Da70kpojcBV0qQm234
+                bcl1HFFVdiOMo13xzg/ImR6wbP7FQf/AwAgNE1l60TR9HxujdLoSeTcpniP7
+                hsIYOcnfcjA+SCagYZISeUz0AZMEIAMrNNuUfkgcy5jGI/INpPC4vxbxhNY6
+                5adI/dYOUh5ueyh4hJwhE7Me7uDuDpjCPdzfSVQeKMwpzCsskKJCXmFCYVJh
+                XGFRwVbUz3B+A5uahrjABAAA
+            """,
+            """
+                androidx/compose/runtime/MutableState.class:
+                H4sIAAAAAAAA/4VR0WoTURA9c3eT3aQxbmOradRaBTHxwa3FBzGlIKIYSBCa
+                EIQ83SZrvM1mt+TeDX3cb/HBj/BBlj76UeJsKkUM1Zc7c+aeOTOc+fHz23cA
+                L/CQ8FhGk0WsJuf+OJ6fxTrwF0lk1Dzwe4mRJ2HQN9IEDojQOxy86p7KpfRD
+                GU39Dyenwdi0j9ZL3Ws1V2KHg0H7qE3w/m50YBN2/93soEhwp4EZyjAJCFvN
+                1voChEKzxVOYqa+Y2811YmtIKDaZmSeb3VlsQhX5vcDIiTSS+8V8abFVlD9u
+                /oBAM66fqxztczZ5TnibpdWyqItylq6CcAvup3qWPrXdLPXowK3ZNfGe9sVx
+                3bMa4mWWfrz4Wr34Uqw0bNf2Co9st+g5udgB4cn1/v15E96OBoS9/7idm7G8
+                dMDrR/JMf47N6uPZzBBKfTWNpEkW/F3ux8liHLxTIYOd40uRodKKJ76Oopib
+                VBxp9l+gwD44bIDgi7koMdrNEcqMN1C5wjdg/c4sPFjF+9jj+IYZVVa5OYLV
+                gdfBZgc13OIUWx1s4/YIpHEH9RGfEDsaDY27Gvd0DksaGxqVX4ySaL/HAgAA
+            """,
+            """
+                androidx/compose/runtime/SnapshotStateKt.class:
+                H4sIAAAAAAAA/5WSXW/TMBSGX6efC2XLCoO142sbsA4Jsk1IXAwhIQRSRNZK
+                C1RCvXIbU9ymdpQ4VS/7r5BAgl7zoxB2WqmaEBfcvOf4PY+P7ZP8+v3tB4Dn
+                OCJoUREmkoczdyAnsUyZm2RC8QlzA0Hj9ItUgaKKvVcVEAJnRKfUjagYup3+
+                iA20WyDYnmSK9iPmiSXc+UzwtOUd+//sfXF1wznBoS+ToTtiqp9QLlKXCiF1
+                iUudt6VqZ1GkqaP1+R9FmsWxTBQLOzFLcvTtbMBik1RQJSgFKuvvV2ETlF9y
+                wdUrgp2Wv24RqISL4flxt4YartvYwCZBzZCcRl0aZYyAePp9/liqiAv3gika
+                UkX1TazJtKBnSIxUjUCzY5NYujjjJjvRWXhKcLCY1+zF3LZ2rTw41eams5g3
+                rRNyVnYsHQuGPCN48j8j0yc6Vz7Ss7EiKL6Rob73ls8Fa2eTPks+mH0EdV8O
+                zLMSbtYrc+9y2d4TU55ybb1ez53ADmSWDNg7btDGCu3+BeIUFopYDqGBEsp6
+                vZ//YQXtAPZ3bHyqX/uKrZ9mQDjQWs4rFRxqrS0pONjW8eGqWsnZR7k+wGMd
+                X2i3rvvf6KHg4aaHHQ+3cNvDLhoemtjrgaS4g7s9FFOUUtxL4aS4/wetLc8s
+                8QIAAA==
+            """,
+            """
+                androidx/compose/runtime/State.class:
+                H4sIAAAAAAAA/31Qy0rDQBQ9k6RpjK/4bquIy+rCVHEhvsCNUKgItojQ1diO
+                dWw6KZ1pcZlvceFHuJDg0o8Sb1pXKm7uvefce+7r4/P1DcABNhg2uWoPYtl+
+                Cltxrx9rEQ6GysieCOuGG5EHYyifNI5qj3zEw4irTnh19yha5vjsN8UQ/OTy
+                cBi8jjA3PBoKhuXy9l+6XHm70SC/UOvGJpIqvBSGt7nhxFm9kU3rssx4mQED
+                6xL/JDNUoai9x3CUJnO+VbD8NPGtIDOe7d0X0mTH8dIkYPtWxbpeDOySdZgm
+                t+8vzvuz65YczwlyWYd9hq3a/8+gXViDZeNzo8k1QV3xvn6IzTi/2zUMU3XZ
+                UdwMB5T26/Fw0BIXMiJQvJ70upFa3kXiXKmYRDJW2qX5yGF8GX3LBX0dRUIW
+                PNjfkY3S2BewTv6UKqZI4zdhVzFdxUwVs5ijEPNVBFhogmksYqkJV2NZY0Vj
+                VWNNZzD/Bf5sje8BAgAA
+                """
         )
     }
 }
diff --git a/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/RememberSaveable.kt b/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/RememberSaveable.kt
index a2c50c1..b7818d3 100644
--- a/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/RememberSaveable.kt
+++ b/compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/RememberSaveable.kt
@@ -17,15 +17,15 @@
 package androidx.compose.runtime.saveable
 
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.RememberObserver
+import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.SnapshotMutationPolicy
 import androidx.compose.runtime.currentCompositeKeyHash
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.neverEqualPolicy
 import androidx.compose.runtime.referentialEqualityPolicy
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.runtime.snapshots.SnapshotMutableState
 import androidx.compose.runtime.structuralEqualityPolicy
 
@@ -69,46 +69,32 @@
     key: String? = null,
     init: () -> T
 ): T {
+    val compositeKey = currentCompositeKeyHash
     // key is the one provided by the user or the one generated by the compose runtime
     val finalKey = if (!key.isNullOrEmpty()) {
         key
     } else {
-        currentCompositeKeyHash.toString(MaxSupportedRadix)
+        compositeKey.toString(MaxSupportedRadix)
     }
     @Suppress("UNCHECKED_CAST")
     (saver as Saver<T, Any>)
 
     val registry = LocalSaveableStateRegistry.current
-    // value is restored using the registry or created via [init] lambda
-    val value = remember(*inputs) {
-        // TODO not restore when the input values changed (use hashKeys?) b/152014032
+
+    val holder = remember {
+        // value is restored using the registry or created via [init] lambda
         val restored = registry?.consumeRestored(finalKey)?.let {
             saver.restore(it)
         }
-        restored ?: init()
+        val finalValue = restored ?: init()
+        SaveableHolder(saver, registry, finalKey, finalValue, inputs)
     }
 
-    // re-register if the registry or key has been changed
-    if (registry != null) {
-        // we want to use the latest instances of saver and value in the valueProvider lambda
-        // without restarting DisposableEffect as it would cause re-registering the provider in
-        // the different order. so we use rememberUpdatedState.
-        val saverState = rememberUpdatedState(saver)
-        val valueState = rememberUpdatedState(value)
-
-        DisposableEffect(registry, finalKey) {
-            val valueProvider = {
-                with(saverState.value) {
-                    SaverScope { registry.canBeSaved(it) }.save(valueState.value)
-                }
-            }
-            registry.requireCanBeSaved(valueProvider())
-            val entry = registry.registerProvider(finalKey, valueProvider)
-            onDispose {
-                entry.unregister()
-            }
-        }
+    val value = holder.getValueIfInputsDidntChange(inputs) ?: init()
+    SideEffect {
+        holder.update(saver, registry, finalKey, value, inputs)
     }
+
     return value
 }
 
@@ -146,6 +132,83 @@
     init = init
 )
 
+private class SaveableHolder<T>(
+    private var saver: Saver<T, Any>,
+    private var registry: SaveableStateRegistry?,
+    private var key: String,
+    private var value: T,
+    private var inputs: Array<out Any?>
+) : () -> Any?, SaverScope, RememberObserver {
+    private var entry: SaveableStateRegistry.Entry? = null
+
+    fun update(
+        saver: Saver<T, Any>,
+        registry: SaveableStateRegistry?,
+        key: String,
+        value: T,
+        inputs: Array<out Any?>
+    ) {
+        var entryIsOutdated = false
+        if (this.registry !== registry) {
+            this.registry = registry
+            entryIsOutdated = true
+        }
+        if (this.key != key) {
+            this.key = key
+            entryIsOutdated = true
+        }
+        this.saver = saver
+        this.value = value
+        this.inputs = inputs
+        if (entry != null && entryIsOutdated) {
+            entry?.unregister()
+            entry = null
+            register()
+        }
+    }
+
+    private fun register() {
+        val registry = registry
+        require(entry == null) { "entry($entry) is not null" }
+        if (registry != null) {
+            registry.requireCanBeSaved(invoke())
+            entry = registry.registerProvider(key, this)
+        }
+    }
+
+    /**
+     * Value provider called by the registry.
+     */
+    override fun invoke(): Any? = with(saver) {
+        save(requireNotNull(value) { "Value should be initialized" })
+    }
+
+    override fun canBeSaved(value: Any): Boolean {
+        val registry = registry
+        return registry == null || registry.canBeSaved(value)
+    }
+
+    override fun onRemembered() {
+        register()
+    }
+
+    override fun onForgotten() {
+        entry?.unregister()
+    }
+
+    override fun onAbandoned() {
+        entry?.unregister()
+    }
+
+    fun getValueIfInputsDidntChange(inputs: Array<out Any?>): T? {
+        return if (inputs.contentEquals(this.inputs)) {
+            value
+        } else {
+            null
+        }
+    }
+}
+
 @Suppress("UNCHECKED_CAST")
 private fun <T> mutableStateSaver(inner: Saver<T, out Any>) = with(inner as Saver<T, Any>) {
     Saver<MutableState<T>, MutableState<Any?>>(
diff --git a/compose/runtime/runtime/api/current.txt b/compose/runtime/runtime/api/current.txt
index 8137882..5fdddbb 100644
--- a/compose/runtime/runtime/api/current.txt
+++ b/compose/runtime/runtime/api/current.txt
@@ -294,7 +294,7 @@
 
   @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface DoubleState extends androidx.compose.runtime.State<java.lang.Double> {
     method public double getDoubleValue();
-    method public default Double getValue();
+    method @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="doubleValue") public default Double getValue();
     property public abstract double doubleValue;
     property @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="doubleValue") public default Double value;
   }
@@ -310,7 +310,7 @@
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void LaunchedEffect(Object? key1, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void LaunchedEffect(Object![]? keys, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
     method @Deprecated @androidx.compose.runtime.Composable public static void LaunchedEffect(kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void SideEffect(kotlin.jvm.functions.Function0<kotlin.Unit> effect);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ExplicitGroupsComposable @androidx.compose.runtime.NonRestartableComposable public static void SideEffect(kotlin.jvm.functions.Function0<kotlin.Unit> effect);
     method @androidx.compose.runtime.Composable public static inline kotlinx.coroutines.CoroutineScope rememberCoroutineScope(optional kotlin.jvm.functions.Function0<? extends kotlin.coroutines.CoroutineContext> getContext);
   }
 
@@ -325,7 +325,7 @@
 
   @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface FloatState extends androidx.compose.runtime.State<java.lang.Float> {
     method public float getFloatValue();
-    method public default Float getValue();
+    method @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="floatValue") public default Float getValue();
     property public abstract float floatValue;
     property @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="floatValue") public default Float value;
   }
@@ -335,7 +335,7 @@
 
   @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface IntState extends androidx.compose.runtime.State<java.lang.Integer> {
     method public int getIntValue();
-    method public default Integer getValue();
+    method @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="intValue") public default Integer getValue();
     property public abstract int intValue;
     property @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="intValue") public default Integer value;
   }
@@ -348,7 +348,7 @@
 
   @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface LongState extends androidx.compose.runtime.State<java.lang.Long> {
     method public long getLongValue();
-    method public default Long getValue();
+    method @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="longValue") public default Long getValue();
     property public abstract long longValue;
     property @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="longValue") public default Long value;
   }
@@ -396,28 +396,28 @@
 
   @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface MutableDoubleState extends androidx.compose.runtime.DoubleState androidx.compose.runtime.MutableState<java.lang.Double> {
     method public void setDoubleValue(double);
-    method public default void setValue(double);
+    method @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="doubleValue") public default void setValue(double);
     property public abstract double doubleValue;
     property @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="doubleValue") public default Double value;
   }
 
   @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface MutableFloatState extends androidx.compose.runtime.FloatState androidx.compose.runtime.MutableState<java.lang.Float> {
     method public void setFloatValue(float);
-    method public default void setValue(float);
+    method @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="floatValue") public default void setValue(float);
     property public abstract float floatValue;
     property @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="floatValue") public default Float value;
   }
 
   @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface MutableIntState extends androidx.compose.runtime.IntState androidx.compose.runtime.MutableState<java.lang.Integer> {
     method public void setIntValue(int);
-    method public default void setValue(int);
+    method @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="intValue") public default void setValue(int);
     property public abstract int intValue;
     property @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="intValue") public default Integer value;
   }
 
   @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface MutableLongState extends androidx.compose.runtime.LongState androidx.compose.runtime.MutableState<java.lang.Long> {
     method public void setLongValue(long);
-    method public default void setValue(long);
+    method @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="longValue") public default void setValue(long);
     property public abstract long longValue;
     property @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="longValue") public default Long value;
   }
@@ -803,7 +803,7 @@
 
 package androidx.compose.runtime.snapshots {
 
-  @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.PROPERTY) public @interface AutoboxingStateValueProperty {
+  @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.PROPERTY_SETTER, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER}) public @interface AutoboxingStateValueProperty {
     method public abstract String preferredPropertyName();
     property public abstract String preferredPropertyName;
   }
diff --git a/compose/runtime/runtime/api/restricted_current.txt b/compose/runtime/runtime/api/restricted_current.txt
index 84dee7f..4a0f6ac 100644
--- a/compose/runtime/runtime/api/restricted_current.txt
+++ b/compose/runtime/runtime/api/restricted_current.txt
@@ -321,7 +321,7 @@
 
   @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface DoubleState extends androidx.compose.runtime.State<java.lang.Double> {
     method public double getDoubleValue();
-    method public default Double getValue();
+    method @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="doubleValue") public default Double getValue();
     property public abstract double doubleValue;
     property @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="doubleValue") public default Double value;
   }
@@ -337,7 +337,7 @@
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void LaunchedEffect(Object? key1, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void LaunchedEffect(Object![]? keys, kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
     method @Deprecated @androidx.compose.runtime.Composable public static void LaunchedEffect(kotlin.jvm.functions.Function2<? super kotlinx.coroutines.CoroutineScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> block);
-    method @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable public static void SideEffect(kotlin.jvm.functions.Function0<kotlin.Unit> effect);
+    method @androidx.compose.runtime.Composable @androidx.compose.runtime.ExplicitGroupsComposable @androidx.compose.runtime.NonRestartableComposable public static void SideEffect(kotlin.jvm.functions.Function0<kotlin.Unit> effect);
     method @kotlin.PublishedApi internal static kotlinx.coroutines.CoroutineScope createCompositionCoroutineScope(kotlin.coroutines.CoroutineContext coroutineContext, androidx.compose.runtime.Composer composer);
     method @androidx.compose.runtime.Composable public static inline kotlinx.coroutines.CoroutineScope rememberCoroutineScope(optional kotlin.jvm.functions.Function0<? extends kotlin.coroutines.CoroutineContext> getContext);
   }
@@ -357,7 +357,7 @@
 
   @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface FloatState extends androidx.compose.runtime.State<java.lang.Float> {
     method public float getFloatValue();
-    method public default Float getValue();
+    method @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="floatValue") public default Float getValue();
     property public abstract float floatValue;
     property @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="floatValue") public default Float value;
   }
@@ -367,7 +367,7 @@
 
   @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface IntState extends androidx.compose.runtime.State<java.lang.Integer> {
     method public int getIntValue();
-    method public default Integer getValue();
+    method @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="intValue") public default Integer getValue();
     property public abstract int intValue;
     property @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="intValue") public default Integer value;
   }
@@ -380,7 +380,7 @@
 
   @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface LongState extends androidx.compose.runtime.State<java.lang.Long> {
     method public long getLongValue();
-    method public default Long getValue();
+    method @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="longValue") public default Long getValue();
     property public abstract long longValue;
     property @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="longValue") public default Long value;
   }
@@ -428,28 +428,28 @@
 
   @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface MutableDoubleState extends androidx.compose.runtime.DoubleState androidx.compose.runtime.MutableState<java.lang.Double> {
     method public void setDoubleValue(double);
-    method public default void setValue(double);
+    method @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="doubleValue") public default void setValue(double);
     property public abstract double doubleValue;
     property @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="doubleValue") public default Double value;
   }
 
   @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface MutableFloatState extends androidx.compose.runtime.FloatState androidx.compose.runtime.MutableState<java.lang.Float> {
     method public void setFloatValue(float);
-    method public default void setValue(float);
+    method @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="floatValue") public default void setValue(float);
     property public abstract float floatValue;
     property @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="floatValue") public default Float value;
   }
 
   @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface MutableIntState extends androidx.compose.runtime.IntState androidx.compose.runtime.MutableState<java.lang.Integer> {
     method public void setIntValue(int);
-    method public default void setValue(int);
+    method @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="intValue") public default void setValue(int);
     property public abstract int intValue;
     property @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="intValue") public default Integer value;
   }
 
   @androidx.compose.runtime.Stable @kotlin.jvm.JvmDefaultWithCompatibility public interface MutableLongState extends androidx.compose.runtime.LongState androidx.compose.runtime.MutableState<java.lang.Long> {
     method public void setLongValue(long);
-    method public default void setValue(long);
+    method @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="longValue") public default void setValue(long);
     property public abstract long longValue;
     property @androidx.compose.runtime.snapshots.AutoboxingStateValueProperty(preferredPropertyName="longValue") public default Long value;
   }
@@ -841,7 +841,7 @@
 
 package androidx.compose.runtime.snapshots {
 
-  @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.PROPERTY) public @interface AutoboxingStateValueProperty {
+  @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.PROPERTY_SETTER, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER}) public @interface AutoboxingStateValueProperty {
     method public abstract String preferredPropertyName();
     property public abstract String preferredPropertyName;
   }
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Effects.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Effects.kt
index cfb0359..080ee94 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Effects.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Effects.kt
@@ -42,6 +42,7 @@
  */
 @Composable
 @NonRestartableComposable
+@ExplicitGroupsComposable
 @OptIn(InternalComposeApi::class)
 fun SideEffect(
     effect: () -> Unit
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotDoubleState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotDoubleState.kt
index 3868163..6ec8ca2 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotDoubleState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotDoubleState.kt
@@ -63,7 +63,7 @@
 @Stable
 @JvmDefaultWithCompatibility
 interface DoubleState : State<Double> {
-    @AutoboxingStateValueProperty("doubleValue")
+    @get:AutoboxingStateValueProperty("doubleValue")
     override val value: Double
         @Suppress("AutoBoxing") get() = doubleValue
 
@@ -92,7 +92,8 @@
 @Stable
 @JvmDefaultWithCompatibility
 interface MutableDoubleState : DoubleState, MutableState<Double> {
-    @AutoboxingStateValueProperty("doubleValue")
+    @get:AutoboxingStateValueProperty("doubleValue")
+    @set:AutoboxingStateValueProperty("doubleValue")
     override var value: Double
         @Suppress("AutoBoxing") get() = doubleValue
         set(value) { doubleValue = value }
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFloatState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFloatState.kt
index 7158d09..1490f39 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFloatState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotFloatState.kt
@@ -62,7 +62,7 @@
 @Stable
 @JvmDefaultWithCompatibility
 interface FloatState : State<Float> {
-    @AutoboxingStateValueProperty("floatValue")
+    @get:AutoboxingStateValueProperty("floatValue")
     override val value: Float
         @Suppress("AutoBoxing") get() = floatValue
 
@@ -88,7 +88,8 @@
 @Stable
 @JvmDefaultWithCompatibility
 interface MutableFloatState : FloatState, MutableState<Float> {
-    @AutoboxingStateValueProperty("floatValue")
+    @get:AutoboxingStateValueProperty("floatValue")
+    @set:AutoboxingStateValueProperty("floatValue")
     override var value: Float
         @Suppress("AutoBoxing") get() = floatValue
         set(value) { floatValue = value }
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotIntState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotIntState.kt
index e43f884..137cbf7 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotIntState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotIntState.kt
@@ -61,7 +61,7 @@
 @Stable
 @JvmDefaultWithCompatibility
 interface IntState : State<Int> {
-    @AutoboxingStateValueProperty("intValue")
+    @get:AutoboxingStateValueProperty("intValue")
     override val value: Int
         @Suppress("AutoBoxing") get() = intValue
 
@@ -87,7 +87,8 @@
 @Stable
 @JvmDefaultWithCompatibility
 interface MutableIntState : IntState, MutableState<Int> {
-    @AutoboxingStateValueProperty("intValue")
+    @get:AutoboxingStateValueProperty("intValue")
+    @set:AutoboxingStateValueProperty("intValue")
     override var value: Int
         @Suppress("AutoBoxing") get() = intValue
         set(value) { intValue = value }
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotLongState.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotLongState.kt
index a22c5c4..bde2f06 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotLongState.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SnapshotLongState.kt
@@ -62,7 +62,7 @@
 @Stable
 @JvmDefaultWithCompatibility
 interface LongState : State<Long> {
-    @AutoboxingStateValueProperty("longValue")
+    @get:AutoboxingStateValueProperty("longValue")
     override val value: Long
         @Suppress("AutoBoxing") get() = longValue
 
@@ -88,7 +88,8 @@
 @Stable
 @JvmDefaultWithCompatibility
 interface MutableLongState : LongState, MutableState<Long> {
-    @AutoboxingStateValueProperty("longValue")
+    @get:AutoboxingStateValueProperty("longValue")
+    @set:AutoboxingStateValueProperty("longValue")
     override var value: Long
         @Suppress("AutoBoxing") get() = longValue
         set(value) { longValue = value }
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/changelist/Operation.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/changelist/Operation.kt
index 8aec129..9cbcd48 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/changelist/Operation.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/changelist/Operation.kt
@@ -48,7 +48,7 @@
 ) {
 
     val name: String
-        get() = javaClass.simpleName
+        get() = this::class.simpleName.orEmpty()
 
     abstract fun OperationArgContainer.execute(
         applier: Applier<*>,
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/AutoboxingStateValueProperty.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/AutoboxingStateValueProperty.kt
index 3f3a814..b81a5e5 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/AutoboxingStateValueProperty.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/AutoboxingStateValueProperty.kt
@@ -27,7 +27,7 @@
  * instead.
  */
 @Retention(AnnotationRetention.BINARY)
-@Target(AnnotationTarget.PROPERTY)
+@Target(AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.PROPERTY_GETTER)
 annotation class AutoboxingStateValueProperty(
     /**
      * An alternative, non-boxing property that can be used instead of the annotated property.
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionTests.kt
index f33bb2b..8f8615b 100644
--- a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionTests.kt
+++ b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionTests.kt
@@ -3865,6 +3865,65 @@
         }
     }
 
+    @Stable
+    class VarargConsumer(var invokeCount: Int = 0) {
+        @Composable fun Varargs(vararg ints: Int) {
+            invokeCount++
+            for (i in ints) {
+                use(i)
+            }
+        }
+    }
+
+    // Regression test for b/286132194
+    @Test
+    fun composableVarargs_skipped() = compositionTest {
+        val consumer = VarargConsumer()
+        var recomposeTrigger by mutableStateOf(0)
+        compose {
+            Linear {
+                use(recomposeTrigger)
+                consumer.Varargs(0, 1, 2, 3)
+            }
+        }
+
+        assertEquals(1, consumer.invokeCount)
+
+        recomposeTrigger = 1
+        advance()
+
+        assertEquals(1, consumer.invokeCount)
+    }
+
+    fun interface TestFunInterface {
+        fun compute(value: Int)
+    }
+
+    @Composable fun TestMemoizedFun(compute: TestFunInterface) {
+        val oldCompute = remember { compute }
+        assertEquals(oldCompute, compute)
+    }
+
+    @Test
+    fun funInterface_isMemoized() = compositionTest {
+        var recomposeTrigger by mutableStateOf(0)
+        val capture = 0
+        compose {
+            use(recomposeTrigger)
+            TestMemoizedFun {
+                // no captures
+                use(it)
+            }
+            TestMemoizedFun {
+                // stable captures
+                use(capture)
+            }
+        }
+
+        recomposeTrigger++
+        advance()
+    }
+
     private inline fun CoroutineScope.withGlobalSnapshotManager(block: CoroutineScope.() -> Unit) {
         val channel = Channel<Unit>(Channel.CONFLATED)
         val job = launch {
diff --git a/compose/test-utils/build.gradle b/compose/test-utils/build.gradle
index 582e745..4331b6f 100644
--- a/compose/test-utils/build.gradle
+++ b/compose/test-utils/build.gradle
@@ -107,7 +107,6 @@
         if (desktopEnabled) {
             desktopTest {
                 dependsOn(jvmTest)
-                dependsOn(desktopMain)
                 dependencies {
                 }
             }
diff --git a/compose/test-utils/src/commonMain/kotlin/androidx/compose/testutils/Expect.kt b/compose/test-utils/src/commonMain/kotlin/androidx/compose/testutils/Expect.kt
index 2ec325a..8c2e559 100644
--- a/compose/test-utils/src/commonMain/kotlin/androidx/compose/testutils/Expect.kt
+++ b/compose/test-utils/src/commonMain/kotlin/androidx/compose/testutils/Expect.kt
@@ -48,7 +48,7 @@
     expectedMessage: String = ".*",
     block: () -> Unit
 ) {
-    val expectedClassName = T::class.java.simpleName
+    val expectedClassName = T::class.simpleName
     try {
         block()
     } catch (thrown: Throwable) {
@@ -88,7 +88,7 @@
     }
 
     val expected = expectedClassName?.let { "a $it".plusMessage(expectedMessage) } ?: "nothing"
-    val actual = thrown?.run { "a ${javaClass.simpleName}".plusMessage(message) } ?: "nothing"
+    val actual = thrown?.run { "a ${this::class.simpleName}".plusMessage(message) } ?: "nothing"
     throw AssertionError(
         "Expected that $expected would be thrown, but $actual was thrown$stackTrace"
     )
diff --git a/compose/ui/ui-graphics/lint-baseline.xml b/compose/ui/ui-graphics/lint-baseline.xml
index 7a307a3..51329f0 100644
--- a/compose/ui/ui-graphics/lint-baseline.xml
+++ b/compose/ui/ui-graphics/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.1.0-beta02" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-beta02)" variant="all" version="8.1.0-beta02">
+<issues format="6" by="lint 8.1.0-beta05" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-beta05)" variant="all" version="8.1.0-beta05">
 
     <issue
         id="PrimitiveInLambda"
diff --git a/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/ComposeViewTest.kt b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/ComposeViewTest.kt
index a6321c17..e93096e 100644
--- a/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/ComposeViewTest.kt
+++ b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/ComposeViewTest.kt
@@ -21,6 +21,7 @@
 import androidx.compose.ui.inspection.testdata.ComposeViewTestActivity
 import androidx.compose.ui.inspection.util.GetComposablesCommand
 import androidx.compose.ui.inspection.util.GetParametersByIdCommand
+import androidx.compose.ui.inspection.util.GetUpdateSettingsCommand
 import androidx.compose.ui.inspection.util.flatten
 import androidx.compose.ui.inspection.util.toMap
 import androidx.test.filters.LargeTest
@@ -37,6 +38,10 @@
 
     @Test
     fun composeView(): Unit = runBlocking {
+        rule.inspectorTester.sendCommand(
+            GetUpdateSettingsCommand(reduceChildNesting = true)
+        ).updateSettingsResponse
+
         val response = rule.inspectorTester.sendCommand(
             GetComposablesCommand(rule.rootId, skipSystemComposables = false)
         ).getComposablesResponse
@@ -49,6 +54,17 @@
         assertThat(firstText?.textParameter).isEqualTo("one")
         assertThat(secondText?.textParameter).isEqualTo("two")
         assertThat(thirdText?.textParameter).isEqualTo("three")
+
+        val nested1 = roots[2].nodesList.single()
+        assertThat(strings[nested1.name]).isEqualTo("Nested")
+        assertThat(nested1.flags).isEqualTo(ComposableNode.Flags.NESTED_SINGLE_CHILDREN_VALUE)
+        assertThat(nested1.childrenList.size).isAtLeast(3)
+        val nested2 = nested1.childrenList[0]
+        assertThat(nested2.name).isEqualTo(nested1.name)
+        val nested3 = nested1.childrenList[1]
+        assertThat(nested3.name).isEqualTo(nested1.name)
+        val nested4 = nested1.childrenList[2]
+        assertThat(nested4.name).isEqualTo(thirdText?.name)
     }
 
     private fun Iterable<ComposableNode>.findNode(
diff --git a/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/ParametersTest.kt b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/ParametersTest.kt
index bc87381..2e9c992 100644
--- a/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/ParametersTest.kt
+++ b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/ParametersTest.kt
@@ -280,14 +280,7 @@
         var column = composables.filter("Column").first()
         var text = column.childrenList.single { strings[it.name] == "Text" }
 
-        var paramsById = rule.inspectorTester.sendCommand(
-            GetParametersByIdCommand(rule.rootId, text.id)
-        ).getParametersResponse
-        // We are using delayed parameter extractions so the cache does not have parameters
-        // (The code should look for an anchor but the anchor is not specified.)
-        assertThat(paramsById.parameterGroup.parameterList).isEmpty()
-
-        // But looking up by anchor will find the parameters
+        // Lookup by anchor will find the parameters
         var paramsByAnchor = rule.inspectorTester.sendCommand(
             GetParametersByAnchorIdCommand(rule.rootId, text.anchorHash, text.id)
         ).getParametersResponse
@@ -296,6 +289,17 @@
         var textValue = paramsByAnchor.find("text")
         assertThat(strings[textValue.int32Value]).isEqualTo("four")
 
+        // Lookup parameters without anchor should fallback to the older approach and return
+        // the same parameters:
+        var paramsById = rule.inspectorTester.sendCommand(
+            GetParametersByIdCommand(rule.rootId, text.id)
+        ).getParametersResponse
+        // We are using delayed parameter extractions so the cache does not have parameters
+        // (The code should look for an anchor but the anchor is not specified.)
+        assertThat(paramsById.parameterGroup.parameterList).isNotEmpty()
+        textValue = paramsById.find("text")
+        assertThat(strings[textValue.int32Value]).isEqualTo("four")
+
         val snapshot = rule.inspectorTester.sendCommand(
             GetComposablesCommand(rule.rootId, extractAllParameters = true)
         ).getComposablesResponse
diff --git a/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/testdata/ComposeViewTestActivity.kt b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/testdata/ComposeViewTestActivity.kt
index 3c36a5a..8b9c648 100644
--- a/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/testdata/ComposeViewTestActivity.kt
+++ b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/testdata/ComposeViewTestActivity.kt
@@ -23,6 +23,7 @@
 import androidx.activity.compose.setContent
 import androidx.compose.foundation.layout.Column
 import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
 import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.viewinterop.AndroidView
 
@@ -45,7 +46,13 @@
                                             orientation = LinearLayout.VERTICAL
                                             addView(ComposeView(context).apply {
                                                 setContent {
-                                                    Text("three")
+                                                    Nested {
+                                                        Nested {
+                                                            Nested {
+                                                                Text("three")
+                                                            }
+                                                        }
+                                                    }
                                                 }
                                             })
                                         }
@@ -59,3 +66,8 @@
         }
     }
 }
+
+@Composable
+fun Nested(content: @Composable () -> Unit) {
+    content()
+}
diff --git a/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/testdata/RippleTestActivity.kt b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/testdata/RippleTestActivity.kt
index 04f415b..7b0aa4a 100644
--- a/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/testdata/RippleTestActivity.kt
+++ b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/testdata/RippleTestActivity.kt
@@ -22,7 +22,9 @@
 import androidx.compose.foundation.LocalIndication
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.indication
+import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.PressInteraction
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.padding
@@ -31,14 +33,25 @@
 import androidx.compose.material.ripple.rememberRipple
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.unit.dp
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
 
 class RippleTestActivity : ComponentActivity() {
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContent {
             Column {
-                val interactionSource = remember { MutableInteractionSource() }
+                // Always pressed interaction source to trigger creating a ripple view immediately
+                val interactionSource = remember {
+                    object : MutableInteractionSource {
+                        override suspend fun emit(interaction: Interaction) {}
+                        override fun tryEmit(interaction: Interaction) = true
+                        override val interactions: Flow<Interaction>
+                            get() = flowOf(PressInteraction.Press(Offset.Zero))
+                    }
+                }
                 Text(
                     text = "Click me with indication",
                     modifier = Modifier
diff --git a/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/util/ProtoExtensions.kt b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/util/ProtoExtensions.kt
index feba40c..b709c53 100644
--- a/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/util/ProtoExtensions.kt
+++ b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/util/ProtoExtensions.kt
@@ -117,13 +117,15 @@
 fun GetUpdateSettingsCommand(
     includeRecomposeCounts: Boolean = false,
     keepRecomposeCounts: Boolean = false,
-    delayParameterExtractions: Boolean = false
+    delayParameterExtractions: Boolean = false,
+    reduceChildNesting: Boolean = false
 ): Command =
     Command.newBuilder().apply {
         updateSettingsCommand = UpdateSettingsCommand.newBuilder().apply {
             this.includeRecomposeCounts = includeRecomposeCounts
             this.keepRecomposeCounts = keepRecomposeCounts
             this.delayParameterExtractions = delayParameterExtractions
+            this.reduceChildNesting = reduceChildNesting
         }.build()
     }.build()
 
diff --git a/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/ComposeLayoutInspector.kt b/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/ComposeLayoutInspector.kt
index 8490d41..6388008 100644
--- a/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/ComposeLayoutInspector.kt
+++ b/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/ComposeLayoutInspector.kt
@@ -25,9 +25,11 @@
 import androidx.compose.ui.inspection.inspector.InspectorNode
 import androidx.compose.ui.inspection.inspector.LayoutInspectorTree
 import androidx.compose.ui.inspection.inspector.NodeParameterReference
+import androidx.compose.ui.inspection.proto.ConversionContext
 import androidx.compose.ui.inspection.proto.StringTable
 import androidx.compose.ui.inspection.proto.convert
 import androidx.compose.ui.inspection.proto.toComposableRoot
+import androidx.compose.ui.inspection.util.NO_ANCHOR_ID
 import androidx.compose.ui.inspection.util.ThreadUtils
 import androidx.compose.ui.unit.IntOffset
 import androidx.inspection.Connection
@@ -95,6 +97,9 @@
     private val layoutInspectorTree = LayoutInspectorTree()
     private val recompositionHandler = RecompositionHandler(environment.artTooling())
     private var delayParameterExtractions = false
+    // Reduce the protobuf nesting of ComposableNode by storing nested nodes with only 1 child each
+    // as children under the top node. This limits the stack used when computing the protobuf size.
+    private var reduceChildNesting = false
 
     // Sidestep threading concerns by only ever accessing cachedNodes on the inspector thread
     private val inspectorThread = Thread.currentThread()
@@ -163,8 +168,10 @@
         val windowPos = IntOffset(location[0], location[1])
 
         val stringTable = StringTable()
+        val context =
+            ConversionContext(stringTable, windowPos, recompositionHandler, reduceChildNesting)
         val trees = data?.trees ?: emptyList()
-        val roots = trees.map { it.toComposableRoot(stringTable, windowPos, recompositionHandler) }
+        val roots = trees.map { it.toComposableRoot(context) }
 
         callback.reply {
             getComposablesResponse = GetComposablesResponse.newBuilder().apply {
@@ -178,16 +185,19 @@
         getParametersCommand: GetParametersCommand,
         callback: CommandCallback
     ) {
-        val foundComposable = if (delayParameterExtractions && !cachedHasAllParameters) {
-            getComposableFromAnchor(getParametersCommand.anchorHash)
-        } else {
-            getComposableNodes(
-                getParametersCommand.rootViewId,
-                getParametersCommand.skipSystemComposables,
-                true,
-                getParametersCommand.generation
-            )?.lookup?.get(getParametersCommand.composableId)
-        }
+        val foundComposable =
+            if (delayParameterExtractions && !cachedHasAllParameters &&
+                getParametersCommand.anchorHash != NO_ANCHOR_ID
+            ) {
+                getComposableFromAnchor(getParametersCommand.anchorHash)
+            } else {
+                getComposableNodes(
+                    getParametersCommand.rootViewId,
+                    getParametersCommand.skipSystemComposables,
+                    true,
+                    getParametersCommand.generation
+                )?.lookup?.get(getParametersCommand.composableId)
+            }
         val semanticsNode = getCachedComposableNodes(
             getParametersCommand.rootViewId
         )?.lookup?.get(getParametersCommand.composableId)
@@ -256,16 +266,19 @@
             getParameterDetailsCommand.reference.parameterIndex,
             getParameterDetailsCommand.reference.compositeIndexList
         )
-        val foundComposable = if (delayParameterExtractions && !cachedHasAllParameters) {
-            getComposableFromAnchor(reference.anchorId)
-        } else {
-            getComposableNodes(
-                getParameterDetailsCommand.rootViewId,
-                getParameterDetailsCommand.skipSystemComposables,
-                true,
-                getParameterDetailsCommand.generation
-            )?.lookup?.get(reference.nodeId)
-        }
+        val foundComposable =
+            if (delayParameterExtractions && !cachedHasAllParameters &&
+                reference.anchorId != NO_ANCHOR_ID
+            ) {
+                getComposableFromAnchor(reference.anchorId)
+            } else {
+                getComposableNodes(
+                    getParameterDetailsCommand.rootViewId,
+                    getParameterDetailsCommand.skipSystemComposables,
+                    true,
+                    getParameterDetailsCommand.generation
+                )?.lookup?.get(reference.nodeId)
+            }
         val semanticsNode = getCachedComposableNodes(
             getParameterDetailsCommand.rootViewId
         )?.lookup?.get(getParameterDetailsCommand.reference.composableId)
@@ -304,6 +317,7 @@
             updateSettingsCommand.keepRecomposeCounts
         )
         delayParameterExtractions = updateSettingsCommand.delayParameterExtractions
+        reduceChildNesting = updateSettingsCommand.reduceChildNesting
         callback.reply {
             updateSettingsResponse = UpdateSettingsResponse.newBuilder().apply {
                 canDelayParameterExtractions = true
diff --git a/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/proto/ComposeExtensions.kt b/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/proto/ComposeExtensions.kt
index 91fe56b..d046f8a 100644
--- a/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/proto/ComposeExtensions.kt
+++ b/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/proto/ComposeExtensions.kt
@@ -20,14 +20,12 @@
 import androidx.annotation.VisibleForTesting
 import androidx.compose.ui.inspection.ComposeLayoutInspector.CacheTree
 import androidx.compose.ui.inspection.LambdaLocation
-import androidx.compose.ui.inspection.RecompositionHandler
 import androidx.compose.ui.inspection.inspector.InspectorNode
 import androidx.compose.ui.inspection.inspector.NodeParameter
 import androidx.compose.ui.inspection.inspector.NodeParameterReference
 import androidx.compose.ui.inspection.inspector.ParameterKind
 import androidx.compose.ui.inspection.inspector.ParameterType
 import androidx.compose.ui.inspection.inspector.systemPackages
-import androidx.compose.ui.unit.IntOffset
 import layoutinspector.compose.inspection.LayoutInspectorComposeProtocol.Bounds
 import layoutinspector.compose.inspection.LayoutInspectorComposeProtocol.ComposableNode
 import layoutinspector.compose.inspection.LayoutInspectorComposeProtocol.ComposableRoot
@@ -37,37 +35,57 @@
 import layoutinspector.compose.inspection.LayoutInspectorComposeProtocol.Quad
 import layoutinspector.compose.inspection.LayoutInspectorComposeProtocol.Rect
 
-fun InspectorNode.toComposableNode(
-    stringTable: StringTable,
-    windowPos: IntOffset,
-    recompositionHandler: RecompositionHandler
-): ComposableNode {
-    return toComposableNodeImpl(stringTable, windowPos, recompositionHandler).resetSystemFlag()
+internal fun InspectorNode.toComposableNode(context: ConversionContext): ComposableNode {
+    return toNodeBuilder(context)
+        .resetSystemFlag()
         .build()
 }
 
 private val SELECTOR_EXPR = Regex("(\\\$(lambda-)?[0-9]+)+$")
 
-private fun InspectorNode.toComposableNodeImpl(
-    stringTable: StringTable,
-    windowPos: IntOffset,
-    recompositionHandler: RecompositionHandler
-): ComposableNode.Builder {
+/**
+ * Convert an [InspectorNode] to protobuf [ComposableNode].
+ * If the reduceChildNesting option is set: store a subtree with single child nodes as children
+ * of this node and mark the node with [ComposableNode.Flags.NESTED_SINGLE_CHILDREN].
+ */
+private fun InspectorNode.toNodeBuilder(context: ConversionContext): ComposableNode.Builder {
+    val builder = toFlatNode(context)
+    if (!context.reduceChildNesting || children.size != 1) {
+        children.forEach { child ->
+            builder.addChildren(child.toNodeBuilder(context))
+        }
+    } else {
+        var nested = children.single()
+
+        while (nested.children.size == 1) {
+            builder.addChildren(nested.toFlatNode(context))
+            builder.flags = builder.flags or ComposableNode.Flags.NESTED_SINGLE_CHILDREN_VALUE
+            nested = nested.children.single()
+        }
+        builder.addChildren(nested.toNodeBuilder(context))
+    }
+    return builder
+}
+
+/**
+ * Convert an [InspectorNode] to protobuf [ComposableNode] without child nesting.
+ */
+private fun InspectorNode.toFlatNode(context: ConversionContext): ComposableNode.Builder {
     val inspectorNode = this
     return ComposableNode.newBuilder().apply {
         id = inspectorNode.id
 
         packageHash = inspectorNode.packageHash
-        filename = stringTable.put(inspectorNode.fileName)
+        filename = context.stringTable.put(inspectorNode.fileName)
         lineNumber = inspectorNode.lineNumber
         offset = inspectorNode.offset
 
-        name = stringTable.put(inspectorNode.name)
+        name = context.stringTable.put(inspectorNode.name)
 
         bounds = Bounds.newBuilder().apply {
             layout = Rect.newBuilder().apply {
-                x = inspectorNode.left + windowPos.x
-                y = inspectorNode.top + windowPos.y
+                x = inspectorNode.left + context.windowPos.x
+                y = inspectorNode.top + context.windowPos.y
                 w = inspectorNode.width
                 h = inspectorNode.height
             }.build()
@@ -87,15 +105,11 @@
 
         flags = flags()
         viewId = inspectorNode.viewId
-        recompositionHandler.getCounts(inspectorNode.key, inspectorNode.anchorId)?.let {
+        context.recompositionHandler.getCounts(inspectorNode.key, inspectorNode.anchorId)?.let {
             recomposeCount = it.count
             recomposeSkips = it.skips
         }
 
-        children.forEach { child ->
-            addChildren(child.toComposableNodeImpl(stringTable, windowPos, recompositionHandler))
-        }
-
         anchorHash = inspectorNode.anchorId
     }
 }
@@ -252,17 +266,14 @@
     }.build()
 }
 
-internal fun CacheTree.toComposableRoot(
-    stringTable: StringTable,
-    windowPos: IntOffset,
-    recompositionHandler: RecompositionHandler
-): ComposableRoot = ComposableRoot.newBuilder().also { root ->
-    root.viewId = viewParent.uniqueDrawingId
-    root.addAllNodes(nodes.map {
-        it.toComposableNode(stringTable, windowPos, recompositionHandler)
-    })
-    root.addAllViewsToSkip(viewsToSkip)
-}.build()
+internal fun CacheTree.toComposableRoot(context: ConversionContext): ComposableRoot =
+    ComposableRoot.newBuilder().also { root ->
+        root.viewId = viewParent.uniqueDrawingId
+        root.addAllNodes(nodes.map {
+            it.toComposableNode(context)
+        })
+        root.addAllViewsToSkip(viewsToSkip)
+    }.build()
 
 fun Iterable<NodeParameter>.convertAll(stringTable: StringTable): List<Parameter> {
     return this.map { it.convert(stringTable) }
diff --git a/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/proto/ConversionContext.kt b/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/proto/ConversionContext.kt
new file mode 100644
index 0000000..a60fc4d
--- /dev/null
+++ b/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/proto/ConversionContext.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.inspection.proto
+
+import androidx.compose.ui.inspection.RecompositionHandler
+import androidx.compose.ui.unit.IntOffset
+
+/**
+ * Parameters for recursive protobuf constructor.
+ */
+internal class ConversionContext(
+    val stringTable: StringTable,
+    val windowPos: IntOffset,
+    val recompositionHandler: RecompositionHandler,
+    val reduceChildNesting: Boolean
+)
diff --git a/compose/ui/ui-inspection/src/main/proto/compose_layout_inspection.proto b/compose/ui/ui-inspection/src/main/proto/compose_layout_inspection.proto
index 0d54474..af96d0d 100644
--- a/compose/ui/ui-inspection/src/main/proto/compose_layout_inspection.proto
+++ b/compose/ui/ui-inspection/src/main/proto/compose_layout_inspection.proto
@@ -89,6 +89,9 @@
       HAS_MERGED_SEMANTICS = 0x2;
       HAS_UNMERGED_SEMANTICS = 0x4;
       INLINED = 0x8;
+      // If a node has the NESTED_SINGLE_CHILDREN flag, it means the children should be
+      // interpreted as a subtree of single nodes.
+      NESTED_SINGLE_CHILDREN = 0x16;
     }
     int32 flags = 9;
 
@@ -305,6 +308,8 @@
   bool keep_recompose_counts = 2;
   // If true, the compose parameters will not be extracted before
   bool delay_parameter_extractions = 3;
+  // If true, encode nested single children under the top node.
+  bool reduce_child_nesting = 4;
 }
 
 // The active settings after handling of an UpdateSettingsCommand
diff --git a/compose/ui/ui-test-junit4/lint-baseline.xml b/compose/ui/ui-test-junit4/lint-baseline.xml
index 1a69136..5b697f0 100644
--- a/compose/ui/ui-test-junit4/lint-baseline.xml
+++ b/compose/ui/ui-test-junit4/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.0.0-beta03" type="baseline" client="gradle" dependencies="false" name="AGP (8.0.0-beta03)" variant="all" version="8.0.0-beta03">
+<issues format="6" by="lint 8.1.0-beta05" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-beta05)" variant="all" version="8.1.0-beta05">
 
     <issue
         id="BanThreadSleep"
diff --git a/compose/ui/ui-text/lint-baseline.xml b/compose/ui/ui-text/lint-baseline.xml
index 4f7f4b5..c00a104 100644
--- a/compose/ui/ui-text/lint-baseline.xml
+++ b/compose/ui/ui-text/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.1.0-beta02" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-beta02)" variant="all" version="8.1.0-beta02">
+<issues format="6" by="lint 8.1.0-beta05" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-beta05)" variant="all" version="8.1.0-beta05">
 
     <issue
         id="BanInlineOptIn"
diff --git a/compose/ui/ui-tooling-data/build.gradle b/compose/ui/ui-tooling-data/build.gradle
index a2ba3a7..0faefb4 100644
--- a/compose/ui/ui-tooling-data/build.gradle
+++ b/compose/ui/ui-tooling-data/build.gradle
@@ -116,7 +116,6 @@
         if (desktopEnabled) {
             desktopTest {
                 dependsOn(jvmTest)
-                dependsOn(desktopMain)
                 dependencies {
 
                 }
diff --git a/compose/ui/ui-tooling-preview/build.gradle b/compose/ui/ui-tooling-preview/build.gradle
index 022bc02c..c32950f 100644
--- a/compose/ui/ui-tooling-preview/build.gradle
+++ b/compose/ui/ui-tooling-preview/build.gradle
@@ -101,7 +101,6 @@
         if (desktopEnabled) {
             desktopTest {
                 dependsOn(jvmTest)
-                dependsOn(desktopMain)
                 dependencies {
 
                 }
diff --git a/compose/ui/ui-tooling/build.gradle b/compose/ui/ui-tooling/build.gradle
index 4e12802..ac62e0d 100644
--- a/compose/ui/ui-tooling/build.gradle
+++ b/compose/ui/ui-tooling/build.gradle
@@ -126,7 +126,6 @@
         if (desktopEnabled) {
             desktopTest {
                 dependsOn(jvmTest)
-                dependsOn(desktopMain)
                 dependencies {
 
                 }
diff --git a/compose/ui/ui/build.gradle b/compose/ui/ui/build.gradle
index 7622917..f88d2c1 100644
--- a/compose/ui/ui/build.gradle
+++ b/compose/ui/ui/build.gradle
@@ -172,7 +172,6 @@
         if (desktopEnabled) {
             desktopTest {
                 dependsOn(jvmTest)
-                dependsOn(desktopMain)
                 dependencies {
                     implementation(libs.truth)
                     implementation(libs.junit)
@@ -199,7 +198,7 @@
         // version of foundation will be at least this version. This will prevent the bug in
         // foundation from occurring. This does _NOT_ require that the app have foundation as
         // a dependency.
-        implementation("androidx.compose.foundation:foundation:1.4.0") {
+        commonMainImplementation("androidx.compose.foundation:foundation:1.4.0") {
             because 'prevents a critical bug in Text'
         }
     }
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/ViewInterop.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/ViewInterop.kt
index 20cfb51..2ea6de9 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/ViewInterop.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/viewinterop/ViewInterop.kt
@@ -77,10 +77,10 @@
         val colorIndex = remember { mutableIntStateOf(0) }
         Button(
             onClick = {
-                colorIndex.value = (colorIndex.value + 1) % 4
+                colorIndex.intValue = (colorIndex.intValue + 1) % 4
                 squareRef.value!!.color = arrayOf(
                     Color.Blue, Color.LightGray, Color.Yellow, Color.Cyan
-                )[colorIndex.value]
+                )[colorIndex.intValue]
             }
         ) {
             Text("Change color of Android view")
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt
index 35c654f..0d4f41a 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt
@@ -807,12 +807,7 @@
                 ArgumentMatcher {
                     it.eventType == AccessibilityEvent.TYPE_VIEW_SCROLLED &&
                         it.scrollY == 1 &&
-                        it.maxScrollY == 100 &&
-                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
-                            it.scrollDeltaY == 1
-                        } else {
-                            true
-                        }
+                        it.maxScrollY > it.scrollY
                 }
             )
         )
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
index 3a416f3..93604d6 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
@@ -2952,6 +2952,7 @@
     @Test
     fun instancesKeepDelegates() {
         var color by mutableStateOf(Color.Red)
+        var size by mutableStateOf(30)
         var m: Measurable? = null
         val layoutCaptureModifier = object : LayoutModifier {
             override fun MeasureScope.measure(
@@ -2960,15 +2961,22 @@
             ): MeasureResult {
                 m = measurable
                 val p = measurable.measure(constraints)
-                drawLatch.countDown()
                 return layout(p.width, p.height) {
                     p.place(0, 0)
                 }
             }
         }
+        val drawCaptureModifier = object : DrawModifier {
+            override fun ContentDrawScope.draw() {
+                drawLatch.countDown()
+            }
+        }
         activityTestRule.runOnUiThread {
             activity.setContent {
-                FixedSize(30, layoutCaptureModifier.background(color)) {}
+                FixedSize(
+                    size = size,
+                    modifier = layoutCaptureModifier.background(color).then(drawCaptureModifier)
+                ) {}
             }
         }
         assertTrue(drawLatch.await(1, TimeUnit.SECONDS))
@@ -2977,6 +2985,7 @@
 
         activityTestRule.runOnUiThread {
             m = null
+            size = 40
             color = Color.Blue
         }
 
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/vector/VectorTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/vector/VectorTest.kt
index 8d021e3..f99c430 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/vector/VectorTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/vector/VectorTest.kt
@@ -543,6 +543,25 @@
         }
     }
 
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
+    fun testVectorStrokeWidth() {
+        val strokeWidth = mutableStateOf(100)
+        rule.setContent {
+            VectorStroke(strokeWidth = strokeWidth.value)
+        }
+        takeScreenShot(200).apply {
+            assertEquals(Color.Yellow.toArgb(), getPixel(100, 25))
+            assertEquals(Color.Blue.toArgb(), getPixel(100, 75))
+        }
+        rule.runOnUiThread { strokeWidth.value = 200 }
+        rule.waitForIdle()
+        takeScreenShot(200).apply {
+            assertEquals(Color.Yellow.toArgb(), getPixel(100, 25))
+            assertEquals(Color.Yellow.toArgb(), getPixel(100, 75))
+        }
+    }
+
     @Composable
     private fun VectorTint(
         size: Int = 200,
@@ -681,6 +700,47 @@
     }
 
     @Composable
+    private fun VectorStroke(
+        size: Int = 200,
+        strokeWidth: Int = 100,
+        minimumSize: Int = size,
+        alignment: Alignment = Alignment.Center
+    ) {
+        val sizePx = size.toFloat()
+        val sizeDp = (size / LocalDensity.current.density).dp
+        val strokeWidthPx = strokeWidth.toFloat()
+        val background = Modifier.paint(
+            rememberVectorPainter(
+                defaultWidth = sizeDp,
+                defaultHeight = sizeDp,
+                autoMirror = false
+            ) { _, _ ->
+                Path(
+                    pathData = PathData {
+                        lineTo(sizePx, 0.0f)
+                        lineTo(sizePx, sizePx)
+                        lineTo(0.0f, sizePx)
+                        close()
+                    },
+                    fill = SolidColor(Color.Blue)
+                )
+                // A thick stroke
+                Path(
+                    pathData = PathData {
+                        moveTo(0.0f, 0.0f)
+                        lineTo(sizePx, 0.0f)
+                    },
+                    stroke = SolidColor(Color.Yellow),
+                    strokeLineWidth = strokeWidthPx,
+                )
+            },
+            alignment = alignment
+        )
+        AtLeastSize(size = minimumSize, modifier = background) {
+        }
+    }
+
+    @Composable
     private fun VectorMirror(size: Int): VectorPainter {
         val sizePx = size.toFloat()
         val sizeDp = (size / LocalDensity.current.density).dp
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/AndroidViewTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/AndroidViewTest.kt
index 09da4d5..ffe8b60 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/AndroidViewTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/AndroidViewTest.kt
@@ -48,6 +48,8 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.DisallowComposableCalls
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.ReusableContent
 import androidx.compose.runtime.ReusableContentHost
 import androidx.compose.runtime.SideEffect
@@ -58,6 +60,7 @@
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.saveable.rememberSaveableStateHolder
 import androidx.compose.runtime.setValue
+import androidx.compose.runtime.withFrameNanos
 import androidx.compose.testutils.assertPixels
 import androidx.compose.ui.AbsoluteAlignment
 import androidx.compose.ui.ExperimentalComposeUiApi
@@ -122,6 +125,7 @@
 import androidx.test.filters.SdkSuppress
 import com.google.common.truth.Truth.assertThat
 import kotlin.math.roundToInt
+import kotlin.test.assertIs
 import org.hamcrest.CoreMatchers.endsWith
 import org.hamcrest.CoreMatchers.equalTo
 import org.hamcrest.CoreMatchers.instanceOf
@@ -443,7 +447,124 @@
     }
 
     @Test
-    fun androidView_updateObservesStateChanges() {
+    fun androidView_updateIsRanInitially() {
+        rule.setContent {
+            Box {
+                AndroidView(::UpdateTestView) { view ->
+                    view.counter = 1
+                }
+            }
+        }
+
+        onView(instanceOf(UpdateTestView::class.java)).check { view, _ ->
+            assertIs<UpdateTestView>(view)
+            assertThat(view.counter).isEqualTo(1)
+        }
+    }
+
+    @Test
+    fun androidView_updateObservesMultipleStateChanges() {
+        var counter by mutableStateOf(1)
+
+        rule.setContent {
+            Box {
+                AndroidView(::UpdateTestView) { view ->
+                    view.counter = counter
+                }
+            }
+        }
+
+        counter = 2
+        onView(instanceOf(UpdateTestView::class.java)).check { view, _ ->
+            assertIs<UpdateTestView>(view)
+            assertThat(view.counter).isEqualTo(counter)
+        }
+
+        counter = 3
+        onView(instanceOf(UpdateTestView::class.java)).check { view, _ ->
+            assertIs<UpdateTestView>(view)
+            assertThat(view.counter).isEqualTo(counter)
+        }
+
+        counter = 4
+        onView(instanceOf(UpdateTestView::class.java)).check { view, _ ->
+            assertIs<UpdateTestView>(view)
+            assertThat(view.counter).isEqualTo(counter)
+        }
+    }
+
+    @Test
+    fun androidView_updateObservesStateChanges_fromDisposableEffect() {
+        var counter by mutableStateOf(1)
+
+        rule.setContent {
+            DisposableEffect(Unit) {
+                counter = 2
+                onDispose {}
+            }
+
+            Box {
+                AndroidView(::UpdateTestView) { view ->
+                    view.counter = counter
+                }
+            }
+        }
+
+        onView(instanceOf(UpdateTestView::class.java)).check { view, _ ->
+            assertIs<UpdateTestView>(view)
+            assertThat(view.counter).isEqualTo(2)
+        }
+    }
+
+    @Test
+    fun androidView_updateObservesStateChanges_fromLaunchedEffect() {
+        var counter by mutableStateOf(1)
+
+        rule.setContent {
+            LaunchedEffect(Unit) {
+                counter = 2
+            }
+
+            Box {
+                AndroidView(::UpdateTestView) { view ->
+                    view.counter = counter
+                }
+            }
+        }
+
+        onView(instanceOf(UpdateTestView::class.java)).check { view, _ ->
+            assertIs<UpdateTestView>(view)
+            assertThat(view.counter).isEqualTo(2)
+        }
+    }
+
+    @Test
+    fun androidView_updateObservesMultipleStateChanges_fromEffect() {
+        var counter by mutableStateOf(1)
+
+        rule.setContent {
+            LaunchedEffect(Unit) {
+                counter = 2
+                withFrameNanos {
+                    counter = 3
+                }
+            }
+
+            Box {
+                AndroidView(::UpdateTestView) { view ->
+                    view.counter = counter
+                }
+            }
+        }
+
+        onView(instanceOf(UpdateTestView::class.java)).check { view, _ ->
+            assertIs<UpdateTestView>(view)
+            assertThat(view.counter).isEqualTo(3)
+        }
+    }
+
+    @Test
+    fun androidView_updateObservesLayoutStateChanges() {
         var size by mutableStateOf(20)
         var obtainedSize: IntSize = IntSize.Zero
         rule.setContent {
@@ -1522,7 +1643,11 @@
                     },
                 )
 
-                Box(Modifier.size(viewSizeDp).testTag("box"))
+                Box(
+                    Modifier
+                        .size(viewSizeDp)
+                        .testTag("box")
+                )
             }
         }
 
@@ -1637,6 +1762,10 @@
         }
     }
 
+    private class UpdateTestView(context: Context) : View(context) {
+        var counter = 0
+    }
+
     private fun Dp.toPx(displayMetrics: DisplayMetrics) =
         TypedValue.applyDimension(
             TypedValue.COMPLEX_UNIT_DIP,
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/VelocityTrackingParityTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/VelocityTrackingParityTest.kt
index 9373997..175e984 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/VelocityTrackingParityTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/viewinterop/VelocityTrackingParityTest.kt
@@ -41,7 +41,11 @@
 import androidx.lifecycle.Lifecycle
 import androidx.test.core.app.ActivityScenario
 import androidx.test.espresso.Espresso
-import androidx.test.espresso.action.ViewActions.swipeUp
+import androidx.test.espresso.action.CoordinatesProvider
+import androidx.test.espresso.action.GeneralLocation
+import androidx.test.espresso.action.GeneralSwipeAction
+import androidx.test.espresso.action.Press
+import androidx.test.espresso.action.Swipe
 import androidx.test.espresso.matcher.ViewMatchers.withId
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
@@ -100,6 +104,8 @@
         checkVisibility(composeView, View.VISIBLE)
         checkVisibility(draggableView, View.GONE)
 
+        assertTrue { isValidGesture(draggableView.motionEvents.filterNotNull()) }
+
         // Inject the same events in compose view
         for (event in draggableView.motionEvents) {
             composeView.dispatchTouchEvent(event)
@@ -129,9 +135,39 @@
     }
 
     private fun swipeView(id: Int) {
-        Espresso.onView(withId(id)).perform(swipeUp())
+        controlledSwipeUp(id)
         rule.waitForIdle()
     }
+
+    /**
+     * Checks the contents of [events] represents a swipe gesture.
+     */
+    private fun isValidGesture(events: List<MotionEvent>): Boolean {
+        val down = events.filter { it.action == MotionEvent.ACTION_DOWN }
+        val move = events.filter { it.action == MotionEvent.ACTION_MOVE }
+        val up = events.filter { it.action == MotionEvent.ACTION_UP }
+        return down.size == 1 && move.isNotEmpty() && up.size == 1
+    }
+}
+
+internal fun controlledSwipeUp(id: Int) {
+    Espresso.onView(withId(id))
+        .perform(
+            espressoSwipe(
+                GeneralLocation.CENTER,
+                GeneralLocation.TOP_CENTER
+            )
+        )
+}
+
+private fun espressoSwipe(
+    start: CoordinatesProvider,
+    end: CoordinatesProvider
+): GeneralSwipeAction {
+    return GeneralSwipeAction(
+        Swipe.FAST, start, end,
+        Press.FINGER
+    )
 }
 
 @Composable
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
index eba5a74..f4deee8 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
@@ -2733,9 +2733,6 @@
                     event.scrollY = newYState.value().toInt()
                     event.maxScrollY = newYState.maxValue().toInt()
                 }
-                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
-                    Api28Impl.setScrollEventDelta(event, deltaX.toInt(), deltaY.toInt())
-                }
                 sendEvent(event)
             }
 
@@ -3300,16 +3297,6 @@
         }
     }
 
-    @RequiresApi(Build.VERSION_CODES.P)
-    private object Api28Impl {
-        @JvmStatic
-        @DoNotInline
-        fun setScrollEventDelta(event: AccessibilityEvent, deltaX: Int, deltaY: Int) {
-            event.scrollDeltaX = deltaX
-            event.scrollDeltaY = deltaY
-        }
-    }
-
     @RequiresApi(Build.VERSION_CODES.Q)
     private object Api29Impl {
         @JvmStatic
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidView.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidView.android.kt
index 2aba2bf..22176eb 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidView.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidView.android.kt
@@ -41,12 +41,14 @@
 import androidx.compose.ui.node.ComposeUiNode.Companion.SetCompositeKeyHash
 import androidx.compose.ui.node.ComposeUiNode.Companion.SetResolvedCompositionLocals
 import androidx.compose.ui.node.LayoutNode
+import androidx.compose.ui.node.Owner
 import androidx.compose.ui.node.UiApplier
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.platform.LocalLifecycleOwner
 import androidx.compose.ui.platform.LocalSavedStateRegistryOwner
+import androidx.compose.ui.platform.LocalView
 import androidx.compose.ui.platform.ViewRootForInspector
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.LayoutDirection
@@ -266,6 +268,7 @@
     val context = LocalContext.current
     val parentReference = rememberCompositionContext()
     val stateRegistry = LocalSaveableStateRegistry.current
+    val ownerView = LocalView.current
 
     return {
         ViewFactoryHolder(
@@ -273,7 +276,8 @@
             factory = factory,
             parentContext = parentReference,
             saveStateRegistry = stateRegistry,
-            compositeKeyHash = compositeKeyHash
+            compositeKeyHash = compositeKeyHash,
+            owner = ownerView as Owner
         ).layoutNode
     }
 }
@@ -323,7 +327,8 @@
     val dispatcher: NestedScrollDispatcher = NestedScrollDispatcher(),
     private val saveStateRegistry: SaveableStateRegistry?,
     private val compositeKeyHash: Int,
-) : AndroidViewHolder(context, parentContext, compositeKeyHash, dispatcher, typedView),
+    owner: Owner,
+) : AndroidViewHolder(context, parentContext, compositeKeyHash, dispatcher, typedView, owner),
     ViewRootForInspector {
 
     constructor(
@@ -331,13 +336,15 @@
         factory: (Context) -> T,
         parentContext: CompositionContext? = null,
         saveStateRegistry: SaveableStateRegistry?,
-        compositeKeyHash: Int
+        compositeKeyHash: Int,
+        owner: Owner,
     ) : this(
         context = context,
         typedView = factory(context),
         parentContext = parentContext,
         saveStateRegistry = saveStateRegistry,
         compositeKeyHash = compositeKeyHash,
+        owner = owner,
     )
 
     override val viewRoot: View get() = this
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidViewHolder.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidViewHolder.android.kt
index 18e7963..3eab661 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidViewHolder.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidViewHolder.android.kt
@@ -20,13 +20,11 @@
 import android.graphics.Rect
 import android.graphics.Region
 import android.os.Build
-import android.os.Looper
 import android.view.View
 import android.view.ViewGroup
 import android.view.ViewParent
 import androidx.compose.runtime.ComposeNodeLifecycleCallback
 import androidx.compose.runtime.CompositionContext
-import androidx.compose.runtime.snapshots.SnapshotStateObserver
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.InternalComposeUiApi
 import androidx.compose.ui.Modifier
@@ -48,6 +46,9 @@
 import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.layout.positionInRoot
 import androidx.compose.ui.node.LayoutNode
+import androidx.compose.ui.node.Owner
+import androidx.compose.ui.node.OwnerScope
+import androidx.compose.ui.node.OwnerSnapshotObserver
 import androidx.compose.ui.platform.AndroidComposeView
 import androidx.compose.ui.platform.composeToViewOffset
 import androidx.compose.ui.platform.compositionContext
@@ -69,6 +70,9 @@
  * A base class used to host a [View] inside Compose.
  * This API is not designed to be used directly, but rather using the [AndroidView] and
  * `AndroidViewBinding` APIs, which are built on top of [AndroidViewHolder].
+ *
+ * @param view The view hosted by this holder.
+ * @param owner The [Owner] of the composition that this holder lives in.
  */
 @OptIn(ExperimentalComposeUiApi::class)
 internal open class AndroidViewHolder(
@@ -76,11 +80,9 @@
     parentContext: CompositionContext?,
     private val compositeKeyHash: Int,
     private val dispatcher: NestedScrollDispatcher,
-    /**
-     * The view hosted by this holder.
-     */
-    val view: View
-) : ViewGroup(context), NestedScrollingParent3, ComposeNodeLifecycleCallback {
+    val view: View,
+    private val owner: Owner,
+) : ViewGroup(context), NestedScrollingParent3, ComposeNodeLifecycleCallback, OwnerScope {
 
     init {
         // Any [Abstract]ComposeViews that are descendants of this view will host
@@ -161,20 +163,26 @@
             }
         }
 
-    private val snapshotObserver = SnapshotStateObserver { command ->
-        if (handler.looper === Looper.myLooper()) {
-            command()
-        } else {
-            handler.post(command)
+    /**
+     * The [OwnerSnapshotObserver] of this holder's [Owner]. Will be null when this view is not
+     * attached, since the observer is not valid unless the view is attached.
+     */
+    private val snapshotObserver: OwnerSnapshotObserver
+        get() {
+            check(isAttachedToWindow) {
+                "Expected AndroidViewHolder to be attached when observing reads."
+            }
+            return owner.snapshotObserver
         }
-    }
 
     private val onCommitAffectingUpdate: (AndroidViewHolder) -> Unit = {
         handler.post(runUpdate)
     }
 
     private val runUpdate: () -> Unit = {
-        if (hasUpdateBlock) {
+        // If we're not attached, the observer isn't started, so don't bother running it.
+        // onAttachedToWindow will run an update the next time the view is attached.
+        if (hasUpdateBlock && isAttachedToWindow) {
             snapshotObserver.observeReads(this, onCommitAffectingUpdate, update)
         }
     }
@@ -195,6 +203,9 @@
 
     private var isDrawing = false
 
+    override val isValidOwnerScope: Boolean
+        get() = isAttachedToWindow
+
     override fun onReuse() {
         // We reset at the same time we remove the view. So if the view was removed, we can just
         // re-add it and it's ready to go. If it's already attached, we didn't reset it and need
@@ -259,14 +270,13 @@
 
     override fun onAttachedToWindow() {
         super.onAttachedToWindow()
-        snapshotObserver.start()
+        runUpdate()
     }
 
     override fun onDetachedFromWindow() {
         super.onDetachedFromWindow()
-        snapshotObserver.stop()
         // remove all observations:
-        snapshotObserver.clear()
+        snapshotObserver.clear(this)
     }
 
     // When there is no hardware acceleration invalidates are intercepted using this method,
@@ -346,7 +356,8 @@
                         isDrawing = false
                     }
                 }
-            }.onGloballyPositioned {
+            }
+            .onGloballyPositioned {
                 // The global position of this LayoutNode can change with it being replaced. For
                 // these cases, we need to inform the View.
                 layoutAccordingTo(layoutNode)
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/Vector.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/Vector.kt
index 9c256c7..2eed50f 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/Vector.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/Vector.kt
@@ -214,6 +214,7 @@
     var strokeLineWidth = DefaultStrokeLineWidth
         set(value) {
             field = value
+            isStrokeDirty = true
             invalidate()
         }
 
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/VectorPainter.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/VectorPainter.kt
index a6940e0..648babc7 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/VectorPainter.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/graphics/vector/VectorPainter.kt
@@ -219,6 +219,7 @@
         return next
     }
 
+    // TODO replace with mutableStateOf(Unit, neverEqualPolicy()) after b/291647821 is addressed
     private var invalidateCount by mutableIntStateOf(0)
 
     @Composable
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsNode.kt
index e72cfd8..79f4d78 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsNode.kt
@@ -260,10 +260,15 @@
         // TODO(lmr): visitChildren would be great for this but we would lose the zSorted bit...
         //  i wonder if we can optimize this for the common case of no z-sortedness going on.
         zSortedChildren.forEach { child ->
-            if (child.nodes.has(Nodes.Semantics)) {
-                list.add(SemanticsNode(child, mergingEnabled))
-            } else {
-                child.fillOneLayerOfSemanticsWrappers(list)
+            // TODO(b/290936195): In some conditions it appears that children here can be
+            //  unattached. We just guard against that here as a "quick fix" but we need to
+            //  understand why this is happening and followup with a proper fix.
+            if (child.isAttached) {
+                if (child.nodes.has(Nodes.Semantics)) {
+                    list.add(SemanticsNode(child, mergingEnabled))
+                } else {
+                    child.fillOneLayerOfSemanticsWrappers(list)
+                }
             }
         }
     }
diff --git a/constraintlayout/constraintlayout-compose/api/current.txt b/constraintlayout/constraintlayout-compose/api/current.txt
index 06682d3..6e529b9 100644
--- a/constraintlayout/constraintlayout-compose/api/current.txt
+++ b/constraintlayout/constraintlayout-compose/api/current.txt
@@ -227,7 +227,7 @@
   }
 
   public final class ConstraintLayoutKt {
-    method @androidx.compose.runtime.Composable public static inline void ConstraintLayout(optional androidx.compose.ui.Modifier modifier, optional int optimizationLevel, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.ConstraintLayoutScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static inline void ConstraintLayout(optional androidx.compose.ui.Modifier modifier, optional int optimizationLevel, optional boolean animateChanges, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function0<kotlin.Unit>? finishedAnimationListener, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.ConstraintLayoutScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static inline void ConstraintLayout(androidx.constraintlayout.compose.ConstraintSet constraintSet, optional androidx.compose.ui.Modifier modifier, optional int optimizationLevel, optional boolean animateChanges, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function0<kotlin.Unit>? finishedAnimationListener, kotlin.jvm.functions.Function0<kotlin.Unit> content);
     method public static androidx.constraintlayout.compose.ConstraintSet ConstraintSet(androidx.constraintlayout.compose.ConstraintSet extendConstraintSet, @org.intellij.lang.annotations.Language("json5") String jsonContent);
     method public static androidx.constraintlayout.compose.ConstraintSet ConstraintSet(androidx.constraintlayout.compose.ConstraintSet extendConstraintSet, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.ConstraintSetScope,kotlin.Unit> description);
@@ -480,6 +480,29 @@
     property public final androidx.constraintlayout.compose.VerticalAnchorable start;
   }
 
+  public final class InvalidationStrategy {
+    ctor public InvalidationStrategy(optional androidx.constraintlayout.compose.InvalidationStrategy.OnIncomingConstraints? onIncomingConstraints, kotlin.jvm.functions.Function0<kotlin.Unit>? onObservedStateChange);
+    method public androidx.constraintlayout.compose.InvalidationStrategy.OnIncomingConstraints? getOnIncomingConstraints();
+    method public kotlin.jvm.functions.Function0<kotlin.Unit>? getOnObservedStateChange();
+    property public final androidx.constraintlayout.compose.InvalidationStrategy.OnIncomingConstraints? onIncomingConstraints;
+    property public final kotlin.jvm.functions.Function0<kotlin.Unit>? onObservedStateChange;
+    field public static final androidx.constraintlayout.compose.InvalidationStrategy.Companion Companion;
+  }
+
+  public static final class InvalidationStrategy.Companion {
+    method public androidx.constraintlayout.compose.InvalidationStrategy getDefaultInvalidationStrategy();
+    property public final androidx.constraintlayout.compose.InvalidationStrategy DefaultInvalidationStrategy;
+  }
+
+  public static fun interface InvalidationStrategy.OnIncomingConstraints {
+    method public operator boolean invoke(androidx.constraintlayout.compose.InvalidationStrategyScope, long old, long new);
+  }
+
+  public final class InvalidationStrategyScope {
+    method public boolean fixedHeightRate(long oldConstraints, long newConstraints, int skipCount, int threshold);
+    method public boolean fixedWidthRate(long oldConstraints, long newConstraints, int skipCount, int threshold);
+  }
+
   public final class KeyAttributeScope extends androidx.constraintlayout.compose.BaseKeyFrameScope {
     method public float getAlpha();
     method public float getRotationX();
@@ -639,9 +662,9 @@
   }
 
   public final class MotionLayoutKt {
-    method @androidx.compose.runtime.Composable public static inline void MotionLayout(androidx.constraintlayout.compose.ConstraintSet start, androidx.constraintlayout.compose.ConstraintSet end, float progress, optional androidx.compose.ui.Modifier modifier, optional androidx.constraintlayout.compose.Transition? transition, optional int debugFlags, optional int optimizationLevel, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static inline void MotionLayout(androidx.constraintlayout.compose.MotionScene motionScene, float progress, optional androidx.compose.ui.Modifier modifier, optional String transitionName, optional int debugFlags, optional int optimizationLevel, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static inline void MotionLayout(androidx.constraintlayout.compose.MotionScene motionScene, String? constraintSetName, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? finishedAnimationListener, optional int debugFlags, optional int optimizationLevel, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static inline void MotionLayout(androidx.constraintlayout.compose.ConstraintSet start, androidx.constraintlayout.compose.ConstraintSet end, float progress, optional androidx.compose.ui.Modifier modifier, optional androidx.constraintlayout.compose.Transition? transition, optional int debugFlags, optional int optimizationLevel, optional androidx.constraintlayout.compose.InvalidationStrategy invalidationStrategy, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static inline void MotionLayout(androidx.constraintlayout.compose.MotionScene motionScene, float progress, optional androidx.compose.ui.Modifier modifier, optional String transitionName, optional int debugFlags, optional int optimizationLevel, optional androidx.constraintlayout.compose.InvalidationStrategy invalidationStrategy, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static inline void MotionLayout(androidx.constraintlayout.compose.MotionScene motionScene, String? constraintSetName, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? finishedAnimationListener, optional int debugFlags, optional int optimizationLevel, optional androidx.constraintlayout.compose.InvalidationStrategy invalidationStrategy, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
   }
 
   @androidx.compose.foundation.layout.LayoutScopeMarker public final class MotionLayoutScope {
diff --git a/constraintlayout/constraintlayout-compose/api/restricted_current.txt b/constraintlayout/constraintlayout-compose/api/restricted_current.txt
index 74d1f95..af5cd2f 100644
--- a/constraintlayout/constraintlayout-compose/api/restricted_current.txt
+++ b/constraintlayout/constraintlayout-compose/api/restricted_current.txt
@@ -210,6 +210,7 @@
     method public final androidx.constraintlayout.compose.LayoutReference withHorizontalChainParams(androidx.constraintlayout.compose.LayoutReference, optional float startMargin, optional float endMargin, optional float startGoneMargin, optional float endGoneMargin, optional float weight);
     method public final androidx.constraintlayout.compose.LayoutReference withVerticalChainParams(androidx.constraintlayout.compose.LayoutReference, optional float topMargin, optional float bottomMargin, optional float topGoneMargin, optional float bottomGoneMargin, optional float weight);
     property @Deprecated protected final java.util.List<kotlin.jvm.functions.Function1<androidx.constraintlayout.compose.State,kotlin.Unit>> tasks;
+    field @kotlin.PublishedApi internal final androidx.constraintlayout.core.parser.CLObject containerObject;
     field @kotlin.PublishedApi internal int helpersHashCode;
   }
 
@@ -235,7 +236,7 @@
   }
 
   public final class ConstraintLayoutKt {
-    method @androidx.compose.runtime.Composable public static inline void ConstraintLayout(optional androidx.compose.ui.Modifier modifier, optional int optimizationLevel, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.ConstraintLayoutScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static inline void ConstraintLayout(optional androidx.compose.ui.Modifier modifier, optional int optimizationLevel, optional boolean animateChanges, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function0<kotlin.Unit>? finishedAnimationListener, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.ConstraintLayoutScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static inline void ConstraintLayout(androidx.constraintlayout.compose.ConstraintSet constraintSet, optional androidx.compose.ui.Modifier modifier, optional int optimizationLevel, optional boolean animateChanges, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional kotlin.jvm.functions.Function0<kotlin.Unit>? finishedAnimationListener, kotlin.jvm.functions.Function0<kotlin.Unit> content);
     method public static androidx.constraintlayout.compose.ConstraintSet ConstraintSet(androidx.constraintlayout.compose.ConstraintSet extendConstraintSet, @org.intellij.lang.annotations.Language("json5") String jsonContent);
     method public static androidx.constraintlayout.compose.ConstraintSet ConstraintSet(androidx.constraintlayout.compose.ConstraintSet extendConstraintSet, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.ConstraintSetScope,kotlin.Unit> description);
@@ -258,6 +259,7 @@
     method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier constrainAs(androidx.compose.ui.Modifier, androidx.constraintlayout.compose.ConstrainedLayoutReference ref, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.ConstrainScope,kotlin.Unit> constrainBlock);
     method public androidx.constraintlayout.compose.ConstrainedLayoutReference createRef();
     method @androidx.compose.runtime.Stable public androidx.constraintlayout.compose.ConstraintLayoutScope.ConstrainedLayoutReferences createRefs();
+    field @kotlin.PublishedApi internal boolean isAnimateChanges;
   }
 
   public final class ConstraintLayoutScope.ConstrainedLayoutReferences {
@@ -523,6 +525,29 @@
     property public final androidx.constraintlayout.compose.VerticalAnchorable start;
   }
 
+  public final class InvalidationStrategy {
+    ctor public InvalidationStrategy(optional androidx.constraintlayout.compose.InvalidationStrategy.OnIncomingConstraints? onIncomingConstraints, kotlin.jvm.functions.Function0<kotlin.Unit>? onObservedStateChange);
+    method public androidx.constraintlayout.compose.InvalidationStrategy.OnIncomingConstraints? getOnIncomingConstraints();
+    method public kotlin.jvm.functions.Function0<kotlin.Unit>? getOnObservedStateChange();
+    property public final androidx.constraintlayout.compose.InvalidationStrategy.OnIncomingConstraints? onIncomingConstraints;
+    property public final kotlin.jvm.functions.Function0<kotlin.Unit>? onObservedStateChange;
+    field public static final androidx.constraintlayout.compose.InvalidationStrategy.Companion Companion;
+  }
+
+  public static final class InvalidationStrategy.Companion {
+    method public androidx.constraintlayout.compose.InvalidationStrategy getDefaultInvalidationStrategy();
+    property public final androidx.constraintlayout.compose.InvalidationStrategy DefaultInvalidationStrategy;
+  }
+
+  public static fun interface InvalidationStrategy.OnIncomingConstraints {
+    method public operator boolean invoke(androidx.constraintlayout.compose.InvalidationStrategyScope, long old, long new);
+  }
+
+  public final class InvalidationStrategyScope {
+    method public boolean fixedHeightRate(long oldConstraints, long newConstraints, int skipCount, int threshold);
+    method public boolean fixedWidthRate(long oldConstraints, long newConstraints, int skipCount, int threshold);
+  }
+
   public final class KeyAttributeScope extends androidx.constraintlayout.compose.BaseKeyFrameScope {
     method public float getAlpha();
     method public float getRotationX();
@@ -625,6 +650,10 @@
     property public final androidx.constraintlayout.compose.RelativePosition type;
   }
 
+  public final class LateMotionLayoutKt {
+    method @androidx.compose.runtime.Composable @kotlin.PublishedApi internal static void LateMotionLayout(androidx.compose.runtime.MutableState<androidx.constraintlayout.compose.ConstraintSet> start, androidx.compose.runtime.MutableState<androidx.constraintlayout.compose.ConstraintSet> end, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, kotlinx.coroutines.channels.Channel<androidx.constraintlayout.compose.ConstraintSet> channel, androidx.compose.runtime.State<kotlin.Unit> contentTracker, androidx.compose.ui.node.Ref<androidx.constraintlayout.compose.CompositionSource> compositionSource, int optimizationLevel, kotlin.jvm.functions.Function0<kotlin.Unit>? finishedAnimationListener, androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+  }
+
   public enum LayoutInfoFlags {
     method public static androidx.constraintlayout.compose.LayoutInfoFlags valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
     method public static androidx.constraintlayout.compose.LayoutInfoFlags[] values();
@@ -716,12 +745,12 @@
   }
 
   public final class MotionLayoutKt {
-    method @androidx.compose.runtime.Composable public static inline void MotionLayout(androidx.constraintlayout.compose.ConstraintSet start, androidx.constraintlayout.compose.ConstraintSet end, float progress, optional androidx.compose.ui.Modifier modifier, optional androidx.constraintlayout.compose.Transition? transition, optional int debugFlags, optional int optimizationLevel, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static inline void MotionLayout(androidx.constraintlayout.compose.MotionScene motionScene, float progress, optional androidx.compose.ui.Modifier modifier, optional String transitionName, optional int debugFlags, optional int optimizationLevel, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static inline void MotionLayout(androidx.constraintlayout.compose.MotionScene motionScene, String? constraintSetName, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? finishedAnimationListener, optional int debugFlags, optional int optimizationLevel, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable @kotlin.PublishedApi internal static void MotionLayoutCore(androidx.constraintlayout.compose.ConstraintSet start, androidx.constraintlayout.compose.ConstraintSet end, androidx.constraintlayout.compose.Transition? transition, float progress, androidx.constraintlayout.compose.LayoutInformationReceiver? informationReceiver, int optimizationLevel, boolean showBounds, boolean showPaths, boolean showKeyPositions, androidx.compose.ui.Modifier modifier, androidx.compose.runtime.State<kotlin.Unit> contentTracker, androidx.compose.ui.node.Ref<androidx.constraintlayout.compose.CompositionSource> compositionSource, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable @kotlin.PublishedApi internal static void MotionLayoutCore(androidx.constraintlayout.compose.MotionScene motionScene, float progress, String transitionName, int optimizationLevel, int debugFlags, androidx.compose.ui.Modifier modifier, androidx.compose.runtime.State<kotlin.Unit> contentTracker, androidx.compose.ui.node.Ref<androidx.constraintlayout.compose.CompositionSource> compositionSource, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable @kotlin.PublishedApi internal static void MotionLayoutCore(androidx.constraintlayout.compose.MotionScene motionScene, String? constraintSetName, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? finishedAnimationListener, optional int debugFlags, optional int optimizationLevel, androidx.compose.runtime.State<kotlin.Unit> contentTracker, androidx.compose.ui.node.Ref<androidx.constraintlayout.compose.CompositionSource> compositionSource, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static inline void MotionLayout(androidx.constraintlayout.compose.ConstraintSet start, androidx.constraintlayout.compose.ConstraintSet end, float progress, optional androidx.compose.ui.Modifier modifier, optional androidx.constraintlayout.compose.Transition? transition, optional int debugFlags, optional int optimizationLevel, optional androidx.constraintlayout.compose.InvalidationStrategy invalidationStrategy, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static inline void MotionLayout(androidx.constraintlayout.compose.MotionScene motionScene, float progress, optional androidx.compose.ui.Modifier modifier, optional String transitionName, optional int debugFlags, optional int optimizationLevel, optional androidx.constraintlayout.compose.InvalidationStrategy invalidationStrategy, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static inline void MotionLayout(androidx.constraintlayout.compose.MotionScene motionScene, String? constraintSetName, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? finishedAnimationListener, optional int debugFlags, optional int optimizationLevel, optional androidx.constraintlayout.compose.InvalidationStrategy invalidationStrategy, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable @kotlin.PublishedApi internal static void MotionLayoutCore(androidx.constraintlayout.compose.ConstraintSet start, androidx.constraintlayout.compose.ConstraintSet end, androidx.constraintlayout.compose.Transition? transition, float progress, androidx.constraintlayout.compose.LayoutInformationReceiver? informationReceiver, int optimizationLevel, boolean showBounds, boolean showPaths, boolean showKeyPositions, androidx.compose.ui.Modifier modifier, androidx.compose.runtime.MutableState<kotlin.Unit> contentTracker, androidx.compose.ui.node.Ref<androidx.constraintlayout.compose.CompositionSource> compositionSource, androidx.constraintlayout.compose.InvalidationStrategy invalidationStrategy, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable @kotlin.PublishedApi internal static void MotionLayoutCore(androidx.constraintlayout.compose.MotionScene motionScene, float progress, String transitionName, int optimizationLevel, int debugFlags, androidx.compose.ui.Modifier modifier, androidx.compose.runtime.MutableState<kotlin.Unit> contentTracker, androidx.compose.ui.node.Ref<androidx.constraintlayout.compose.CompositionSource> compositionSource, androidx.constraintlayout.compose.InvalidationStrategy invalidationStrategy, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable @kotlin.PublishedApi internal static void MotionLayoutCore(androidx.constraintlayout.compose.MotionScene motionScene, String? constraintSetName, androidx.compose.animation.core.AnimationSpec<java.lang.Float> animationSpec, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? finishedAnimationListener, optional int debugFlags, optional int optimizationLevel, androidx.compose.runtime.MutableState<kotlin.Unit> contentTracker, androidx.compose.ui.node.Ref<androidx.constraintlayout.compose.CompositionSource> compositionSource, androidx.constraintlayout.compose.InvalidationStrategy invalidationStrategy, kotlin.jvm.functions.Function1<? super androidx.constraintlayout.compose.MotionLayoutScope,kotlin.Unit> content);
   }
 
   @androidx.compose.foundation.layout.LayoutScopeMarker public final class MotionLayoutScope {
@@ -835,6 +864,11 @@
     property public final androidx.constraintlayout.compose.SwipeSide side;
   }
 
+  @androidx.compose.runtime.Immutable @kotlin.PublishedApi internal final class RawConstraintSet implements androidx.constraintlayout.compose.ConstraintSet {
+    ctor public RawConstraintSet(androidx.constraintlayout.core.parser.CLObject clObject);
+    method public void applyTo(androidx.constraintlayout.compose.State state, java.util.List<? extends androidx.compose.ui.layout.Measurable> measurables);
+  }
+
   public final class RelativePosition {
     method public String getName();
     property public String name;
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/compose-benchmark/build.gradle b/constraintlayout/constraintlayout-compose/integration-tests/compose-benchmark/build.gradle
new file mode 100644
index 0000000..57aa1a8
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/compose-benchmark/build.gradle
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("AndroidXComposePlugin")
+    id("org.jetbrains.kotlin.android")
+    id("androidx.benchmark")
+}
+
+dependencies {
+    androidTestImplementation project(":constraintlayout:constraintlayout-compose")
+    androidTestImplementation project(":constraintlayout:constraintlayout-core")
+    androidTestImplementation project(":benchmark:benchmark-junit4")
+    androidTestImplementation project(":compose:runtime:runtime")
+    androidTestImplementation project(":compose:benchmark-utils")
+    androidTestImplementation(libs.testRules)
+    androidTestImplementation(libs.junit)
+    androidTestImplementation(libs.kotlinStdlib)
+    androidTestImplementation(libs.kotlinTestCommon)
+    androidTestImplementation(libs.truth)
+}
+
+android {
+    namespace "androidx.constraintlayout.compose.benchmark"
+}
\ No newline at end of file
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/compose-benchmark/src/androidTest/java/androidx/constraintlayout/compose/benchmark/MotionSceneBenchmark.kt b/constraintlayout/constraintlayout-compose/integration-tests/compose-benchmark/src/androidTest/java/androidx/constraintlayout/compose/benchmark/MotionSceneBenchmark.kt
new file mode 100644
index 0000000..f3acb7c
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/integration-tests/compose-benchmark/src/androidTest/java/androidx/constraintlayout/compose/benchmark/MotionSceneBenchmark.kt
@@ -0,0 +1,290 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.constraintlayout.compose.benchmark
+
+import androidx.benchmark.junit4.BenchmarkRule
+import androidx.benchmark.junit4.measureRepeated
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import androidx.constraintlayout.compose.Dimension
+import androidx.constraintlayout.compose.Easing
+import androidx.constraintlayout.compose.MotionScene
+import androidx.constraintlayout.compose.OnSwipe
+import androidx.constraintlayout.compose.SwipeDirection
+import androidx.constraintlayout.compose.SwipeMode
+import androidx.constraintlayout.compose.SwipeSide
+import androidx.constraintlayout.compose.SwipeTouchUp
+import androidx.constraintlayout.compose.Visibility
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class MotionSceneBenchmark {
+    @get:Rule
+    val benchmarkRule = BenchmarkRule()
+
+    /**
+     * One of the most basics MotionScenes.
+     *
+     * Just a box moving from one corner to the other. Fairly minimal example.
+     */
+    @Test
+    fun motionScene_simple() {
+        benchmarkRule.measureRepeated {
+            MotionScene {
+                val boxRef = createRefFor("box")
+                defaultTransition(
+                    from = constraintSet {
+                        constrain(boxRef) {
+                            width = 50.dp.asDimension()
+                            height = 50.dp.asDimension()
+
+                            top.linkTo(parent.top, 8.dp)
+                            start.linkTo(parent.start, 8.dp)
+                        }
+                    },
+                    to = constraintSet {
+                        constrain(boxRef) {
+                            width = 50.dp.asDimension()
+                            height = 50.dp.asDimension()
+
+                            bottom.linkTo(parent.bottom, 8.dp)
+                            end.linkTo(parent.end, 8.dp)
+                        }
+                    }
+                )
+            }
+        }
+    }
+
+    /**
+     * The MotionScene was mostly a copy of `messageMotionScene()` from NewMessage.kt in the
+     * macrobenchmark-target module.
+     *
+     * It's been modified to represent a more complex scenario. Does not necessarily have to make
+     * sense since it's for benchmarking.
+     */
+    @Test
+    fun motionScene_complex() {
+        val primary = Color(0xFFF44336)
+        val primaryVariant = Color(0xFFE91E63)
+        val onPrimary = Color(0xFF673AB7)
+        val surface = Color(0xFF3F51B5)
+        val onSurface = Color(0xFF2196F3)
+
+        benchmarkRule.measureRepeated {
+            MotionScene {
+                val (box, minIcon, editClose, title, content) =
+                    createRefsFor("box", "minIcon", "editClose", "title", "content")
+
+                val fab = constraintSet(NewMessageLayout.Fab.name) {
+                    constrain(box) {
+                        width = Dimension.value(50.dp)
+                        height = Dimension.value(50.dp)
+                        end.linkTo(parent.end, 12.dp)
+                        bottom.linkTo(parent.bottom, 12.dp)
+
+                        customColor("background", primary)
+
+                        staggeredWeight = 1f
+                    }
+                    constrain(minIcon) {
+                        width = Dimension.value(40.dp)
+                        height = Dimension.value(40.dp)
+
+                        end.linkTo(editClose.start, 8.dp)
+                        top.linkTo(editClose.top)
+                        customColor("content", onPrimary)
+                    }
+                    constrain(editClose) {
+                        width = Dimension.value(40.dp)
+                        height = Dimension.value(40.dp)
+
+                        centerTo(box)
+
+                        customColor("content", onPrimary)
+                    }
+                    constrain(title) {
+                        width = Dimension.fillToConstraints
+                        top.linkTo(box.top)
+                        bottom.linkTo(editClose.bottom)
+                        start.linkTo(box.start, 8.dp)
+                        end.linkTo(minIcon.start, 8.dp)
+                        customColor("content", onPrimary)
+
+                        visibility = Visibility.Gone
+                    }
+                    constrain(content) {
+                        width = Dimension.fillToConstraints
+                        height = Dimension.fillToConstraints
+                        start.linkTo(box.start, 8.dp)
+                        end.linkTo(box.end, 8.dp)
+
+                        top.linkTo(editClose.bottom, 8.dp)
+                        bottom.linkTo(box.bottom, 8.dp)
+
+                        visibility = Visibility.Gone
+                    }
+                }
+                val full = constraintSet(NewMessageLayout.Full.name) {
+                    constrain(box) {
+                        width = Dimension.fillToConstraints
+                        height = Dimension.fillToConstraints
+                        start.linkTo(parent.start, 12.dp)
+                        end.linkTo(parent.end, 12.dp)
+                        bottom.linkTo(parent.bottom, 12.dp)
+                        top.linkTo(parent.top, 40.dp)
+                        customColor("background", surface)
+                    }
+                    constrain(minIcon) {
+                        width = Dimension.value(40.dp)
+                        height = Dimension.value(40.dp)
+
+                        end.linkTo(editClose.start, 8.dp)
+                        top.linkTo(editClose.top)
+                        customColor("content", onSurface)
+                    }
+                    constrain(editClose) {
+                        width = Dimension.value(40.dp)
+                        height = Dimension.value(40.dp)
+
+                        end.linkTo(box.end, 4.dp)
+                        top.linkTo(box.top, 4.dp)
+                        customColor("content", onSurface)
+                    }
+                    constrain(title) {
+                        width = Dimension.fillToConstraints
+                        top.linkTo(box.top)
+                        bottom.linkTo(editClose.bottom)
+                        start.linkTo(box.start, 8.dp)
+                        end.linkTo(minIcon.start, 8.dp)
+                        customColor("content", onSurface)
+                    }
+                    constrain(content) {
+                        width = Dimension.fillToConstraints
+                        height = Dimension.fillToConstraints
+                        start.linkTo(box.start, 8.dp)
+                        end.linkTo(box.end, 8.dp)
+                        top.linkTo(editClose.bottom, 8.dp)
+                        bottom.linkTo(box.bottom, 8.dp)
+                    }
+                }
+                val mini = constraintSet(NewMessageLayout.Mini.name) {
+                    constrain(box) {
+                        width = Dimension.value(220.dp)
+                        height = Dimension.value(50.dp)
+
+                        end.linkTo(parent.end, 12.dp)
+                        bottom.linkTo(parent.bottom, 12.dp)
+
+                        customColor("background", primaryVariant)
+                    }
+                    constrain(minIcon) {
+                        width = Dimension.value(40.dp)
+                        height = Dimension.value(40.dp)
+
+                        end.linkTo(editClose.start, 8.dp)
+                        top.linkTo(editClose.top)
+
+                        rotationZ = 180f
+
+                        customColor("content", onPrimary)
+                    }
+                    constrain(editClose) {
+                        width = Dimension.value(40.dp)
+                        height = Dimension.value(40.dp)
+
+                        end.linkTo(box.end, 4.dp)
+                        top.linkTo(box.top, 4.dp)
+                        customColor("content", onPrimary)
+                    }
+                    constrain(title) {
+                        width = Dimension.fillToConstraints
+                        top.linkTo(box.top)
+                        bottom.linkTo(editClose.bottom)
+                        start.linkTo(box.start, 8.dp)
+                        end.linkTo(minIcon.start, 8.dp)
+                        customColor("content", onPrimary)
+                    }
+                    constrain(content) {
+                        width = Dimension.fillToConstraints
+                        start.linkTo(box.start, 8.dp)
+                        end.linkTo(box.end, 8.dp)
+
+                        top.linkTo(editClose.bottom, 8.dp)
+                        bottom.linkTo(box.bottom, 8.dp)
+
+                        visibility = Visibility.Gone
+                    }
+                }
+
+                fun constraintSetFor(layoutState: NewMessageLayout) =
+                    when (layoutState) {
+                        NewMessageLayout.Full -> full
+                        NewMessageLayout.Mini -> mini
+                        NewMessageLayout.Fab -> fab
+                    }
+                defaultTransition(
+                    from = constraintSetFor(NewMessageLayout.Fab),
+                    to = constraintSetFor(NewMessageLayout.Full)
+                ) {
+                    maxStaggerDelay = 0.6f
+
+                    keyAttributes(title, content) {
+                        frame(30) {
+                            alpha = 0.5f
+                        }
+                        frame(60) {
+                            alpha = 0.9f
+                        }
+                    }
+                }
+
+                transition(
+                    from = constraintSetFor(NewMessageLayout.Full),
+                    to = constraintSetFor(NewMessageLayout.Mini)
+                ) {
+                    onSwipe = OnSwipe(
+                        anchor = editClose,
+                        side = SwipeSide.Middle,
+                        direction = SwipeDirection.Down,
+                        onTouchUp = SwipeTouchUp.AutoComplete,
+                        mode = SwipeMode.spring(threshold = 0.001f)
+                    )
+
+                    keyCycles(minIcon) {
+                        easing = Easing.cubic(x1 = 0.3f, y1 = 0.2f, x2 = 0.8f, y2 = 0.7f)
+                        frame(50) {
+                            rotationZ = 90f
+                            period = 4f
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private enum class NewMessageLayout {
+        Full,
+        Mini,
+        Fab
+    }
+}
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/demos/build.gradle b/constraintlayout/constraintlayout-compose/integration-tests/demos/build.gradle
index 17f8024..db7fe29 100644
--- a/constraintlayout/constraintlayout-compose/integration-tests/demos/build.gradle
+++ b/constraintlayout/constraintlayout-compose/integration-tests/demos/build.gradle
@@ -24,6 +24,7 @@
 dependencies {
     implementation(project(":constraintlayout:constraintlayout-compose"))
 
+    implementation(project(":activity:activity-compose"))
     implementation(project(":compose:foundation:foundation-layout"))
     implementation(project(":compose:runtime:runtime"))
     implementation(project(":compose:ui:ui"))
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/AllDemos.kt b/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/AllDemos.kt
index 4ab8cf2..b2fef8e 100644
--- a/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/AllDemos.kt
+++ b/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/AllDemos.kt
@@ -16,6 +16,8 @@
 
 package androidx.constraintlayout.compose.demos
 
+import android.app.Activity
+import androidx.activity.compose.BackHandler
 import androidx.compose.foundation.background
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Arrangement
@@ -44,9 +46,11 @@
 import androidx.compose.ui.draw.clip
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
+import kotlin.system.exitProcess
 
 data class ComposeDemo(val title: String, val content: @Composable () -> Unit)
 
@@ -56,6 +60,9 @@
         ComposeDemo("Simple OnSwipe") { SimpleOnSwipe() },
         ComposeDemo("Multiple OnSwipe") { MultiSwipeDsl() },
         ComposeDemo("AnimatedChainOrientation") { ChainsAnimatedOrientationDemo() },
+        ComposeDemo("AnimatedChainOrientation w/ Modifier DSL") {
+            ChainsAnimatedOrientationDemo1()
+        },
         ComposeDemo("CollapsibleToolbar w/ Column") { ToolBarDslDemo() },
         ComposeDemo("CollapsibleToolbar w/ LazyColumn") { ToolBarLazyDslDemo() },
         ComposeDemo("MotionLayout in LazyList") { MotionInLazyColumnDslDemo() },
@@ -150,6 +157,16 @@
             }
         }
     }
+
+    val activity = LocalContext.current as? Activity
+    // If there's a demo being displayed, return to demo list, otherwise, exit app
+    BackHandler {
+        if (displayedDemoIndex >= 0) {
+            displayedDemoIndex = -1
+        } else {
+            activity?.finishAffinity() ?: exitProcess(0)
+        }
+    }
 }
 
 @Composable
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/ChainsDemo.kt b/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/ChainsDemo.kt
index c1dfe25..391cca6 100644
--- a/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/ChainsDemo.kt
+++ b/constraintlayout/constraintlayout-compose/integration-tests/demos/src/main/java/androidx/constraintlayout/compose/demos/ChainsDemo.kt
@@ -108,3 +108,85 @@
         }
     }
 }
+
+@Preview
+@Composable
+fun ChainsAnimatedOrientationDemo1() {
+    val boxColors = listOf(Color.Red, Color.Blue, Color.Green)
+    var isHorizontal by remember { mutableStateOf(true) }
+
+    Column(Modifier.fillMaxSize()) {
+        ConstraintLayout(
+            animateChanges = true, // Set to true, to automatically animate on ConstraintSet changes
+            animationSpec = tween(800),
+            modifier = Modifier
+                .fillMaxWidth()
+                .weight(1.0f, true)
+        ) {
+            val (box0, box1, box2) = createRefs()
+
+            if (isHorizontal) {
+                createHorizontalChain(
+                    box0,
+                    box1.withChainParams(8.dp, 8.dp, 8.dp, 8.dp),
+                    box2
+                )
+            } else {
+                createVerticalChain(
+                    box0,
+                    box1.withChainParams(8.dp, 8.dp, 8.dp, 8.dp),
+                    box2
+                )
+            }
+
+            Box(
+                modifier = Modifier
+                    .constrainAs(box0) {
+                        if (isHorizontal) {
+                            width = Dimension.fillToConstraints
+                            height = Dimension.value(20.dp)
+                            centerVerticallyTo(parent)
+                        } else {
+                            width = Dimension.value(20.dp)
+                            height = Dimension.fillToConstraints
+                            centerHorizontallyTo(parent)
+                        }
+                    }
+                    .background(boxColors[0])
+            )
+            Box(
+                modifier = Modifier
+                    .constrainAs(box1) {
+                        if (isHorizontal) {
+                            width = Dimension.fillToConstraints
+                            height = Dimension.ratio("2:1")
+                            centerVerticallyTo(parent)
+                        } else {
+                            width = Dimension.ratio("2:1")
+                            height = Dimension.fillToConstraints
+                            centerHorizontallyTo(parent)
+                        }
+                    }
+                    .background(boxColors[1])
+            )
+            Box(
+                modifier = Modifier
+                    .constrainAs(box2) {
+                        if (isHorizontal) {
+                            width = Dimension.fillToConstraints
+                            height = Dimension.value(20.dp)
+                            centerVerticallyTo(parent)
+                        } else {
+                            width = Dimension.value(20.dp)
+                            height = Dimension.fillToConstraints
+                            centerHorizontallyTo(parent)
+                        }
+                    }
+                    .background(boxColors[2])
+            )
+        }
+        Button(onClick = { isHorizontal = !isHorizontal }) {
+            Text(text = "Toggle Orientation")
+        }
+    }
+}
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/MotionLayoutActivity.kt b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/MotionLayoutActivity.kt
index 91a28e8..d3d54ab 100644
--- a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/MotionLayoutActivity.kt
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/MotionLayoutActivity.kt
@@ -32,6 +32,7 @@
 import androidx.constraintlayout.compose.integration.macrobenchmark.target.graphs.DynamicGraphsPreview
 import androidx.constraintlayout.compose.integration.macrobenchmark.target.newmessage.NewMotionMessagePreview
 import androidx.constraintlayout.compose.integration.macrobenchmark.target.newmessage.NewMotionMessagePreviewWithDsl
+import androidx.constraintlayout.compose.integration.macrobenchmark.target.newmessage.NewMotionMessagePreviewWithDslOptimized
 import androidx.constraintlayout.compose.integration.macrobenchmark.target.toolbar.MotionCollapseToolbarPreview
 
 class MotionLayoutActivity : ComponentActivity() {
@@ -55,15 +56,23 @@
                         "NewMessageJson" -> {
                             NewMotionMessagePreview()
                         }
+
                         "NewMessageDsl" -> {
                             NewMotionMessagePreviewWithDsl()
                         }
+
+                        "OptimizedNewMessageDsl" -> {
+                            NewMotionMessagePreviewWithDslOptimized()
+                        }
+
                         "CollapsibleToolbar" -> {
                             MotionCollapseToolbarPreview()
                         }
+
                         "DynamicGraphs" -> {
                             DynamicGraphsPreview()
                         }
+
                         else -> {
                             throw IllegalArgumentException("No Composable with name: $name")
                         }
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/newmessage/NewMessage.kt b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/newmessage/NewMessage.kt
index 0c12a36..7b952db 100644
--- a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/newmessage/NewMessage.kt
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark-target/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/target/newmessage/NewMessage.kt
@@ -64,6 +64,7 @@
 import androidx.constraintlayout.compose.ConstraintSet
 import androidx.constraintlayout.compose.Dimension
 import androidx.constraintlayout.compose.ExperimentalMotionApi
+import androidx.constraintlayout.compose.InvalidationStrategy
 import androidx.constraintlayout.compose.MotionLayout
 import androidx.constraintlayout.compose.MotionLayoutScope
 import androidx.constraintlayout.compose.MotionScene
@@ -75,19 +76,26 @@
 @Preview
 @Composable
 fun NewMotionMessagePreview() {
-    NewMotionMessageWithControls(useDsl = false)
+    NewMotionMessageWithControls(useDsl = false, optimize = false)
 }
 
 @Preview
 @Composable
 fun NewMotionMessagePreviewWithDsl() {
-    NewMotionMessageWithControls(useDsl = true)
+    NewMotionMessageWithControls(useDsl = true, optimize = false)
+}
+
+@Preview
+@Composable
+fun NewMotionMessagePreviewWithDslOptimized() {
+    NewMotionMessageWithControls(useDsl = true, optimize = true)
 }
 
 @OptIn(ExperimentalComposeUiApi::class, ExperimentalMotionApi::class)
 @Composable
 fun NewMotionMessageWithControls(
-    useDsl: Boolean
+    useDsl: Boolean,
+    optimize: Boolean
 ) {
     val initialLayout = NewMessageLayout.Full
     val newMessageState = rememberNewMessageState(initialLayoutState = initialLayout)
@@ -111,10 +119,20 @@
                 text = "Mini"
             )
         }
+        val invalidationStrategy = remember(newMessageState, optimize) {
+            if (optimize) {
+                InvalidationStrategy {
+                    newMessageState.currentState
+                }
+            } else {
+                InvalidationStrategy.DefaultInvalidationStrategy
+            }
+        }
         NewMessageButton(
             modifier = Modifier.fillMaxSize(),
             motionScene = motionScene,
             state = newMessageState,
+            invalidationStrategy = invalidationStrategy,
         )
     }
 }
@@ -533,16 +551,18 @@
 
 @Composable
 private fun NewMessageButton(
-    modifier: Modifier = Modifier,
     motionScene: MotionScene,
-    state: NewMessageState
+    state: NewMessageState,
+    invalidationStrategy: InvalidationStrategy,
+    modifier: Modifier = Modifier,
 ) {
     val currentStateName = state.currentState.name
     MotionLayout(
         motionScene = motionScene,
         animationSpec = tween(700),
         constraintSetName = currentStateName,
-        modifier = modifier
+        modifier = modifier,
+        invalidationStrategy = invalidationStrategy
     ) {
         MotionMessageContent(state = state)
     }
diff --git a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/MotionLayoutBenchmark.kt b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/MotionLayoutBenchmark.kt
index 73f439c..530ec36 100644
--- a/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/MotionLayoutBenchmark.kt
+++ b/constraintlayout/constraintlayout-compose/integration-tests/macrobenchmark/src/main/java/androidx/constraintlayout/compose/integration/macrobenchmark/MotionLayoutBenchmark.kt
@@ -50,6 +50,9 @@
     @Test
     fun messageDsl() = benchmarkRule.testNewMessage(NewMessageMode.Dsl)
 
+    @Test
+    fun messageOptimizedDsl() = benchmarkRule.testNewMessage(NewMessageMode.OptimizedDsl)
+
     /**
      * Transitions the Layout through its three different ConstraintSets using the MotionScene JSON.
      */
@@ -158,7 +161,8 @@
 
     internal enum class NewMessageMode(val composableName: String) {
         Json("NewMessageJson"),
-        Dsl("NewMessageDsl")
+        Dsl("NewMessageDsl"),
+        OptimizedDsl("OptimizedNewMessageDsl")
     }
 
     private fun UiDevice.waitForComposeIdle(timeoutMs: Long = 3000) {
diff --git a/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/ConstraintLayoutTest.kt b/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/ConstraintLayoutTest.kt
index ac64762..9af8cfb 100644
--- a/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/ConstraintLayoutTest.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/ConstraintLayoutTest.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.os.Build
+import androidx.compose.animation.core.tween
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.IntrinsicSize
@@ -71,6 +72,7 @@
 import androidx.test.filters.SdkSuppress
 import kotlin.math.roundToInt
 import kotlin.test.assertNotEquals
+import kotlin.test.assertTrue
 import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNull
@@ -2542,6 +2544,58 @@
         assertEquals(Offset(rootSizePx / 2f, rootSizePx / 2f).round(), box1Position)
     }
 
+    @Test
+    fun testAnimateChanges_withInlineDsl() = with(rule.density) {
+        val durationMs = 200
+        val rootSizePx = 100
+        val boxSizePx = 20
+        val expectedEndPosition = IntOffset(rootSizePx - boxSizePx, rootSizePx - boxSizePx)
+        var box0Position = IntOffset.Zero
+        val atTopLeftCorner = mutableStateOf(true)
+
+        rule.setContent {
+            ConstraintLayout(
+                modifier = Modifier.size(rootSizePx.toDp()),
+                animateChanges = true,
+                animationSpec = tween(durationMs)
+            ) {
+                val boxRef = createRef()
+                Box(
+                    Modifier
+                        .background(Color.Red)
+                        .constrainAs(boxRef) {
+                            width = boxSizePx.toDp().asDimension()
+                            height = boxSizePx.toDp().asDimension()
+                            if (atTopLeftCorner.value) {
+                                top.linkTo(parent.top)
+                                start.linkTo(parent.start)
+                            } else {
+                                bottom.linkTo(parent.bottom)
+                                end.linkTo(parent.end)
+                            }
+                        }
+                        .onGloballyPositioned {
+                            box0Position = it.positionInParent().round()
+                        }
+                )
+            }
+        }
+        rule.waitForIdle()
+        assertEquals(IntOffset.Zero, box0Position)
+
+        rule.mainClock.autoAdvance = false
+        atTopLeftCorner.value = false
+
+        rule.mainClock.advanceTimeBy(durationMs / 2L)
+        rule.waitForIdle()
+        assertTrue(box0Position.x > 0 && box0Position.y > 0)
+        assertTrue(box0Position.x < expectedEndPosition.x && box0Position.y < expectedEndPosition.y)
+
+        rule.mainClock.autoAdvance = true
+        rule.waitForIdle()
+        assertEquals(expectedEndPosition, box0Position)
+    }
+
     private fun listAnchors(box: ConstrainedLayoutReference): List<ConstrainScope.() -> Unit> {
         // TODO(172055763) directly construct an immutable list when Lint supports it
         val anchors = mutableListOf<ConstrainScope.() -> Unit>()
diff --git a/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/MotionLayoutTest.kt b/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/MotionLayoutTest.kt
index ad4c332..da53a92 100644
--- a/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/MotionLayoutTest.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/MotionLayoutTest.kt
@@ -44,6 +44,7 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
+import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Rect
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.toArgb
@@ -660,6 +661,234 @@
         assertEquals(14, actualTextSize.height.value.roundToInt())
     }
 
+    @Test
+    fun testOnSwipe_withLimitBounds() = with(rule.density) {
+        val rootSizePx = 300
+        val boxSizePx = 30
+        val boxId = "box"
+        var boxPosition = IntOffset.Zero
+
+        rule.setContent {
+            MotionLayout(
+                motionScene = remember {
+                    createCornerToCornerMotionScene(
+                        boxId = boxId,
+                        boxSizePx = boxSizePx
+                    ) { boxRef ->
+                        onSwipe = OnSwipe(
+                            anchor = boxRef,
+                            side = SwipeSide.End,
+                            direction = SwipeDirection.End,
+                            limitBoundsTo = boxRef
+                        )
+                    }
+                },
+                progress = 0f,
+                modifier = Modifier
+                    .layoutTestId("MyMotion")
+                    .size(rootSizePx.toDp())
+            ) {
+                Box(
+                    Modifier
+                        .background(Color.Red)
+                        .layoutTestId(boxId)
+                        .onGloballyPositioned {
+                            boxPosition = it
+                                .positionInParent()
+                                .round()
+                        }
+                )
+            }
+        }
+        rule.waitForIdle()
+        val motionSemantic = rule.onNodeWithTag("MyMotion")
+        motionSemantic
+            .assertExists()
+            // The first swipe will completely miss the Box, so it shouldn't move
+            .performSwipe(
+                from = {
+                    Offset(left + boxSizePx / 2, centerY)
+                },
+                to = {
+                    Offset(right * 0.9f, centerY)
+                }
+            )
+        // Wait a frame for the Touch Up animation to start
+        rule.mainClock.advanceTimeByFrame()
+        // Then wait for it to end
+        rule.waitForIdle()
+        // Box didn't move since the swipe didn't start within the box
+        assertEquals(IntOffset.Zero, boxPosition)
+
+        motionSemantic
+            .assertExists()
+            // The second swipe will start within the Box
+            .performSwipe(
+                from = {
+                    Offset(left + boxSizePx / 2, top + boxSizePx / 2)
+                },
+                to = {
+                    Offset(right * 0.9f, centerY)
+                }
+            )
+        // Wait a frame for the Touch Up animation to start
+        rule.mainClock.advanceTimeByFrame()
+        // Then wait for it to end
+        rule.waitForIdle()
+        // Box moved to end
+        assertEquals(IntOffset(rootSizePx - boxSizePx, rootSizePx - boxSizePx), boxPosition)
+    }
+
+    @Test
+    fun testInvalidationStrategy_onObservedStateChange() = with(rule.density) {
+        val rootSizePx = 200
+        val progress = mutableStateOf(0f)
+        val textContent = mutableStateOf("Foo")
+        val optimizeCorrectly = mutableStateOf(false)
+        val textId = "text"
+
+        rule.setContent {
+            WithConsistentTextStyle {
+                MotionLayout(
+                    motionScene = remember {
+                        MotionScene {
+                            val textRef = createRefFor(textId)
+
+                            defaultTransition(
+                                from = constraintSet {
+                                    constrain(textRef) {
+                                        centerTo(parent)
+                                    }
+                                },
+                                to = constraintSet {
+                                    constrain(textRef) {
+                                        centerTo(parent)
+                                    }
+                                }
+                            )
+                        }
+                    },
+                    progress = progress.value,
+                    modifier = Modifier.size(rootSizePx.toDp()),
+                    invalidationStrategy = remember(optimizeCorrectly.value) {
+                        if (optimizeCorrectly.value) {
+                            InvalidationStrategy {
+                                textContent.value
+                            }
+                        } else {
+                            InvalidationStrategy {
+                                // Do not invalidate on recomposition
+                            }
+                        }
+                    }
+                ) {
+                    Text(
+                        text = textContent.value,
+                        fontSize = 10.sp,
+                        modifier = Modifier.layoutTestId(textId)
+                    )
+                }
+            }
+        }
+
+        rule.waitForIdle()
+        var actualTextSize = rule.onNodeWithTag(textId).getUnclippedBoundsInRoot()
+        assertEquals(18, actualTextSize.width.value.roundToInt())
+        assertEquals(14, actualTextSize.height.value.roundToInt())
+
+        textContent.value = "Foo\nBar"
+
+        // Because we are optimizing "incorrectly" the text layout remains unchanged
+        rule.waitForIdle()
+        actualTextSize = rule.onNodeWithTag(textId).getUnclippedBoundsInRoot()
+        assertEquals(18, actualTextSize.width.value.roundToInt())
+        assertEquals(14, actualTextSize.height.value.roundToInt())
+
+        textContent.value = "Foo"
+        optimizeCorrectly.value = true
+
+        // We change the text back and update the optimization strategy to be correct, text should
+        // be the same as in its initial state
+        rule.waitForIdle()
+        actualTextSize = rule.onNodeWithTag(textId).getUnclippedBoundsInRoot()
+        assertEquals(18, actualTextSize.width.value.roundToInt())
+        assertEquals(14, actualTextSize.height.value.roundToInt())
+
+        textContent.value = "Foo\nBar"
+
+        // With the appropriate optimization strategy, the layout is invalidated when the text
+        // changes
+        rule.waitForIdle()
+        actualTextSize = rule.onNodeWithTag(textId).getUnclippedBoundsInRoot()
+        assertEquals(18, actualTextSize.width.value.roundToInt())
+        assertEquals(25, actualTextSize.height.value.roundToInt())
+    }
+
+    @Test
+    fun testOnSwipe_withDragScale() = with(rule.density) {
+        val rootSizePx = 300
+        val boxSizePx = 30
+        val boxId = "box"
+        val dragScale = 3f
+        var boxPosition = IntOffset.Zero
+
+        rule.setContent {
+            MotionLayout(
+                motionScene = remember {
+                    createCornerToCornerMotionScene(
+                        boxId = boxId,
+                        boxSizePx = boxSizePx
+                    ) { boxRef ->
+                        onSwipe = OnSwipe(
+                            anchor = boxRef,
+                            side = SwipeSide.Middle,
+                            direction = SwipeDirection.Down,
+                            onTouchUp = SwipeTouchUp.ToStart,
+                            dragScale = dragScale
+                        )
+                    }
+                },
+                progress = 0f,
+                modifier = Modifier
+                    .layoutTestId("MyMotion")
+                    .size(rootSizePx.toDp())
+            ) {
+                Box(
+                    Modifier
+                        .background(Color.Red)
+                        .layoutTestId(boxId)
+                        .onGloballyPositioned {
+                            boxPosition = it
+                                .positionInParent()
+                                .round()
+                        }
+                )
+            }
+        }
+        rule.waitForIdle()
+        val motionSemantic = rule.onNodeWithTag("MyMotion")
+
+        motionSemantic
+            .assertExists()
+            .performSwipe(
+                from = {
+                    Offset(center.x, top + (boxSizePx / 2f))
+                },
+                to = {
+                    // Move only half-way, with a dragScale of 1f, it would be forced to
+                    // return to the start position
+                    val off = ((bottom - (boxSizePx / 2f)) - (top + (boxSizePx / 2f))) * 0.5f
+                    Offset(center.x, (top + (boxSizePx / 2f)) + off)
+                }
+            )
+        // Wait a frame for the Touch Up animation to start
+        rule.mainClock.advanceTimeByFrame()
+        // Then wait for it to end
+        rule.waitForIdle()
+        // Box is at the ending position because of the increased dragScale
+        assertEquals(IntOffset(rootSizePx - boxSizePx, rootSizePx - boxSizePx), boxPosition)
+    }
+
     private fun Color.toHexString(): String = toArgb().toUInt().toString(16)
 }
 
@@ -718,7 +947,6 @@
 private fun WithConsistentTextStyle(
     content: @Composable () -> Unit
 ) {
-    @Suppress("DEPRECATION")
     CompositionLocalProvider(
         LocalDensity provides Density(1f, 1f),
         LocalTextStyle provides TextStyle(
@@ -729,3 +957,34 @@
         content = content
     )
 }
+
+private fun Density.createCornerToCornerMotionScene(
+    boxId: String,
+    boxSizePx: Int,
+    transitionContent: TransitionScope.(boxRef: ConstrainedLayoutReference) -> Unit
+) = MotionScene {
+    val boxRef = createRefFor(boxId)
+
+    defaultTransition(
+        from = constraintSet {
+            constrain(boxRef) {
+                width = boxSizePx.toDp().asDimension()
+                height = boxSizePx.toDp().asDimension()
+
+                top.linkTo(parent.top)
+                start.linkTo(parent.start)
+            }
+        },
+        to = constraintSet {
+            constrain(boxRef) {
+                width = boxSizePx.toDp().asDimension()
+                height = boxSizePx.toDp().asDimension()
+
+                bottom.linkTo(parent.bottom)
+                end.linkTo(parent.end)
+            }
+        }
+    ) {
+        transitionContent(boxRef)
+    }
+}
diff --git a/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/OnSwipeTest.kt b/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/OnSwipeTest.kt
index 056cee8..324b226 100644
--- a/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/OnSwipeTest.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/OnSwipeTest.kt
@@ -24,7 +24,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.lerp
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
 import androidx.compose.ui.platform.testTag
@@ -33,10 +32,8 @@
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.performTouchInput
 import androidx.compose.ui.unit.dp
-import androidx.compose.ui.util.lerp
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
-import kotlin.math.roundToInt
 import org.junit.After
 import org.junit.Before
 import org.junit.Ignore
@@ -78,32 +75,15 @@
         val motionSemantic = rule.onNodeWithTag("MyMotion")
         motionSemantic
             .assertExists()
-            .performTouchInput {
-                // Do a periodic swipe between two points that lasts 500ms
-                val start = Offset(right * 0.25f, centerY)
-                val end = Offset(right * 0.5f, centerY)
-                val durationMillis = 500L
-                val durationMillisFloat = durationMillis.toFloat()
-
-                // Start touch input
-                down(0, start)
-
-                val steps = (durationMillisFloat / eventPeriodMillis.toFloat()).roundToInt()
-                var step = 0
-
-                val getPositionAt: (Long) -> Offset = {
-                    lerp(start, end, it.toFloat() / durationMillis)
-                }
-
-                var tP = 0L
-                while (step++ < steps) {
-                    val progress = step / steps.toFloat()
-                    val tn = lerp(0, durationMillis, progress)
-                    updatePointerTo(0, getPositionAt(tn))
-                    move(tn - tP)
-                    tP = tn
-                }
-            }
+            .performSwipe(
+                from = {
+                    Offset(right * 0.25f, centerY)
+                },
+                to = {
+                    Offset(right * 0.5f, centerY)
+                },
+                endWithUp = false
+            )
         rule.onNodeWithTag("box").assertPositionInRootIsEqualTo(51.6.dp, 128.3.dp)
         motionSemantic
             .performTouchInput {
diff --git a/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/Utils.kt b/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/Utils.kt
index 3ba47d7..18609a9 100644
--- a/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/Utils.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidAndroidTest/kotlin/androidx/constraintlayout/compose/Utils.kt
@@ -17,8 +17,15 @@
 package androidx.constraintlayout.compose
 
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.lerp
 import androidx.compose.ui.layout.layoutId
 import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.SemanticsNodeInteraction
+import androidx.compose.ui.test.TouchInjectionScope
+import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.util.lerp
+import kotlin.math.roundToInt
 
 /**
  * Applies the [id] to the [layoutId] and [testTag] Modifiers.
@@ -26,3 +33,56 @@
  * This allows using syntax such as: `rule.onNodeWithTag(id)...`
  */
 internal fun Modifier.layoutTestId(id: Any): Modifier = testTag(id.toString()).layoutId(id)
+
+/**
+ * Helper method that will simulate a swipe on the given [SemanticsNodeInteraction].
+ *
+ * Use [from] and [to] to calculate the starting and ending position. [TouchInjectionScope]
+ * includes the dimension of the layout: [TouchInjectionScope.left], [TouchInjectionScope.center],
+ * etc.
+ *
+ * If [endWithUp] is false, the touch pointer will remain down at [to]. In which case you'll have
+ * to make sure you lift the pointer later on, eg:
+ *
+ * ```
+ * rule.onNodeWithTag("MyTag")
+ *     .performTouchInput {
+ *         up()
+ *     }
+ * ```
+ */
+internal fun SemanticsNodeInteraction.performSwipe(
+    from: TouchInjectionScope.() -> Offset,
+    to: TouchInjectionScope.() -> Offset,
+    endWithUp: Boolean = true
+) {
+    performTouchInput {
+        // Do a periodic swipe between two points that lasts 500ms
+        val start = from()
+        val end = to()
+        val durationMillis = 500L
+        val durationMillisFloat = durationMillis.toFloat()
+
+        // Start touch input
+        down(0, start)
+
+        val steps = (durationMillisFloat / eventPeriodMillis.toFloat()).roundToInt()
+        var step = 0
+
+        val getPositionAt: (Long) -> Offset = {
+            lerp(start, end, it.toFloat() / durationMillis)
+        }
+
+        var tP = 0L
+        while (step++ < steps) {
+            val progress = step / steps.toFloat()
+            val tn = lerp(0, durationMillis, progress)
+            updatePointerTo(0, getPositionAt(tn))
+            move(tn - tP)
+            tP = tn
+        }
+        if (endWithUp) {
+            up()
+        }
+    }
+}
diff --git a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayout.kt b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayout.kt
index 6d9fd43..80dd5e3 100644
--- a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayout.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayout.kt
@@ -64,6 +64,7 @@
 import androidx.compose.ui.layout.ParentDataModifier
 import androidx.compose.ui.layout.Placeable
 import androidx.compose.ui.layout.layoutId
+import androidx.compose.ui.node.Ref
 import androidx.compose.ui.platform.InspectorValueInfo
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.debugInspectorInfo
@@ -353,13 +354,83 @@
  * variables or configuration changes, consider using the [ConstraintSet] pattern instead, makes it
  * clearer to distinguish different layouts and allows you to automatically animate the layout when
  * the provided [ConstraintSet] is different.
+ *
+ * @param modifier Modifier to apply to this layout node.
+ * @param optimizationLevel Optimization flags for ConstraintLayout. The default is
+ * [Optimizer.OPTIMIZATION_STANDARD].
+ * @param animateChanges When enabled, ConstraintLayout will animate the layout if there were any
+ * changes on the constraints during recomposition. If there's a change while the layout is still
+ * animating the current animation will always complete before animating to the latest changes.
+ * @param animationSpec The [AnimationSpec] used for [animateChanges]. [tween] by default.
+ * @param finishedAnimationListener Lambda called whenever an animation due to [animateChanges]
+ * finishes.
+ * @param content Content of this layout node.
  */
+@SuppressLint("AutoboxingStateCreation")
 @Composable
 inline fun ConstraintLayout(
     modifier: Modifier = Modifier,
     optimizationLevel: Int = Optimizer.OPTIMIZATION_STANDARD,
+    animateChanges: Boolean = false,
+    animationSpec: AnimationSpec<Float> = tween<Float>(),
+    noinline finishedAnimationListener: (() -> Unit)? = null,
     crossinline content: @Composable ConstraintLayoutScope.() -> Unit
 ) {
+    if (animateChanges) {
+        val start: MutableState<ConstraintSet?> = remember { mutableStateOf(null) }
+        val end: MutableState<ConstraintSet?> = remember { mutableStateOf(null) }
+        val scope = remember { ConstraintLayoutScope().apply { isAnimateChanges = true } }
+        val contentTracker = remember { mutableStateOf(Unit, neverEqualPolicy()) }
+        val compositionSource =
+            remember { Ref<CompositionSource>().apply { value = CompositionSource.Unknown } }
+        val channel = remember { Channel<ConstraintSet>(Channel.CONFLATED) }
+
+        val contentDelegate: @Composable () -> Unit = {
+            // Perform a reassignment to the State tracker, this will force readers to recompose at
+            // the same pass as the content. The only expected reader is our MeasurePolicy.
+            contentTracker.value = Unit
+
+            if (compositionSource.value == CompositionSource.Unknown) {
+                // Set the content as the original composition source if the MotionLayout was not
+                // recomposed by the caller or by itself
+                compositionSource.value = CompositionSource.Content
+            }
+
+            // Resetting the scope also resets the underlying ConstraintSet
+            scope.reset()
+            content(scope) // The ConstraintSet is built at this step
+
+            SideEffect {
+                // Extract a copy of the underlying ConstraintSet and send it through the channel
+                // We do it within a SideEffect to avoid a recomposition loop from reading and
+                // writing the State variables for `end` and `start`
+                val cSet = RawConstraintSet(scope.containerObject.clone())
+                if (start.value == null || end.value == null) {
+                    // guarantee first constraintSet here
+                    start.value = cSet
+                    end.value = start.value
+                } else {
+                    // send to channel
+                    channel.trySend(cSet)
+                }
+            }
+        }
+
+        LateMotionLayout(
+            start = start,
+            end = end,
+            animationSpec = animationSpec,
+            channel = channel,
+            contentTracker = contentTracker,
+            compositionSource = compositionSource,
+            optimizationLevel = optimizationLevel,
+            finishedAnimationListener = finishedAnimationListener,
+            modifier = modifier,
+            content = contentDelegate
+        )
+        return
+    }
+
     val density = LocalDensity.current
     val measurer = remember { Measurer(density) }
     val scope = remember { ConstraintLayoutScope() }
@@ -663,6 +734,19 @@
  *
  * If more control is needed, we recommend using [MotionLayout] instead, which has a very similar
  * pattern through the [MotionScene] object.
+ *
+ * @param constraintSet The [ConstraintSet] that describes the expected layout, defined references
+ * should be bound to Composables with [Modifier.layoutId][androidx.compose.ui.layout.layoutId].
+ * @param modifier Modifier to apply to this layout node.
+ * @param optimizationLevel Optimization flags for ConstraintLayout. The default is
+ * [Optimizer.OPTIMIZATION_STANDARD].
+ * @param animateChanges When enabled, ConstraintLayout will animate the layout if there were any
+ * changes on the constraints during recomposition. If there's a change while the layout is still
+ * animating the current animation will always complete before animating to the latest changes.
+ * @param animationSpec The [AnimationSpec] used for [animateChanges]. [tween] by default.
+ * @param finishedAnimationListener Lambda called whenever an animation due to [animateChanges]
+ * finishes.
+ * @param content Content of this layout node.
  */
 @OptIn(ExperimentalMotionApi::class)
 @Suppress("NOTHING_TO_INLINE")
@@ -797,6 +881,14 @@
     fun createRefs(): ConstraintLayoutScope.ConstrainedLayoutReferences =
         referencesObject ?: ConstrainedLayoutReferences().also { referencesObject = it }
 
+    /**
+     * Indicates whether we expect to animate changes. This is important since normally
+     * ConstraintLayout evaluates constraints at the measure step, but MotionLayout needs to know
+     * the constraints to enter the measure step.
+     */
+    @PublishedApi
+    internal var isAnimateChanges = false
+
     private var referencesObject: ConstrainedLayoutReferences? = null
 
     private val ChildrenStartIndex = 0
@@ -837,7 +929,15 @@
     fun Modifier.constrainAs(
         ref: ConstrainedLayoutReference,
         constrainBlock: ConstrainScope.() -> Unit
-    ) = this.then(ConstrainAsModifier(ref, constrainBlock))
+    ): Modifier {
+        if (isAnimateChanges) {
+            // When we are expecting to animate changes, we need to preemptively obtain the
+            // constraints from the DSL since MotionLayout is not designed to evaluate the DSL
+            val container = ref.asCLContainer()
+            ConstrainScope(ref.id, container).constrainBlock()
+        }
+        return this.then(ConstrainAsModifier(ref, constrainBlock))
+    }
 
     @Stable
     private class ConstrainAsModifier(
diff --git a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayoutBaseScope.kt b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayoutBaseScope.kt
index 392cf7e..03d4dc7 100644
--- a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayoutBaseScope.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintLayoutBaseScope.kt
@@ -39,6 +39,7 @@
     @Deprecated("Tasks is unused, it breaks the immutability promise.")
     protected val tasks = mutableListOf<(State) -> Unit>()
 
+    @PublishedApi
     internal val containerObject: CLObject = extendFrom?.clone() ?: CLObject(charArrayOf())
 
     fun applyTo(state: State) {
diff --git a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintSet.kt b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintSet.kt
index 77d4deb..7cf3901 100644
--- a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintSet.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/ConstraintSet.kt
@@ -18,6 +18,9 @@
 
 import androidx.compose.runtime.Immutable
 import androidx.compose.ui.layout.Measurable
+import androidx.constraintlayout.core.parser.CLObject
+import androidx.constraintlayout.core.state.ConstraintSetParser
+import androidx.constraintlayout.core.state.ConstraintSetParser.LayoutVariables
 import androidx.constraintlayout.core.state.Transition
 
 @JvmDefaultWithCompatibility
@@ -298,3 +301,37 @@
      */
     fun applyToState(state: State)
 }
+
+/**
+ * [ConstraintSet] defined solely on the given [clObject]. Only meant to be used to extract a copy
+ * of the underlying ConstraintSet of ConstraintLayout with the inline Modifier DSL.
+ *
+ * You likely don't mean to use this.
+ */
+@Immutable
+@PublishedApi
+internal class RawConstraintSet(private val clObject: CLObject) : ConstraintSet {
+    private val layoutVariables = LayoutVariables()
+    override fun applyTo(state: State, measurables: List<Measurable>) {
+        ConstraintSetParser.populateState(
+            clObject,
+            state,
+            layoutVariables
+        )
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as RawConstraintSet
+
+        if (clObject != other.clObject) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        return clObject.hashCode()
+    }
+}
diff --git a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/LateMotionLayout.kt b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/LateMotionLayout.kt
new file mode 100644
index 0000000..1a0d0474d
--- /dev/null
+++ b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/LateMotionLayout.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.constraintlayout.compose
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.State
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.MeasurePolicy
+import androidx.compose.ui.layout.MultiMeasureLayout
+import androidx.compose.ui.node.Ref
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.semantics.semantics
+import kotlinx.coroutines.channels.Channel
+
+/**
+ * A version of [MotionLayout] that obtains its [ConstraintSet]s at measure time.
+ *
+ * Note that this version has limited functionality and DOES NOT support on swipe features.
+ *
+ * It's only meant to be used by ConstraintLayout to animate changes.
+ */
+@PublishedApi
+@Composable
+internal fun LateMotionLayout(
+    start: MutableState<ConstraintSet?>,
+    end: MutableState<ConstraintSet?>,
+    animationSpec: AnimationSpec<Float>,
+    channel: Channel<ConstraintSet>,
+    contentTracker: State<Unit>,
+    compositionSource: Ref<CompositionSource>,
+    optimizationLevel: Int,
+    finishedAnimationListener: (() -> Unit)?,
+    modifier: Modifier,
+    content: @Composable () -> Unit
+) {
+    val density = LocalDensity.current
+    val measurer = remember { MotionMeasurer(density) }
+
+    val animatableProgress = remember { Animatable(0.0f) }
+    val motionProgress = remember { animatableProgress.asState() }
+    val direction = remember { mutableIntStateOf(1) }
+
+    // Start and end are guaranteed to be non-null when the lambda is invoked at the measure
+    // step.
+    val measurePolicy = lateMotionLayoutMeasurePolicy(
+        startProvider = remember { { start.value!! } },
+        endProvider = remember { { end.value!! } },
+        contentTracker = contentTracker,
+        compositionSource = compositionSource,
+        motionProgress = motionProgress,
+        measurer = measurer,
+        optimizationLevel = optimizationLevel
+    )
+
+    @Suppress("DEPRECATION")
+    MultiMeasureLayout(
+        modifier = modifier
+            .semantics { designInfoProvider = measurer },
+        measurePolicy = measurePolicy,
+        content = content
+    )
+
+    LaunchedEffect(channel) {
+        for (constraints in channel) {
+            val newConstraints = channel.tryReceive().getOrNull() ?: constraints
+            val currentConstraints =
+                if (direction.intValue == 1) start.value else end.value
+            if (newConstraints != currentConstraints) {
+                if (direction.intValue == 1) {
+                    end.value = newConstraints
+                } else {
+                    start.value = newConstraints
+                }
+                // Force invalidate, since we don't support all MotionLayout features here, we
+                // can do this instead of calling MotionMeasurer.initWith
+                compositionSource.value = CompositionSource.Content
+
+                animatableProgress.animateTo(direction.intValue.toFloat(), animationSpec)
+                direction.intValue = if (direction.intValue == 1) 0 else 1
+                finishedAnimationListener?.invoke()
+            }
+        }
+    }
+}
+
+/**
+ * Same as [motionLayoutMeasurePolicy] but the [ConstraintSet] objects are not available at call
+ * time.
+ */
+private fun lateMotionLayoutMeasurePolicy(
+    startProvider: () -> ConstraintSet,
+    endProvider: () -> ConstraintSet,
+    contentTracker: State<Unit>,
+    compositionSource: Ref<CompositionSource>,
+    motionProgress: State<Float>,
+    measurer: MotionMeasurer,
+    optimizationLevel: Int,
+): MeasurePolicy =
+    MeasurePolicy { measurables, constraints ->
+        // Do a state read, to guarantee that we control measure when the content recomposes without
+        // notifying our Composable caller
+        contentTracker.value
+
+        val layoutSize = measurer.performInterpolationMeasure(
+            constraints = constraints,
+            layoutDirection = this.layoutDirection,
+            constraintSetStart = startProvider(),
+            constraintSetEnd = endProvider(),
+            transition = TransitionImpl.EMPTY,
+            measurables = measurables,
+            optimizationLevel = optimizationLevel,
+            progress = motionProgress.value,
+            compositionSource = compositionSource.value ?: CompositionSource.Unknown,
+            invalidateOnConstraintsCallback = null
+        )
+        compositionSource.value = CompositionSource.Unknown // Reset after measuring
+
+        layout(layoutSize.width, layoutSize.height) {
+            with(measurer) {
+                performLayout(measurables)
+            }
+        }
+    }
diff --git a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionDragHandler.kt b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionDragHandler.kt
index 6d198ae..c46d25a 100644
--- a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionDragHandler.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionDragHandler.kt
@@ -16,14 +16,21 @@
 
 package androidx.constraintlayout.compose
 
-import androidx.compose.foundation.gestures.detectDragGestures
+import android.annotation.SuppressLint
+import androidx.compose.foundation.gestures.awaitEachGesture
+import androidx.compose.foundation.gestures.awaitFirstDown
+import androidx.compose.foundation.gestures.awaitTouchSlopOrCancellation
+import androidx.compose.foundation.gestures.drag
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.MutableFloatState
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.composed
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.pointer.PointerInputChange
+import androidx.compose.ui.input.pointer.PointerInputScope
 import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.input.pointer.positionChange
 import androidx.compose.ui.input.pointer.util.VelocityTracker
 import androidx.compose.ui.input.pointer.util.addPointerInputChange
 import androidx.compose.ui.platform.debugInspectorInfo
@@ -96,8 +103,11 @@
     }
     return@composed this.pointerInput(key) {
         val velocityTracker = VelocityTracker()
-        detectDragGestures(
-            onDragStart = {
+        detectDragGesturesWhenNeeded(
+            onAcceptFirstDown = { offset ->
+                swipeHandler.onAcceptFirstDownForOnSwipe(offset)
+            },
+            onDragStart = { _ ->
                 velocityTracker.resetTracking()
             },
             onDragEnd = {
@@ -105,12 +115,19 @@
                     // Indicate that the swipe has ended, MotionLayout should animate the rest.
                     MotionDragState.onDragEnd(velocityTracker.calculateVelocity())
                 )
+            },
+            onDragCancel = {
+                dragChannel.trySend(
+                    // Indicate that the swipe has ended, MotionLayout should animate the rest.
+                    MotionDragState.onDragEnd(velocityTracker.calculateVelocity())
+                )
+            },
+            onDrag = { change, dragAmount ->
+                velocityTracker.addPointerInputChange(change)
+                // As dragging is done, pass the dragAmount to update the MotionLayout progress.
+                dragChannel.trySend(MotionDragState.onDrag(dragAmount))
             }
-        ) { change, dragAmount ->
-            velocityTracker.addPointerInputChange(change)
-            // As dragging is done, pass the dragAmount to update the MotionLayout progress.
-            dragChannel.trySend(MotionDragState.onDrag(dragAmount))
-        }
+        )
     }
 }
 
@@ -139,3 +156,47 @@
             )
     }
 }
+
+/**
+ * Copy of [androidx.compose.foundation.gestures.detectDragGestures] with the opportunity to decide
+ * whether we consume the rest of the drag with [onAcceptFirstDown].
+ */
+@SuppressLint("PrimitiveInLambda")
+private suspend fun PointerInputScope.detectDragGesturesWhenNeeded(
+    onAcceptFirstDown: (Offset) -> Boolean,
+    onDragStart: (Offset) -> Unit,
+    onDragEnd: () -> Unit,
+    onDragCancel: () -> Unit,
+    onDrag: (change: PointerInputChange, dragAmount: Offset) -> Unit
+) {
+    awaitEachGesture {
+        val down = awaitFirstDown(requireUnconsumed = true)
+        if (!onAcceptFirstDown(down.position)) {
+            return@awaitEachGesture
+        }
+        var drag: PointerInputChange?
+        var overSlop = Offset.Zero
+        do {
+            drag = awaitTouchSlopOrCancellation(
+                pointerId = down.id
+            ) { change, over ->
+                change.consume()
+                overSlop = over
+            }
+        } while (drag != null && !drag.isConsumed)
+        if (drag != null) {
+            onDragStart.invoke(drag.position)
+            onDrag(drag, overSlop)
+            if (
+                !drag(drag.id) {
+                    onDrag(it, it.positionChange())
+                    it.consume()
+                }
+            ) {
+                onDragCancel()
+            } else {
+                onDragEnd()
+            }
+        }
+    }
+}
diff --git a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionLayout.kt b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionLayout.kt
index eaa32df..18e7f36 100644
--- a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionLayout.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionLayout.kt
@@ -26,6 +26,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.MutableFloatState
+import androidx.compose.runtime.MutableState
 import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.State
 import androidx.compose.runtime.getValue
@@ -35,6 +36,7 @@
 import androidx.compose.runtime.neverEqualPolicy
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshots.Snapshot
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.composed
 import androidx.compose.ui.draw.drawBehind
@@ -51,11 +53,13 @@
 import androidx.compose.ui.platform.LocalView
 import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.TextUnit
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import androidx.constraintlayout.core.widgets.Optimizer
+import kotlin.math.absoluteValue
 import kotlinx.coroutines.channels.Channel
 
 /**
@@ -114,9 +118,12 @@
  * @param modifier Modifier to apply to this layout node.
  * @param transition Defines the interpolation parameters between the [ConstraintSet]s to achieve
  * fine-tuned animations.
+ * @param debugFlags Flags to enable visual debugging. [DebugFlags.None] by default.
  * @param optimizationLevel Optimization parameter for the underlying ConstraintLayout,
  * [Optimizer.OPTIMIZATION_STANDARD] by default.
- * @param debugFlags Flags to enable visual debugging. [DebugFlags.None] by default.
+ * @param invalidationStrategy Provides strategies to optimize invalidations in [MotionLayout].
+ * Excessive invalidations will be the typical cause of bad performance in [MotionLayout]. See
+ * [InvalidationStrategy] to learn how to apply common strategies.
  * @param content The content to be laid out by MotionLayout, note that each layout Composable
  * should be bound to an ID defined in the [ConstraintSet]s using
  * [Modifier.layoutId][androidx.compose.ui.layout.layoutId].
@@ -130,6 +137,7 @@
     transition: Transition? = null,
     debugFlags: DebugFlags = DebugFlags.None,
     optimizationLevel: Int = Optimizer.OPTIMIZATION_STANDARD,
+    invalidationStrategy: InvalidationStrategy = InvalidationStrategy.DefaultInvalidationStrategy,
     crossinline content: @Composable MotionLayoutScope.() -> Unit
 ) {
     /**
@@ -150,7 +158,8 @@
         // the same pass as the content. The only expected reader is our MeasurePolicy.
         contentTracker.value = Unit
 
-        if (compositionSource.value == CompositionSource.Unknown) {
+        if (invalidationStrategy.onObservedStateChange == null &&
+            compositionSource.value == CompositionSource.Unknown) {
             // Set the content as the original composition source if the MotionLayout was not
             // recomposed by the caller or by itself
             compositionSource.value = CompositionSource.Content
@@ -170,6 +179,7 @@
         modifier = modifier,
         contentTracker = contentTracker,
         compositionSource = compositionSource,
+        invalidationStrategy = invalidationStrategy,
         content = contentDelegate
     )
 }
@@ -219,9 +229,12 @@
  * @param modifier Modifier to apply to this layout node.
  * @param transitionName The name of the transition to apply on the layout. By default, it will
  * target the transition defined with [MotionSceneScope.defaultTransition].
+ * @param debugFlags Flags to enable visual debugging. [DebugFlags.None] by default.
  * @param optimizationLevel Optimization parameter for the underlying ConstraintLayout,
  * [Optimizer.OPTIMIZATION_STANDARD] by default.
- * @param debugFlags Flags to enable visual debugging. [DebugFlags.None] by default.
+ * @param invalidationStrategy Provides strategies to optimize invalidations in [MotionLayout].
+ * Excessive invalidations will be the typical cause of bad performance in [MotionLayout]. See
+ * [InvalidationStrategy] to learn how to apply common strategies.
  * @param content The content to be laid out by MotionLayout, note that each layout Composable
  * should be bound to an ID defined in the [ConstraintSet]s using
  * [Modifier.layoutId][androidx.compose.ui.layout.layoutId].
@@ -234,6 +247,7 @@
     transitionName: String = "default",
     debugFlags: DebugFlags = DebugFlags.None,
     optimizationLevel: Int = Optimizer.OPTIMIZATION_STANDARD,
+    invalidationStrategy: InvalidationStrategy = InvalidationStrategy.DefaultInvalidationStrategy,
     crossinline content: @Composable (MotionLayoutScope.() -> Unit),
 ) {
     /**
@@ -254,7 +268,8 @@
         // the same pass as the content. The only expected reader is our MeasurePolicy.
         contentTracker.value = Unit
 
-        if (compositionSource.value == CompositionSource.Unknown) {
+        if (invalidationStrategy.onObservedStateChange == null &&
+            compositionSource.value == CompositionSource.Unknown) {
             // Set the content as the original composition source if the MotionLayout was not
             // recomposed by the caller or by itself
             compositionSource.value = CompositionSource.Content
@@ -271,6 +286,7 @@
         modifier = modifier,
         contentTracker = contentTracker,
         compositionSource = compositionSource,
+        invalidationStrategy = invalidationStrategy,
         content = contentDelegate
     )
 }
@@ -338,9 +354,12 @@
  * @param modifier Modifier to apply to this layout node.
  * @param finishedAnimationListener Called when an animation triggered by a change in
  * [constraintSetName] has ended.
+ * @param debugFlags Flags to enable visual debugging. [DebugFlags.None] by default.
  * @param optimizationLevel Optimization parameter for the underlying ConstraintLayout,
  * [Optimizer.OPTIMIZATION_STANDARD] by default.
- * @param debugFlags Flags to enable visual debugging. [DebugFlags.None] by default.
+ * @param invalidationStrategy Provides strategies to optimize invalidations in [MotionLayout].
+ * Excessive invalidations will be the typical cause of bad performance in [MotionLayout]. See
+ * [InvalidationStrategy] to learn how to apply common strategies.
  * @param content The content to be laid out by MotionLayout, note that each layout Composable
  * should be bound to an ID defined in the [ConstraintSet]s using
  * [Modifier.layoutId][androidx.compose.ui.layout.layoutId].
@@ -354,6 +373,7 @@
     noinline finishedAnimationListener: (() -> Unit)? = null,
     debugFlags: DebugFlags = DebugFlags.None,
     optimizationLevel: Int = Optimizer.OPTIMIZATION_STANDARD,
+    invalidationStrategy: InvalidationStrategy = InvalidationStrategy.DefaultInvalidationStrategy,
     @Suppress("HiddenTypeParameter")
     crossinline content: @Composable (MotionLayoutScope.() -> Unit)
 ) {
@@ -375,7 +395,8 @@
         // the same pass as the content. The only expected reader is our MeasurePolicy.
         contentTracker.value = Unit
 
-        if (compositionSource.value == CompositionSource.Unknown) {
+        if (invalidationStrategy.onObservedStateChange == null &&
+            compositionSource.value == CompositionSource.Unknown) {
             // Set the content as the original composition source if the MotionLayout was not
             // recomposed by the caller or by itself
             compositionSource.value = CompositionSource.Content
@@ -393,6 +414,7 @@
         optimizationLevel = optimizationLevel,
         contentTracker = contentTracker,
         compositionSource = compositionSource,
+        invalidationStrategy = invalidationStrategy,
         content = contentDelegate
     )
 }
@@ -407,8 +429,9 @@
     finishedAnimationListener: (() -> Unit)? = null,
     debugFlags: DebugFlags = DebugFlags.None,
     optimizationLevel: Int = Optimizer.OPTIMIZATION_STANDARD,
-    contentTracker: State<Unit>,
+    contentTracker: MutableState<Unit>,
     compositionSource: Ref<CompositionSource>,
+    invalidationStrategy: InvalidationStrategy,
     @Suppress("HiddenTypeParameter")
     content: @Composable (MotionLayoutScope.() -> Unit)
 ) {
@@ -486,6 +509,7 @@
         modifier = modifier,
         contentTracker = contentTracker,
         compositionSource = compositionSource,
+        invalidationStrategy = invalidationStrategy,
         content = content
     )
 }
@@ -500,8 +524,9 @@
     optimizationLevel: Int,
     debugFlags: DebugFlags,
     modifier: Modifier,
-    contentTracker: State<Unit>,
+    contentTracker: MutableState<Unit>,
     compositionSource: Ref<CompositionSource>,
+    invalidationStrategy: InvalidationStrategy,
     @Suppress("HiddenTypeParameter")
     content: @Composable MotionLayoutScope.() -> Unit,
 ) {
@@ -534,6 +559,7 @@
         modifier = modifier,
         contentTracker = contentTracker,
         compositionSource = compositionSource,
+        invalidationStrategy = invalidationStrategy,
         content = content
     )
 }
@@ -551,8 +577,9 @@
     showPaths: Boolean,
     showKeyPositions: Boolean,
     modifier: Modifier,
-    contentTracker: State<Unit>,
+    contentTracker: MutableState<Unit>,
     compositionSource: Ref<CompositionSource>,
+    invalidationStrategy: InvalidationStrategy,
     @Suppress("HiddenTypeParameter")
     content: @Composable MotionLayoutScope.() -> Unit
 ) {
@@ -584,6 +611,23 @@
         true // Remember is required to return a non-Unit value
     }
 
+    if (invalidationStrategy.onObservedStateChange != null) {
+        Snapshot.observe(
+            readObserver = {
+                // Perform a reassignment to the State tracker, this will force readers to recompose at
+                // the same pass as the content. The only expected reader is our MeasurePolicy.
+                contentTracker.value = Unit
+
+                if (compositionSource.value == CompositionSource.Unknown) {
+                    // Set the content as the original composition source if the MotionLayout was not
+                    // recomposed by the caller or by itself
+                    compositionSource.value = CompositionSource.Content
+                }
+            },
+            block = invalidationStrategy.onObservedStateChange
+        )
+    }
+
     val measurePolicy = motionLayoutMeasurePolicy(
         contentTracker = contentTracker,
         compositionSource = compositionSource,
@@ -592,7 +636,8 @@
         transition = transitionImpl,
         motionProgress = motionProgress,
         measurer = measurer,
-        optimizationLevel = optimizationLevel
+        optimizationLevel = optimizationLevel,
+        invalidationStrategy = invalidationStrategy
     )
 
     measurer.addLayoutInformationReceiver(informationReceiver)
@@ -943,6 +988,7 @@
     motionProgress: MutableFloatState,
     measurer: MotionMeasurer,
     optimizationLevel: Int,
+    invalidationStrategy: InvalidationStrategy
 ): MeasurePolicy =
     MeasurePolicy { measurables, constraints ->
         // Do a state read, to guarantee that we control measure when the content recomposes without
@@ -950,15 +996,16 @@
         contentTracker.value
 
         val layoutSize = measurer.performInterpolationMeasure(
-            constraints,
-            this.layoutDirection,
-            constraintSetStart,
-            constraintSetEnd,
-            transition,
-            measurables,
-            optimizationLevel,
-            motionProgress.floatValue,
-            compositionSource.value ?: CompositionSource.Unknown
+            constraints = constraints,
+            layoutDirection = this.layoutDirection,
+            constraintSetStart = constraintSetStart,
+            constraintSetEnd = constraintSetEnd,
+            transition = transition,
+            measurables = measurables,
+            optimizationLevel = optimizationLevel,
+            progress = motionProgress.floatValue,
+            compositionSource = compositionSource.value ?: CompositionSource.Unknown,
+            invalidateOnConstraintsCallback = invalidationStrategy.shouldInvalidate
         )
         compositionSource.value = CompositionSource.Unknown // Reset after measuring
 
@@ -1149,3 +1196,488 @@
         return view.isShowingLayoutBounds
     }
 }
+
+/**
+ * Helper scope that provides some strategies to improve performance based on incoming constraints.
+ *
+ * As a starting approach, we recommend trying the following:
+ *
+ * ```
+ * MotionLayout(
+ *     ...,
+ *     invalidationStrategy = remember {
+ *         InvalidationStrategy(
+ *             onIncomingConstraints = { old, new ->
+ *                 // We invalidate every third frame, or when the change is higher than 5 pixels
+ *                 fixedWidthRate(old, new, skipCount = 3, threshold = 5) ||
+ *                     fixedHeightRate(old, new, skipCount = 3, threshold = 5)
+ *             },
+ *             onObservedStateChange = null // Default behavior
+ *         )
+ * }
+ * ) {
+ *    // content
+ * }
+ * ```
+ *
+ * See either [fixedWidthRate] or [fixedHeightRate] to learn more about the intent behind
+ * rate-limiting invalidation.
+ */
+class InvalidationStrategyScope internal constructor() {
+    private var widthRateCount = 0
+
+    /**
+     * Limits the rate at which MotionLayout is invalidated while [Constraints.hasFixedWidth] is
+     * true.
+     *
+     * &nbsp;
+     *
+     * The rate limit is defined by two variables. Use [skipCount] to indicate how many consecutive
+     * measure passes should skip invalidation, you may then provide a [threshold] (in pixels) to
+     * indicate when to invalidate regardless of how many passes are left to skip. This is
+     * important since you only want to skip invalidation passes when there's **not** a significant
+     * change in dimensions.
+     *
+     * &nbsp;
+     *
+     * Overall, you don't want [skipCount] to be too high otherwise it'll result in a "jumpy" layout
+     * behavior, but you also don't want the [threshold] to be too low, otherwise you'll lose the
+     * benefit of rate limiting.
+     *
+     * A good starting point is setting [skipCount] to 3 and [threshold] to 5. You can then
+     * adjust based on your expectations of performance and perceived smoothness.
+     */
+    fun fixedWidthRate(
+        oldConstraints: Constraints,
+        newConstraints: Constraints,
+        skipCount: Int,
+        threshold: Int
+    ): Boolean {
+        if (oldConstraints.hasFixedWidth && newConstraints.hasFixedWidth) {
+            val diff = (newConstraints.maxWidth - oldConstraints.maxWidth).absoluteValue
+            if (diff >= threshold) {
+                widthRateCount = 0
+                return true
+            }
+            if (diff != 0) {
+                widthRateCount++
+                if (widthRateCount > skipCount) {
+                    widthRateCount = 0
+                    return true
+                }
+            }
+        } else {
+            widthRateCount = 0
+        }
+        return false
+    }
+
+    private var heightRateCount = 0
+
+    /**
+     * Limits the rate at which MotionLayout is invalidated while [Constraints.hasFixedHeight] is
+     * true.
+     *
+     * &nbsp;
+     *
+     * The rate limit is defined by two variables. Use [skipCount] to indicate how many consecutive
+     * measure passes should skip invalidation, you may then provide a [threshold] (in pixels) to
+     * indicate when to invalidate regardless of how many passes are left to skip. This is
+     * important since you only want to skip invalidation passes when there's **not** a significant
+     * change in dimensions.
+     *
+     * &nbsp;
+     *
+     * Overall, you don't want [skipCount] to be too high otherwise it'll result in a "jumpy" layout
+     * behavior, but you also don't want the [threshold] to be too low, otherwise you'll lose the
+     * benefit of rate limiting.
+     *
+     * A good starting point is setting [skipCount] to 3 and [threshold] to 5. You can then
+     * adjust based on your expectations of performance and perceived smoothness.
+     */
+    fun fixedHeightRate(
+        oldConstraints: Constraints,
+        newConstraints: Constraints,
+        skipCount: Int,
+        threshold: Int
+    ): Boolean {
+        if (oldConstraints.hasFixedHeight && newConstraints.hasFixedHeight) {
+            val diff = (newConstraints.maxHeight - oldConstraints.maxHeight).absoluteValue
+            if (diff >= threshold) {
+                heightRateCount = 0
+                return true
+            }
+            if (diff != 0) {
+                heightRateCount++
+                if (heightRateCount > skipCount) {
+                    heightRateCount = 0
+                    return true
+                }
+            }
+        } else {
+            heightRateCount = 0
+        }
+        return false
+    }
+}
+
+/**
+ * Provide different invalidation strategies for [MotionLayout].
+ *
+ * &nbsp;
+ *
+ * Whenever [MotionLayout] needs invalidating, it has to recalculate all animations based on the
+ * current state at the measure pass, this is the slowest process in the [MotionLayout] cycle.
+ *
+ * An invalidation can be triggered by two reasons:
+ * - Incoming fixed size constraints have changed. This is necessary since layouts are highly
+ * dependent on their available space, it'll typically happen if you are externally animating the
+ * dimensions of [MotionLayout].
+ * - The content of MotionLayout recomposes. This is necessary since Layouts in Compose don't know
+ * the reason for a new measure pass, so we need to recalculate animations even if recomposition
+ * didn't affect the actual Layout. For example, this **definitely** happens if you are using
+ * [MotionLayoutScope.customProperties], even when you are just animating a background color, the
+ * custom property will trigger a recomposition in the content and [MotionLayout] will be forced to
+ * invalidate since it cannot know that the Layout was not affected.
+ *
+ * So, you may use [InvalidationStrategy] to help [MotionLayout] decide when to invalidate:
+ *
+ * - [onObservedStateChange]: Mitigates invalidation from content recomposition by explicitly
+ * reading the State variables you want to cause invalidation. You'll likely want to
+ * apply this strategy to most of your [MotionLayout] Composables. As, in the most simple cases you
+ * can just provide an empty lambda. Here's a full example:
+ *
+ * ```
+ * val progress = remember { Animatable(0f) }
+ *
+ * MotionLayout(
+ *     motionScene = remember {
+ *         // A simple MotionScene that animates a background color from Red to Blue
+ *         MotionScene {
+ *             val (textRef) = createRefsFor("text")
+ *
+ *             val start = constraintSet {
+ *                 constrain(textRef) {
+ *                     centerTo(parent)
+ *                     customColor("background", Color.Red)
+ *                 }
+ *             }
+ *             val end = constraintSet(extendConstraintSet = start) {
+ *                 constrain(textRef) {
+ *                     customColor("background", Color.Blue)
+ *                 }
+ *             }
+ *             defaultTransition(from = start, to = end)
+ *         }
+ *     },
+ *     progress = progress.value,
+ *     modifier = Modifier.fillMaxSize(),
+ *     invalidationStrategy = remember {
+ *         InvalidationStrategy(
+ *             onObservedStateChange = { /* Empty, no need to invalidate on content recomposition */  }
+ *         )
+ *     }
+ * ) {
+ *     // The content doesn't depend on any State variable that may affect the Layout's measure result
+ *     Text(
+ *         text = "Hello, World",
+ *         modifier = Modifier
+ *             .layoutId("text")
+ *             // However, the custom color is causing recomposition on each animated frame
+ *             .background(customColor("text", "background"))
+ *     )
+ * }
+ * LaunchedEffect(Unit) {
+ *     delay(1000)
+ *     progress.animateTo(targetValue = 1f, tween(durationMillis = 1200))
+ * }
+ * ```
+ *
+ * *When should I provide States to read then?*
+ *
+ * &nbsp;
+ *
+ * Whenever a State backed variable that affects the Layout's measure result changes. The most
+ * common cases are Strings on the Text Composable.
+ *
+ * Here's an example where the text changes half-way through the animation:
+ * ```
+ * val progress = remember { Animatable(0f) }
+ *
+ * var textString by remember { mutableStateOf("Hello, World") }
+ * MotionLayout(
+ *     motionScene = remember {
+ *         // A MotionScene that animates a Text from one corner to the other with an animated
+ *         // background color
+ *         MotionScene {
+ *             val (textRef) = createRefsFor("text")
+ *
+ *             defaultTransition(
+ *                 from = constraintSet {
+ *                     constrain(textRef) {
+ *                         top.linkTo(parent.top)
+ *                         start.linkTo(parent.start)
+ *
+ *                         customColor("background", Color.LightGray)
+ *                     }
+ *                 },
+ *                 to = constraintSet {
+ *                     constrain(textRef) {
+ *                         bottom.linkTo(parent.bottom)
+ *                         end.linkTo(parent.end)
+ *
+ *                         customColor("background", Color.Gray)
+ *                     }
+ *                 }
+ *             )
+ *         }
+ *     },
+ *     progress = progress.value,
+ *     modifier = Modifier.fillMaxSize(),
+ *     invalidationStrategy = remember {
+ *         InvalidationStrategy(
+ *             onObservedStateChange = @Suppress("UNUSED_EXPRESSION"){
+ *                 // We read our State String variable in this block, to guarantee that
+ *                 // MotionLayout will invalidate to accommodate the new Text Layout.
+ *                 // Note that we do not read the custom color here since it doesn't affect the Layout
+ *                 textString
+ *             }
+ *         )
+ *     }
+ * ) {
+ *     // The text Layout will change based on the provided State String
+ *     Text(
+ *         text = textString,
+ *         modifier = Modifier
+ *             .layoutId("text")
+ *             // Without an invalidation strategy, the custom color would normally invalidate
+ *             // MotionLayout due to recomposition
+ *             .background(customColor("text", "background"))
+ *     )
+ * }
+ * LaunchedEffect(Unit) {
+ *     delay(1000)
+ *     progress.animateTo(targetValue = 1f, tween(durationMillis = 3000)) {
+ *         if (value >= 0.5f) {
+ *             textString = "This is a\n" + "significantly different text."
+ *         }
+ *     }
+ * }
+ * ```
+ *
+ * *What if my Text changes continuously?*
+ *
+ * &nbsp;
+ *
+ * There's a few strategies you can take depending on how you expect the Text to behave.
+ *
+ * For example, if you don't expect the text to need more than one line, you can set the Text with
+ * `softWrap = false` and `overflow = TextOverflow.Visible`:
+ *
+ * ```
+ * MotionLayout(
+ *     motionScene = motionScene,
+ *     progress = progress,
+ *     modifier = Modifier.size(200.dp),
+ *     invalidationStrategy = remember { InvalidationStrategy { /* Do not invalidate on content recomposition */  } }
+ * ) {
+ *     Text(
+ *         text = <your-State-String>,
+ *         modifier = Modifier.layoutId("text"),
+ *         softWrap = false,
+ *         overflow = TextOverflow.Visible
+ *     )
+ * }
+ * ```
+ *
+ * The Text layout won't change significantly and performance will be much improved.
+ *
+ * - [onIncomingConstraints]: With this lambda you can mitigate invalidation from incoming
+ * constraints. You'll only have to worry about providing this lambda if you or the Layout you're
+ * using is animating measuring constraints on [MotionLayout]. If the size is only changing in specific,
+ * discrete values, then you should allow [MotionLayout] to invalidate normally.
+ *
+ * Here's an example where we manually animate [MotionLayout]'s size through a Modifier (along with
+ * the MotionLayout animation), and shows how to mitigate invalidation by rate-limiting:
+ *
+ * ```
+ * val textId = "text"
+ * val progress = remember { Animatable(0f) }
+ *
+ * val initial = remember { DpSize(100.dp, 100.dp) }
+ * val target = remember { DpSize(120.dp, 200.dp) }
+ * var size by remember { mutableStateOf(initial) }
+ *
+ * MotionLayout(
+ *     motionScene = remember {
+ *         MotionScene {
+ *             val (textRef) = createRefsFor( "text")
+ *
+ *             // Animate text from the bottom of the layout to the top
+ *             defaultTransition(
+ *                 from = constraintSet {
+ *                     constrain(textRef) {
+ *                         centerHorizontallyTo(parent)
+ *                         bottom.linkTo(parent.bottom)
+ *                     }
+ *                 },
+ *                 to = constraintSet {
+ *                     constrain(textRef) {
+ *                         centerHorizontallyTo(parent)
+ *                         top.linkTo(parent.top)
+ *                     }
+ *                 }
+ *             )
+ *         }
+ *     },
+ *     progress = progress.value,
+ *     modifier = Modifier.background(Color.Cyan).size(size),
+ *     invalidationStrategy = remember {
+ *         InvalidationStrategy(
+ *             onIncomingConstraints = { old, new ->
+ *                 // We invalidate every third frame, or when the change is higher than 5 pixels
+ *                 fixedWidthRate(old, new, skipCount = 3, threshold = 5) ||
+ *                     fixedHeightRate(old, new, skipCount = 3, threshold = 5)
+ *             },
+ *             // No need to worry about content state changes for this example
+ *             onObservedStateChange = {}
+ *         )
+ *     }
+ * ) {
+ *     Text("Hello, World!", Modifier.layoutId(textId))
+ * }
+ *
+ * // Animate the size along with the MotionLayout. Without an invalidation strategy, this will cause
+ * // MotionLayout to invalidate at every measure pass since it's getting fixed size Constraints at
+ * // different values
+ * LaunchedEffect(Unit) {
+ *     val sizeDifference = target - initial
+ *     delay(1000)
+ *     progress.animateTo(1f, tween(1200)) {
+ *         size = initial + (sizeDifference * value)
+ *     }
+ * }
+ * ```
+ *
+ * Note that [fixedWidthRate][InvalidationStrategyScope.fixedWidthRate] and [fixedHeightRate][InvalidationStrategyScope.fixedHeightRate]
+ * are helper methods available in [InvalidationStrategyScope].
+ *
+ * &nbsp;
+ *
+ * An alternative to rate-limiting is to "simply" avoid invalidation from changed fixed size constraints.
+ * This can be done by leaving [MotionLayout] as wrap content and then have it choose its own start
+ * and ending size. Naturally, this is not always feasible, specially if it's a parent Composable the one
+ * that's animating the size constraints.
+ *
+ * But, here's the MotionScene showing how to achieve this behavior based on the example above:
+ *
+ * ```
+ * MotionScene {
+ *     // We'll use fakeParentRef to choose our starting and ending size then constrain everything
+ *     // else to it. MotionLayout will animate without invalidating.
+ *     // There's no need to bind "fakeParent" to any actual Composable.
+ *     val (fakeParentRef, textRef) = createRefsFor("fakeParent", "text")
+ *
+ *     defaultTransition(
+ *         from = constraintSet {
+ *             constrain(fakeParentRef) {
+ *                 width = 100.dp.asDimension()
+ *                 height = 100.dp.asDimension()
+ *             }
+ *
+ *             constrain(textRef) {
+ *                 bottom.linkTo(fakeParentRef.bottom)
+ *             }
+ *         },
+ *         to = constraintSet {
+ *             constrain(fakeParentRef) {
+ *                 width = 120.dp.asDimension()
+ *                 height = 200.dp.asDimension()
+ *             }
+ *
+ *             constrain(textRef) {
+ *                 top.linkTo(fakeParentRef.top)
+ *             }
+ *         }
+ *     )
+ * }
+ * ```
+ *
+ * You can then remove the size modifier and the invalidation strategy for `onIncomingConstraints`,
+ * as [MotionLayout] will animate through both sizes without invalidating.
+ *
+ * @see InvalidationStrategy.DefaultInvalidationStrategy
+ * @see InvalidationStrategy.OnIncomingConstraints
+ * @see InvalidationStrategyScope
+ * @see InvalidationStrategyScope.fixedWidthRate
+ * @see InvalidationStrategyScope.fixedHeightRate
+ *
+ * @property onObservedStateChange
+ */
+class InvalidationStrategy(
+    val onIncomingConstraints: OnIncomingConstraints? = null,
+    /**
+     * Lambda to implement invalidation on observed State changes.
+     *
+     * [State][androidx.compose.runtime.State] based variables should be read in the block of
+     * this lambda to have [MotionLayout] invalidate whenever any of those variables
+     * have changed.
+     *
+     * You may use an assigned value or delegated variable for this purpose:
+     * ```
+     * val stateVar0 = remember { mutableStateOf("Foo") }
+     * var stateVar1 by remember { mutableStateOf("Bar") }
+     * val invalidationStrategy = remember {
+     *     InvalidationStrategy(
+     *         onObservedStateChange = @Suppress("UNUSED_EXPRESSION") {
+     *             stateVar0.value
+     *             stateVar1
+     *         }
+     *     )
+     * }
+     * ```
+     *
+     * See [InvalidationStrategy] to learn more about common strategies regarding invalidation on
+     * onObservedStateChange.
+     */
+    val onObservedStateChange: (() -> Unit)?
+) {
+    private val scope = InvalidationStrategyScope()
+
+    /**
+     * Hacky thing to transform: `(InvalidationStrategyScope.(old: Constraints, new: Constraints) -> Boolean)?`
+     * into `((old: Constraints, new: Constraints) -> Boolean)?`
+     */
+    internal val shouldInvalidate: ShouldInvalidateCallback? = kotlin.run {
+        if (onIncomingConstraints == null) {
+            null
+        } else {
+            ShouldInvalidateCallback { old, new ->
+                with(onIncomingConstraints) {
+                    scope(old, new)
+                }
+            }
+        }
+    }
+
+    companion object {
+        /**
+         * Default invalidation strategy for [MotionLayout].
+         *
+         * This will cause it to invalidate whenever its content recomposes or when it receives different
+         * fixed size [Constraints] at the measure pass.
+         */
+        val DefaultInvalidationStrategy = InvalidationStrategy(null, null)
+    }
+
+    /**
+     * Functional interface to implement invalidation on incoming constraints.
+     *
+     * See [InvalidationStrategy] or either of [fixedWidthRate][InvalidationStrategyScope.fixedWidthRate]/[fixedHeightRate][InvalidationStrategyScope.fixedHeightRate].
+     *
+     * To learn some strategies on how to improve invalidation due to incoming constraints.
+     */
+    fun interface OnIncomingConstraints {
+        operator fun InvalidationStrategyScope.invoke(old: Constraints, new: Constraints): Boolean
+    }
+}
diff --git a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionMeasurer.kt b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionMeasurer.kt
index a8a1f1c..dd09496 100644
--- a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionMeasurer.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/MotionMeasurer.kt
@@ -85,9 +85,14 @@
         measurables: List<Measurable>,
         optimizationLevel: Int,
         progress: Float,
-        compositionSource: CompositionSource
+        compositionSource: CompositionSource,
+        invalidateOnConstraintsCallback: ShouldInvalidateCallback?
     ): IntSize {
-        val needsRemeasure = needsRemeasure(constraints, compositionSource)
+        val needsRemeasure = needsRemeasure(
+            constraints = constraints,
+            source = compositionSource,
+            invalidateOnConstraintsCallback = invalidateOnConstraintsCallback
+        )
 
         if (lastProgressInInterpolation != progress ||
             (layoutInformationReceiver?.getForcedWidth() != Int.MIN_VALUE &&
@@ -106,10 +111,19 @@
                 remeasure = needsRemeasure
             )
         }
+        oldConstraints = constraints
         return IntSize(root.width, root.height)
     }
 
     /**
+     * Nullable reference of [Constraints] used for the `invalidateOnConstraintsCallback`.
+     *
+     * Helps us to indicate when we can start calling the callback, as we need at least one measure
+     * pass to populate this reference.
+     */
+    private var oldConstraints: Constraints? = null
+
+    /**
      * Indicates if the layout requires measuring before computing the interpolation.
      *
      * This might happen if the size of MotionLayout or any of its children changed.
@@ -117,17 +131,28 @@
      * MotionLayout size might change from its parent Layout, and in some cases the children size
      * might change (eg: A Text layout has a longer string appended).
      */
-    private fun needsRemeasure(constraints: Constraints, source: CompositionSource): Boolean {
+    private fun needsRemeasure(
+        constraints: Constraints,
+        source: CompositionSource,
+        invalidateOnConstraintsCallback: ShouldInvalidateCallback?
+    ): Boolean {
         if (this.transition.isEmpty || frameCache.isEmpty()) {
             // Nothing measured (by MotionMeasurer)
             return true
         }
 
-        if ((constraints.hasFixedHeight && !state.sameFixedHeight(constraints.maxHeight)) ||
-            (constraints.hasFixedWidth && !state.sameFixedWidth(constraints.maxWidth))
-        ) {
-            // Layout size changed
-            return true
+        if (oldConstraints != null && invalidateOnConstraintsCallback != null) {
+            if (invalidateOnConstraintsCallback(oldConstraints!!, constraints)) {
+                // User is deciding when to invalidate
+                return true
+            }
+        } else {
+            if ((constraints.hasFixedHeight && !state.sameFixedHeight(constraints.maxHeight)) ||
+                (constraints.hasFixedWidth && !state.sameFixedWidth(constraints.maxWidth))
+            ) {
+                // Layout size changed
+                return true
+            }
         }
 
         // Content recomposed
@@ -542,3 +567,11 @@
         transition.applyAllTo(this.transition)
     }
 }
+
+/**
+ * Functional interface to represent the callback of type
+ * `(old: Constraints, new: Constraints) -> Boolean`
+ */
+internal fun interface ShouldInvalidateCallback {
+    operator fun invoke(old: Constraints, new: Constraints): Boolean
+}
diff --git a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/TransitionHandler.kt b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/TransitionHandler.kt
index 35e7131..0da2585 100644
--- a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/TransitionHandler.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/TransitionHandler.kt
@@ -33,6 +33,14 @@
         get() = motionMeasurer.transition
 
     /**
+     * Whether we consume the rest of the drag for OnSwipe.
+     *
+     * @see androidx.constraintlayout.core.state.Transition.isFirstDownAccepted
+     */
+    fun onAcceptFirstDownForOnSwipe(offset: Offset) =
+        transition.isFirstDownAccepted(offset.x, offset.y)
+
+    /**
      * The [motionProgress] is updated based on the [Offset] from a single drag event.
      */
     fun updateProgressOnDrag(dragAmount: Offset) {
diff --git a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/TransitionScope.kt b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/TransitionScope.kt
index f40a4ba..da4523a4 100644
--- a/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/TransitionScope.kt
+++ b/constraintlayout/constraintlayout-compose/src/androidMain/kotlin/androidx/constraintlayout/compose/TransitionScope.kt
@@ -257,10 +257,13 @@
         onSwipe?.let {
             containerObject.put("onSwipe", onSwipeObject)
             onSwipeObject.putString("direction", it.direction.name)
-            onSwipeObject.putNumber("dragScale", it.dragScale)
+            onSwipeObject.putNumber("scale", it.dragScale)
             it.dragAround?.id?.let { id ->
                 onSwipeObject.putString("around", id.toString())
             }
+            it.limitBoundsTo?.id?.let { id ->
+                onSwipeObject.putString("limitBounds", id.toString())
+            }
             onSwipeObject.putNumber("threshold", it.dragThreshold)
             onSwipeObject.putString("anchor", it.anchor.id.toString())
             onSwipeObject.putString("side", it.side.name)
diff --git a/constraintlayout/constraintlayout-core/api/restricted_current.txt b/constraintlayout/constraintlayout-core/api/restricted_current.txt
index 7710fe2..ae20c5f 100644
--- a/constraintlayout/constraintlayout-core/api/restricted_current.txt
+++ b/constraintlayout/constraintlayout-core/api/restricted_current.txt
@@ -2395,6 +2395,7 @@
     method public boolean hasPositionKeyframes();
     method public void interpolate(int, int, float);
     method public boolean isEmpty();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public boolean isFirstDownAccepted(float, float);
     method public boolean isTouchNotDone(float);
     method public void setTouchUp(float, long, float, float);
     method public void setTransitionProperties(androidx.constraintlayout.core.motion.utils.TypedBundle!);
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/parser/CLContainer.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/parser/CLContainer.java
index fea700c..f3a66d0 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/parser/CLContainer.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/parser/CLContainer.java
@@ -358,7 +358,9 @@
         CLContainer clone = (CLContainer) super.clone();
         ArrayList<CLElement> clonedArray = new ArrayList<>(mElements.size());
         for (CLElement element: mElements) {
-            clonedArray.add(element.clone());
+            CLElement elementClone = element.clone();
+            elementClone.setContainer(clone);
+            clonedArray.add(elementClone);
         }
         clone.mElements = clonedArray;
         return clone;
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/parser/CLElement.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/parser/CLElement.java
index cd265f7..acb7a97 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/parser/CLElement.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/parser/CLElement.java
@@ -208,11 +208,7 @@
     @Override
     public CLElement clone() {
         try {
-            CLElement clone = (CLElement) super.clone();
-            if (mContainer != null) {
-                clone.mContainer = (CLContainer) mContainer.clone();
-            }
-            return clone;
+            return (CLElement) super.clone();
         } catch (CloneNotSupportedException e) {
             throw new AssertionError();
         }
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/parser/CLParsingException.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/parser/CLParsingException.java
index 937f0ab..7088cf2 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/parser/CLParsingException.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/parser/CLParsingException.java
@@ -22,6 +22,7 @@
     private final String mElementClass;
 
     public CLParsingException(String reason, CLElement element) {
+        super(reason);
         mReason = reason;
         if (element != null) {
             mElementClass = element.getStrClass();
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/Transition.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/Transition.java
index 7a85871..98dbf1f 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/Transition.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/Transition.java
@@ -17,6 +17,7 @@
 package androidx.constraintlayout.core.state;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
 import androidx.constraintlayout.core.motion.CustomVariable;
 import androidx.constraintlayout.core.motion.Motion;
 import androidx.constraintlayout.core.motion.MotionWidget;
@@ -110,8 +111,7 @@
 
         @SuppressWarnings("unused")
         private String mRotationCenterId;
-        @SuppressWarnings("unused")
-        private String mLimitBoundsTo;
+        String mLimitBoundsTo;
         @SuppressWarnings("unused")
         private boolean mDragVertical = true;
         private int mDragDirection = 0;
@@ -401,6 +401,34 @@
     }
 
     /**
+     * For the given position (in the MotionLayout coordinate space) determine whether we accept
+     * the first down for on swipe.
+     * <p>
+     * This is based off {@link OnSwipe#mLimitBoundsTo}. If null, we accept the drag at any
+     * position, otherwise, we only accept it if it's within its bounds.
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public boolean isFirstDownAccepted(float posX, float posY) {
+        if (mOnSwipe == null) {
+            return false;
+        }
+
+        if (mOnSwipe.mLimitBoundsTo != null) {
+            WidgetState targetWidget = mState.get(mOnSwipe.mLimitBoundsTo);
+            if (targetWidget == null) {
+                System.err.println("mLimitBoundsTo target is null");
+                return false;
+            }
+            // Calculate against the interpolated/current frame
+            WidgetFrame frame = targetWidget.getFrame(2);
+            return posX >= frame.left && posX < frame.right && posY >= frame.top
+                    && posY < frame.bottom;
+        } else {
+            return true;
+        }
+    }
+
+    /**
      * Converts from xy drag to progress
      * This should be used till touch up
      *
diff --git a/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorSnackbarWithButtonTest.java b/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorSnackbarWithButtonTest.java
index 203ba8b..e66d965 100644
--- a/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorSnackbarWithButtonTest.java
+++ b/coordinatorlayout/coordinatorlayout/src/androidTest/java/androidx/coordinatorlayout/widget/CoordinatorSnackbarWithButtonTest.java
@@ -40,6 +40,7 @@
 
 import org.hamcrest.Matcher;
 import org.junit.After;
+import org.junit.Ignore;
 import org.junit.Test;
 
 import java.util.concurrent.CountDownLatch;
@@ -132,6 +133,7 @@
         verifyBarViewStacking(textView, 0);
     }
 
+    @Ignore // b/292019798
     @Test
     public void testBehaviorBasedSlidingFromClassAnnotation() {
         // Use a layout in which a custom child view has Behavior object configured via
@@ -160,6 +162,7 @@
         verifyBarViewStacking(textView, 0);
     }
 
+    @Ignore // b/292021877
     @Test
     public void testBehaviorBasedSlidingFromRuntimeApiCall() {
         // Use a layout in which a TextView child doesn't have any configured Behavior
diff --git a/core/core-animation/src/main/java/androidx/core/animation/ValueAnimator.java b/core/core-animation/src/main/java/androidx/core/animation/ValueAnimator.java
index 3d468ca..20c2325 100644
--- a/core/core-animation/src/main/java/androidx/core/animation/ValueAnimator.java
+++ b/core/core-animation/src/main/java/androidx/core/animation/ValueAnimator.java
@@ -27,7 +27,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
-import androidx.tracing.Trace;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -1164,7 +1163,6 @@
         }
         // mReversing needs to be reset *after* notifying the listeners for the end callbacks.
         mReversing = false;
-        Trace.endSection();
     }
 
     /**
@@ -1172,9 +1170,6 @@
      * called on the UI thread.
      */
     private void startAnimation() {
-
-        Trace.beginSection(getNameForTrace());
-
         mAnimationEndRequested = false;
         initAnimation();
         mRunning = true;
diff --git a/core/core-ktx/api/1.12.0-beta01.txt b/core/core-ktx/api/1.12.0-beta01.txt
new file mode 100644
index 0000000..7a12effb
--- /dev/null
+++ b/core/core-ktx/api/1.12.0-beta01.txt
@@ -0,0 +1,634 @@
+// Signature format: 4.0
+package androidx.core.animation {
+
+  public final class AnimatorKt {
+    method public static inline android.animation.Animator.AnimatorListener addListener(android.animation.Animator, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onEnd, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onStart, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onCancel, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onRepeat);
+    method @RequiresApi(19) public static android.animation.Animator.AnimatorPauseListener addPauseListener(android.animation.Animator, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onResume, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onPause);
+    method public static inline android.animation.Animator.AnimatorListener doOnCancel(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method public static inline android.animation.Animator.AnimatorListener doOnEnd(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method @RequiresApi(19) public static android.animation.Animator.AnimatorPauseListener doOnPause(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method public static inline android.animation.Animator.AnimatorListener doOnRepeat(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method @RequiresApi(19) public static android.animation.Animator.AnimatorPauseListener doOnResume(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method public static inline android.animation.Animator.AnimatorListener doOnStart(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+  }
+
+}
+
+package androidx.core.content {
+
+  public final class ContentValuesKt {
+    method public static android.content.ContentValues contentValuesOf(kotlin.Pair<java.lang.String,?>... pairs);
+  }
+
+  public final class ContextKt {
+    method public static inline <reified T> T! getSystemService(android.content.Context);
+    method public static inline void withStyledAttributes(android.content.Context, optional android.util.AttributeSet? set, int[] attrs, optional @AttrRes int defStyleAttr, optional @StyleRes int defStyleRes, kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,kotlin.Unit> block);
+    method public static inline void withStyledAttributes(android.content.Context, @StyleRes int resourceId, int[] attrs, kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,kotlin.Unit> block);
+  }
+
+  public final class SharedPreferencesKt {
+    method public static inline void edit(android.content.SharedPreferences, optional boolean commit, kotlin.jvm.functions.Function1<? super android.content.SharedPreferences.Editor,kotlin.Unit> action);
+  }
+
+}
+
+package androidx.core.content.res {
+
+  public final class TypedArrayKt {
+    method public static boolean getBooleanOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @ColorInt public static int getColorOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static android.content.res.ColorStateList getColorStateListOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static float getDimensionOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @Dimension public static int getDimensionPixelOffsetOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @Dimension public static int getDimensionPixelSizeOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static android.graphics.drawable.Drawable getDrawableOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static float getFloatOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @RequiresApi(26) public static android.graphics.Typeface getFontOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static int getIntOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static int getIntegerOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @AnyRes public static int getResourceIdOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static String getStringOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static CharSequence![] getTextArrayOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static CharSequence getTextOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static inline <R> R use(android.content.res.TypedArray, kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,? extends R> block);
+  }
+
+}
+
+package androidx.core.database {
+
+  public final class CursorKt {
+    method public static inline byte[]? getBlobOrNull(android.database.Cursor, int index);
+    method public static inline Double? getDoubleOrNull(android.database.Cursor, int index);
+    method public static inline Float? getFloatOrNull(android.database.Cursor, int index);
+    method public static inline Integer? getIntOrNull(android.database.Cursor, int index);
+    method public static inline Long? getLongOrNull(android.database.Cursor, int index);
+    method public static inline Short? getShortOrNull(android.database.Cursor, int index);
+    method public static inline String? getStringOrNull(android.database.Cursor, int index);
+  }
+
+}
+
+package androidx.core.database.sqlite {
+
+  public final class SQLiteDatabaseKt {
+    method public static inline <T> T transaction(android.database.sqlite.SQLiteDatabase, optional boolean exclusive, kotlin.jvm.functions.Function1<? super android.database.sqlite.SQLiteDatabase,? extends T> body);
+  }
+
+}
+
+package androidx.core.graphics {
+
+  public final class BitmapKt {
+    method public static inline android.graphics.Bitmap applyCanvas(android.graphics.Bitmap, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline operator boolean contains(android.graphics.Bitmap, android.graphics.Point p);
+    method public static inline operator boolean contains(android.graphics.Bitmap, android.graphics.PointF p);
+    method public static inline android.graphics.Bitmap createBitmap(int width, int height, optional android.graphics.Bitmap.Config config);
+    method @RequiresApi(26) public static inline android.graphics.Bitmap createBitmap(int width, int height, optional android.graphics.Bitmap.Config config, optional boolean hasAlpha, optional android.graphics.ColorSpace colorSpace);
+    method public static inline operator int get(android.graphics.Bitmap, int x, int y);
+    method public static inline android.graphics.Bitmap scale(android.graphics.Bitmap, int width, int height, optional boolean filter);
+    method public static inline operator void set(android.graphics.Bitmap, int x, int y, @ColorInt int color);
+  }
+
+  public final class CanvasKt {
+    method public static inline void withClip(android.graphics.Canvas, android.graphics.Path clipPath, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withClip(android.graphics.Canvas, android.graphics.Rect clipRect, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withClip(android.graphics.Canvas, android.graphics.RectF clipRect, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withClip(android.graphics.Canvas, float left, float top, float right, float bottom, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withClip(android.graphics.Canvas, int left, int top, int right, int bottom, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withMatrix(android.graphics.Canvas, optional android.graphics.Matrix matrix, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withRotation(android.graphics.Canvas, optional float degrees, optional float pivotX, optional float pivotY, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withSave(android.graphics.Canvas, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withScale(android.graphics.Canvas, optional float x, optional float y, optional float pivotX, optional float pivotY, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withSkew(android.graphics.Canvas, optional float x, optional float y, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withTranslation(android.graphics.Canvas, optional float x, optional float y, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+  }
+
+  public final class ColorKt {
+    method @RequiresApi(26) public static inline operator float component1(android.graphics.Color);
+    method public static inline operator int component1(@ColorInt int);
+    method @RequiresApi(26) public static inline operator float component1(@ColorLong long);
+    method @RequiresApi(26) public static inline operator float component2(android.graphics.Color);
+    method public static inline operator int component2(@ColorInt int);
+    method @RequiresApi(26) public static inline operator float component2(@ColorLong long);
+    method @RequiresApi(26) public static inline operator float component3(android.graphics.Color);
+    method public static inline operator int component3(@ColorInt int);
+    method @RequiresApi(26) public static inline operator float component3(@ColorLong long);
+    method @RequiresApi(26) public static inline operator float component4(android.graphics.Color);
+    method public static inline operator int component4(@ColorInt int);
+    method @RequiresApi(26) public static inline operator float component4(@ColorLong long);
+    method @RequiresApi(26) public static inline infix android.graphics.Color convertTo(android.graphics.Color, android.graphics.ColorSpace colorSpace);
+    method @RequiresApi(26) public static inline infix android.graphics.Color convertTo(android.graphics.Color, android.graphics.ColorSpace.Named colorSpace);
+    method @ColorLong @RequiresApi(26) public static inline infix long convertTo(@ColorInt int, android.graphics.ColorSpace colorSpace);
+    method @ColorLong @RequiresApi(26) public static inline infix long convertTo(@ColorInt int, android.graphics.ColorSpace.Named colorSpace);
+    method @ColorLong @RequiresApi(26) public static inline infix long convertTo(@ColorLong long, android.graphics.ColorSpace colorSpace);
+    method @ColorLong @RequiresApi(26) public static inline infix long convertTo(@ColorLong long, android.graphics.ColorSpace.Named colorSpace);
+    method public static inline int getAlpha(@ColorInt int);
+    method @RequiresApi(26) public static inline float getAlpha(@ColorLong long);
+    method public static inline int getBlue(@ColorInt int);
+    method @RequiresApi(26) public static inline float getBlue(@ColorLong long);
+    method @RequiresApi(26) public static inline android.graphics.ColorSpace getColorSpace(@ColorLong long);
+    method public static inline int getGreen(@ColorInt int);
+    method @RequiresApi(26) public static inline float getGreen(@ColorLong long);
+    method @RequiresApi(26) public static inline float getLuminance(@ColorInt int);
+    method @RequiresApi(26) public static inline float getLuminance(@ColorLong long);
+    method public static inline int getRed(@ColorInt int);
+    method @RequiresApi(26) public static inline float getRed(@ColorLong long);
+    method @RequiresApi(26) public static inline boolean isSrgb(@ColorLong long);
+    method @RequiresApi(26) public static inline boolean isWideGamut(@ColorLong long);
+    method @RequiresApi(26) public static operator android.graphics.Color plus(android.graphics.Color, android.graphics.Color c);
+    method @RequiresApi(26) public static inline android.graphics.Color toColor(@ColorInt int);
+    method @RequiresApi(26) public static inline android.graphics.Color toColor(@ColorLong long);
+    method @ColorInt public static inline int toColorInt(String);
+    method @ColorInt @RequiresApi(26) public static inline int toColorInt(@ColorLong long);
+    method @ColorLong @RequiresApi(26) public static inline long toColorLong(@ColorInt int);
+  }
+
+  public final class ImageDecoderKt {
+    method @RequiresApi(28) public static inline android.graphics.Bitmap decodeBitmap(android.graphics.ImageDecoder.Source, kotlin.jvm.functions.Function3<? super android.graphics.ImageDecoder,? super android.graphics.ImageDecoder.ImageInfo,? super android.graphics.ImageDecoder.Source,kotlin.Unit> action);
+    method @RequiresApi(28) public static inline android.graphics.drawable.Drawable decodeDrawable(android.graphics.ImageDecoder.Source, kotlin.jvm.functions.Function3<? super android.graphics.ImageDecoder,? super android.graphics.ImageDecoder.ImageInfo,? super android.graphics.ImageDecoder.Source,kotlin.Unit> action);
+  }
+
+  public final class MatrixKt {
+    method public static android.graphics.Matrix rotationMatrix(float degrees, optional float px, optional float py);
+    method public static android.graphics.Matrix scaleMatrix(optional float sx, optional float sy);
+    method public static inline operator android.graphics.Matrix times(android.graphics.Matrix, android.graphics.Matrix m);
+    method public static android.graphics.Matrix translationMatrix(optional float tx, optional float ty);
+    method public static inline float[] values(android.graphics.Matrix);
+  }
+
+  public final class PaintKt {
+    method public static inline boolean setBlendMode(android.graphics.Paint, androidx.core.graphics.BlendModeCompat? blendModeCompat);
+  }
+
+  public final class PathKt {
+    method @RequiresApi(19) public static inline infix android.graphics.Path and(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(26) public static Iterable<androidx.core.graphics.PathSegment> flatten(android.graphics.Path, optional float error);
+    method @RequiresApi(19) public static inline operator android.graphics.Path minus(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(19) public static inline infix android.graphics.Path or(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(19) public static inline operator android.graphics.Path plus(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(19) public static inline infix android.graphics.Path xor(android.graphics.Path, android.graphics.Path p);
+  }
+
+  public final class PictureKt {
+    method public static inline android.graphics.Picture record(android.graphics.Picture, int width, int height, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+  }
+
+  public final class PointKt {
+    method public static inline operator int component1(android.graphics.Point);
+    method public static inline operator float component1(android.graphics.PointF);
+    method public static inline operator int component2(android.graphics.Point);
+    method public static inline operator float component2(android.graphics.PointF);
+    method public static inline operator android.graphics.Point div(android.graphics.Point, float scalar);
+    method public static inline operator android.graphics.PointF div(android.graphics.PointF, float scalar);
+    method public static inline operator android.graphics.Point minus(android.graphics.Point, android.graphics.Point p);
+    method public static inline operator android.graphics.Point minus(android.graphics.Point, int xy);
+    method public static inline operator android.graphics.PointF minus(android.graphics.PointF, android.graphics.PointF p);
+    method public static inline operator android.graphics.PointF minus(android.graphics.PointF, float xy);
+    method public static inline operator android.graphics.Point plus(android.graphics.Point, android.graphics.Point p);
+    method public static inline operator android.graphics.Point plus(android.graphics.Point, int xy);
+    method public static inline operator android.graphics.PointF plus(android.graphics.PointF, android.graphics.PointF p);
+    method public static inline operator android.graphics.PointF plus(android.graphics.PointF, float xy);
+    method public static inline operator android.graphics.Point times(android.graphics.Point, float scalar);
+    method public static inline operator android.graphics.PointF times(android.graphics.PointF, float scalar);
+    method public static inline android.graphics.Point toPoint(android.graphics.PointF);
+    method public static inline android.graphics.PointF toPointF(android.graphics.Point);
+    method public static inline operator android.graphics.Point unaryMinus(android.graphics.Point);
+    method public static inline operator android.graphics.PointF unaryMinus(android.graphics.PointF);
+  }
+
+  public final class PorterDuffKt {
+    method public static inline android.graphics.PorterDuffColorFilter toColorFilter(android.graphics.PorterDuff.Mode, int color);
+    method public static inline android.graphics.PorterDuffXfermode toXfermode(android.graphics.PorterDuff.Mode);
+  }
+
+  public final class RectKt {
+    method public static inline infix android.graphics.Rect and(android.graphics.Rect, android.graphics.Rect r);
+    method public static inline infix android.graphics.RectF and(android.graphics.RectF, android.graphics.RectF r);
+    method public static inline operator int component1(android.graphics.Rect);
+    method public static inline operator float component1(android.graphics.RectF);
+    method public static inline operator int component2(android.graphics.Rect);
+    method public static inline operator float component2(android.graphics.RectF);
+    method public static inline operator int component3(android.graphics.Rect);
+    method public static inline operator float component3(android.graphics.RectF);
+    method public static inline operator int component4(android.graphics.Rect);
+    method public static inline operator float component4(android.graphics.RectF);
+    method public static inline operator boolean contains(android.graphics.Rect, android.graphics.Point p);
+    method public static inline operator boolean contains(android.graphics.RectF, android.graphics.PointF p);
+    method public static inline operator android.graphics.Rect minus(android.graphics.Rect, android.graphics.Point xy);
+    method public static inline operator android.graphics.Region minus(android.graphics.Rect, android.graphics.Rect r);
+    method public static inline operator android.graphics.Rect minus(android.graphics.Rect, int xy);
+    method public static inline operator android.graphics.RectF minus(android.graphics.RectF, android.graphics.PointF xy);
+    method public static inline operator android.graphics.Region minus(android.graphics.RectF, android.graphics.RectF r);
+    method public static inline operator android.graphics.RectF minus(android.graphics.RectF, float xy);
+    method public static inline infix android.graphics.Rect or(android.graphics.Rect, android.graphics.Rect r);
+    method public static inline infix android.graphics.RectF or(android.graphics.RectF, android.graphics.RectF r);
+    method public static inline operator android.graphics.Rect plus(android.graphics.Rect, android.graphics.Point xy);
+    method public static inline operator android.graphics.Rect plus(android.graphics.Rect, android.graphics.Rect r);
+    method public static inline operator android.graphics.Rect plus(android.graphics.Rect, int xy);
+    method public static inline operator android.graphics.RectF plus(android.graphics.RectF, android.graphics.PointF xy);
+    method public static inline operator android.graphics.RectF plus(android.graphics.RectF, android.graphics.RectF r);
+    method public static inline operator android.graphics.RectF plus(android.graphics.RectF, float xy);
+    method public static inline operator android.graphics.Rect times(android.graphics.Rect, int factor);
+    method public static inline operator android.graphics.RectF times(android.graphics.RectF, float factor);
+    method public static inline operator android.graphics.RectF times(android.graphics.RectF, int factor);
+    method public static inline android.graphics.Rect toRect(android.graphics.RectF);
+    method public static inline android.graphics.RectF toRectF(android.graphics.Rect);
+    method public static inline android.graphics.Region toRegion(android.graphics.Rect);
+    method public static inline android.graphics.Region toRegion(android.graphics.RectF);
+    method public static inline android.graphics.RectF transform(android.graphics.RectF, android.graphics.Matrix m);
+    method public static inline infix android.graphics.Region xor(android.graphics.Rect, android.graphics.Rect r);
+    method public static inline infix android.graphics.Region xor(android.graphics.RectF, android.graphics.RectF r);
+  }
+
+  public final class RegionKt {
+    method public static inline infix android.graphics.Region and(android.graphics.Region, android.graphics.Rect r);
+    method public static inline infix android.graphics.Region and(android.graphics.Region, android.graphics.Region r);
+    method public static inline operator boolean contains(android.graphics.Region, android.graphics.Point p);
+    method public static inline void forEach(android.graphics.Region, kotlin.jvm.functions.Function1<? super android.graphics.Rect,kotlin.Unit> action);
+    method public static operator java.util.Iterator<android.graphics.Rect> iterator(android.graphics.Region);
+    method public static inline operator android.graphics.Region minus(android.graphics.Region, android.graphics.Rect r);
+    method public static inline operator android.graphics.Region minus(android.graphics.Region, android.graphics.Region r);
+    method public static inline operator android.graphics.Region not(android.graphics.Region);
+    method public static inline infix android.graphics.Region or(android.graphics.Region, android.graphics.Rect r);
+    method public static inline infix android.graphics.Region or(android.graphics.Region, android.graphics.Region r);
+    method public static inline operator android.graphics.Region plus(android.graphics.Region, android.graphics.Rect r);
+    method public static inline operator android.graphics.Region plus(android.graphics.Region, android.graphics.Region r);
+    method public static inline operator android.graphics.Region unaryMinus(android.graphics.Region);
+    method public static inline infix android.graphics.Region xor(android.graphics.Region, android.graphics.Rect r);
+    method public static inline infix android.graphics.Region xor(android.graphics.Region, android.graphics.Region r);
+  }
+
+  public final class ShaderKt {
+    method public static inline void transform(android.graphics.Shader, kotlin.jvm.functions.Function1<? super android.graphics.Matrix,kotlin.Unit> block);
+  }
+
+}
+
+package androidx.core.graphics.drawable {
+
+  public final class BitmapDrawableKt {
+    method public static inline android.graphics.drawable.BitmapDrawable toDrawable(android.graphics.Bitmap, android.content.res.Resources resources);
+  }
+
+  public final class ColorDrawableKt {
+    method @RequiresApi(26) public static inline android.graphics.drawable.ColorDrawable toDrawable(android.graphics.Color);
+    method public static inline android.graphics.drawable.ColorDrawable toDrawable(@ColorInt int);
+  }
+
+  public final class DrawableKt {
+    method public static android.graphics.Bitmap toBitmap(android.graphics.drawable.Drawable, optional @Px int width, optional @Px int height, optional android.graphics.Bitmap.Config? config);
+    method public static android.graphics.Bitmap? toBitmapOrNull(android.graphics.drawable.Drawable, optional @Px int width, optional @Px int height, optional android.graphics.Bitmap.Config? config);
+    method public static void updateBounds(android.graphics.drawable.Drawable, optional @Px int left, optional @Px int top, optional @Px int right, optional @Px int bottom);
+  }
+
+  public final class IconKt {
+    method @RequiresApi(26) public static inline android.graphics.drawable.Icon toAdaptiveIcon(android.graphics.Bitmap);
+    method @RequiresApi(26) public static inline android.graphics.drawable.Icon toIcon(android.graphics.Bitmap);
+    method @RequiresApi(26) public static inline android.graphics.drawable.Icon toIcon(android.net.Uri);
+    method @RequiresApi(26) public static inline android.graphics.drawable.Icon toIcon(byte[]);
+  }
+
+}
+
+package androidx.core.location {
+
+  public final class LocationKt {
+    method public static inline operator double component1(android.location.Location);
+    method public static inline operator double component2(android.location.Location);
+  }
+
+}
+
+package androidx.core.net {
+
+  public final class UriKt {
+    method public static java.io.File toFile(android.net.Uri);
+    method public static inline android.net.Uri toUri(java.io.File);
+    method public static inline android.net.Uri toUri(String);
+  }
+
+}
+
+package androidx.core.os {
+
+  public final class BundleKt {
+    method public static android.os.Bundle bundleOf();
+    method public static android.os.Bundle bundleOf(kotlin.Pair<java.lang.String,?>... pairs);
+  }
+
+  public final class HandlerKt {
+    method public static inline Runnable postAtTime(android.os.Handler, long uptimeMillis, optional Object? token, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public static inline Runnable postDelayed(android.os.Handler, long delayInMillis, optional Object? token, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+  }
+
+  @RequiresApi(31) public final class OutcomeReceiverKt {
+    method @RequiresApi(31) public static <R, E extends java.lang.Throwable> android.os.OutcomeReceiver<R,E> asOutcomeReceiver(kotlin.coroutines.Continuation<? super R>);
+  }
+
+  public final class PersistableBundleKt {
+    method @RequiresApi(21) public static android.os.PersistableBundle persistableBundleOf();
+    method @RequiresApi(21) public static android.os.PersistableBundle persistableBundleOf(kotlin.Pair<java.lang.String,?>... pairs);
+    method @RequiresApi(21) public static android.os.PersistableBundle toPersistableBundle(java.util.Map<java.lang.String,?>);
+  }
+
+  public final class TraceKt {
+    method @Deprecated public static inline <T> T trace(String sectionName, kotlin.jvm.functions.Function0<? extends T> block);
+  }
+
+}
+
+package androidx.core.text {
+
+  public final class CharSequenceKt {
+    method public static inline boolean isDigitsOnly(CharSequence);
+    method public static inline int trimmedLength(CharSequence);
+  }
+
+  public final class HtmlKt {
+    method public static inline android.text.Spanned parseAsHtml(String, optional int flags, optional android.text.Html.ImageGetter? imageGetter, optional android.text.Html.TagHandler? tagHandler);
+    method public static inline String toHtml(android.text.Spanned, optional int option);
+  }
+
+  public final class LocaleKt {
+    method @RequiresApi(17) public static inline int getLayoutDirection(java.util.Locale);
+  }
+
+  public final class SpannableStringBuilderKt {
+    method public static inline android.text.SpannableStringBuilder backgroundColor(android.text.SpannableStringBuilder, @ColorInt int color, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder bold(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannedString buildSpannedString(kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder color(android.text.SpannableStringBuilder, @ColorInt int color, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder inSpans(android.text.SpannableStringBuilder, Object span, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder inSpans(android.text.SpannableStringBuilder, Object![] spans, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder italic(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder scale(android.text.SpannableStringBuilder, float proportion, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder strikeThrough(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder subscript(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder superscript(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder underline(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+  }
+
+  public final class SpannableStringKt {
+    method public static inline void clearSpans(android.text.Spannable);
+    method public static inline operator void set(android.text.Spannable, int start, int end, Object span);
+    method public static inline operator void set(android.text.Spannable, kotlin.ranges.IntRange range, Object span);
+    method public static inline android.text.Spannable toSpannable(CharSequence);
+  }
+
+  public final class SpannedStringKt {
+    method public static inline <reified T> T![] getSpans(android.text.Spanned, optional int start, optional int end);
+    method public static inline android.text.Spanned toSpanned(CharSequence);
+  }
+
+  public final class StringKt {
+    method public static inline String htmlEncode(String);
+  }
+
+}
+
+package androidx.core.transition {
+
+  public final class TransitionKt {
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener addListener(android.transition.Transition, optional kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> onEnd, optional kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> onStart, optional kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> onCancel, optional kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> onResume, optional kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> onPause);
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener doOnCancel(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener doOnEnd(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener doOnPause(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener doOnResume(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener doOnStart(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+  }
+
+}
+
+package androidx.core.util {
+
+  public final class AndroidXConsumerKt {
+    method public static <T> androidx.core.util.Consumer<T> asAndroidXConsumer(kotlin.coroutines.Continuation<? super T>);
+  }
+
+  public final class AtomicFileKt {
+    method @RequiresApi(17) public static inline byte[] readBytes(android.util.AtomicFile);
+    method @RequiresApi(17) public static String readText(android.util.AtomicFile, optional java.nio.charset.Charset charset);
+    method @RequiresApi(17) public static inline void tryWrite(android.util.AtomicFile, kotlin.jvm.functions.Function1<? super java.io.FileOutputStream,kotlin.Unit> block);
+    method @RequiresApi(17) public static void writeBytes(android.util.AtomicFile, byte[] array);
+    method @RequiresApi(17) public static void writeText(android.util.AtomicFile, String text, optional java.nio.charset.Charset charset);
+  }
+
+  @RequiresApi(24) public final class ConsumerKt {
+    method @RequiresApi(24) public static <T> java.util.function.Consumer<T> asConsumer(kotlin.coroutines.Continuation<? super T>);
+  }
+
+  public final class HalfKt {
+    method @RequiresApi(26) public static inline android.util.Half toHalf(double);
+    method @RequiresApi(26) public static inline android.util.Half toHalf(float);
+    method @RequiresApi(26) public static inline android.util.Half toHalf(String);
+    method @RequiresApi(26) public static inline android.util.Half toHalf(@HalfFloat short);
+  }
+
+  public final class LongSparseArrayKt {
+    method @RequiresApi(16) public static inline operator <T> boolean contains(android.util.LongSparseArray<T>, long key);
+    method @RequiresApi(16) public static inline <T> boolean containsKey(android.util.LongSparseArray<T>, long key);
+    method @RequiresApi(16) public static inline <T> boolean containsValue(android.util.LongSparseArray<T>, T value);
+    method @RequiresApi(16) public static inline <T> void forEach(android.util.LongSparseArray<T>, kotlin.jvm.functions.Function2<? super java.lang.Long,? super T,kotlin.Unit> action);
+    method @RequiresApi(16) public static inline <T> T getOrDefault(android.util.LongSparseArray<T>, long key, T defaultValue);
+    method @RequiresApi(16) public static inline <T> T getOrElse(android.util.LongSparseArray<T>, long key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+    method @RequiresApi(16) public static inline <T> int getSize(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static inline <T> boolean isEmpty(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static inline <T> boolean isNotEmpty(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static <T> kotlin.collections.LongIterator keyIterator(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static operator <T> android.util.LongSparseArray<T> plus(android.util.LongSparseArray<T>, android.util.LongSparseArray<T> other);
+    method @RequiresApi(16) public static <T> void putAll(android.util.LongSparseArray<T>, android.util.LongSparseArray<T> other);
+    method @RequiresApi(16) public static <T> boolean remove(android.util.LongSparseArray<T>, long key, T value);
+    method @RequiresApi(16) public static inline operator <T> void set(android.util.LongSparseArray<T>, long key, T value);
+    method @RequiresApi(16) public static <T> java.util.Iterator<T> valueIterator(android.util.LongSparseArray<T>);
+  }
+
+  public final class LruCacheKt {
+    method public static inline <K, V> android.util.LruCache<K,V> lruCache(int maxSize, optional kotlin.jvm.functions.Function2<? super K,? super V,java.lang.Integer> sizeOf, optional kotlin.jvm.functions.Function1<? super K,? extends V> create, optional kotlin.jvm.functions.Function4<? super java.lang.Boolean,? super K,? super V,? super V,kotlin.Unit> onEntryRemoved);
+  }
+
+  public final class PairKt {
+    method public static inline operator <F, S> F component1(android.util.Pair<F,S>);
+    method public static inline operator <F, S> F component1(androidx.core.util.Pair<F,S>);
+    method public static inline operator <F, S> S component2(android.util.Pair<F,S>);
+    method public static inline operator <F, S> S component2(androidx.core.util.Pair<F,S>);
+    method public static inline <F, S> android.util.Pair<F,S> toAndroidPair(kotlin.Pair<? extends F,? extends S>);
+    method public static inline <F, S> androidx.core.util.Pair<F,S> toAndroidXPair(kotlin.Pair<? extends F,? extends S>);
+    method public static inline <F, S> kotlin.Pair<F,S> toKotlinPair(android.util.Pair<F,S>);
+    method public static inline <F, S> kotlin.Pair<F,S> toKotlinPair(androidx.core.util.Pair<F,S>);
+  }
+
+  public final class RangeKt {
+    method @RequiresApi(21) public static inline infix <T extends java.lang.Comparable<? super T>> android.util.Range<T> and(android.util.Range<T>, android.util.Range<T> other);
+    method @RequiresApi(21) public static inline operator <T extends java.lang.Comparable<? super T>> android.util.Range<T> plus(android.util.Range<T>, android.util.Range<T> other);
+    method @RequiresApi(21) public static inline operator <T extends java.lang.Comparable<? super T>> android.util.Range<T> plus(android.util.Range<T>, T value);
+    method @RequiresApi(21) public static inline infix <T extends java.lang.Comparable<? super T>> android.util.Range<T> rangeTo(T, T that);
+    method @RequiresApi(21) public static <T extends java.lang.Comparable<? super T>> kotlin.ranges.ClosedRange<T> toClosedRange(android.util.Range<T>);
+    method @RequiresApi(21) public static <T extends java.lang.Comparable<? super T>> android.util.Range<T> toRange(kotlin.ranges.ClosedRange<T>);
+  }
+
+  public final class RunnableKt {
+    method public static Runnable asRunnable(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+  }
+
+  public final class SizeKt {
+    method @RequiresApi(21) public static inline operator int component1(android.util.Size);
+    method @RequiresApi(21) public static inline operator float component1(android.util.SizeF);
+    method public static inline operator float component1(androidx.core.util.SizeFCompat);
+    method @RequiresApi(21) public static inline operator int component2(android.util.Size);
+    method @RequiresApi(21) public static inline operator float component2(android.util.SizeF);
+    method public static inline operator float component2(androidx.core.util.SizeFCompat);
+  }
+
+  public final class SparseArrayKt {
+    method public static inline operator <T> boolean contains(android.util.SparseArray<T>, int key);
+    method public static inline <T> boolean containsKey(android.util.SparseArray<T>, int key);
+    method public static inline <T> boolean containsValue(android.util.SparseArray<T>, T value);
+    method public static inline <T> void forEach(android.util.SparseArray<T>, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,kotlin.Unit> action);
+    method public static inline <T> T getOrDefault(android.util.SparseArray<T>, int key, T defaultValue);
+    method public static inline <T> T getOrElse(android.util.SparseArray<T>, int key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+    method public static inline <T> int getSize(android.util.SparseArray<T>);
+    method public static inline <T> boolean isEmpty(android.util.SparseArray<T>);
+    method public static inline <T> boolean isNotEmpty(android.util.SparseArray<T>);
+    method public static <T> kotlin.collections.IntIterator keyIterator(android.util.SparseArray<T>);
+    method public static operator <T> android.util.SparseArray<T> plus(android.util.SparseArray<T>, android.util.SparseArray<T> other);
+    method public static <T> void putAll(android.util.SparseArray<T>, android.util.SparseArray<T> other);
+    method public static <T> boolean remove(android.util.SparseArray<T>, int key, T value);
+    method public static inline operator <T> void set(android.util.SparseArray<T>, int key, T value);
+    method public static <T> java.util.Iterator<T> valueIterator(android.util.SparseArray<T>);
+  }
+
+  public final class SparseBooleanArrayKt {
+    method public static inline operator boolean contains(android.util.SparseBooleanArray, int key);
+    method public static inline boolean containsKey(android.util.SparseBooleanArray, int key);
+    method public static inline boolean containsValue(android.util.SparseBooleanArray, boolean value);
+    method public static inline void forEach(android.util.SparseBooleanArray, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Boolean,kotlin.Unit> action);
+    method public static inline boolean getOrDefault(android.util.SparseBooleanArray, int key, boolean defaultValue);
+    method public static inline boolean getOrElse(android.util.SparseBooleanArray, int key, kotlin.jvm.functions.Function0<java.lang.Boolean> defaultValue);
+    method public static inline int getSize(android.util.SparseBooleanArray);
+    method public static inline boolean isEmpty(android.util.SparseBooleanArray);
+    method public static inline boolean isNotEmpty(android.util.SparseBooleanArray);
+    method public static kotlin.collections.IntIterator keyIterator(android.util.SparseBooleanArray);
+    method public static operator android.util.SparseBooleanArray plus(android.util.SparseBooleanArray, android.util.SparseBooleanArray other);
+    method public static void putAll(android.util.SparseBooleanArray, android.util.SparseBooleanArray other);
+    method public static boolean remove(android.util.SparseBooleanArray, int key, boolean value);
+    method public static inline operator void set(android.util.SparseBooleanArray, int key, boolean value);
+    method public static kotlin.collections.BooleanIterator valueIterator(android.util.SparseBooleanArray);
+  }
+
+  public final class SparseIntArrayKt {
+    method public static inline operator boolean contains(android.util.SparseIntArray, int key);
+    method public static inline boolean containsKey(android.util.SparseIntArray, int key);
+    method public static inline boolean containsValue(android.util.SparseIntArray, int value);
+    method public static inline void forEach(android.util.SparseIntArray, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> action);
+    method public static inline int getOrDefault(android.util.SparseIntArray, int key, int defaultValue);
+    method public static inline int getOrElse(android.util.SparseIntArray, int key, kotlin.jvm.functions.Function0<java.lang.Integer> defaultValue);
+    method public static inline int getSize(android.util.SparseIntArray);
+    method public static inline boolean isEmpty(android.util.SparseIntArray);
+    method public static inline boolean isNotEmpty(android.util.SparseIntArray);
+    method public static kotlin.collections.IntIterator keyIterator(android.util.SparseIntArray);
+    method public static operator android.util.SparseIntArray plus(android.util.SparseIntArray, android.util.SparseIntArray other);
+    method public static void putAll(android.util.SparseIntArray, android.util.SparseIntArray other);
+    method public static boolean remove(android.util.SparseIntArray, int key, int value);
+    method public static inline operator void set(android.util.SparseIntArray, int key, int value);
+    method public static kotlin.collections.IntIterator valueIterator(android.util.SparseIntArray);
+  }
+
+  public final class SparseLongArrayKt {
+    method @RequiresApi(18) public static inline operator boolean contains(android.util.SparseLongArray, int key);
+    method @RequiresApi(18) public static inline boolean containsKey(android.util.SparseLongArray, int key);
+    method @RequiresApi(18) public static inline boolean containsValue(android.util.SparseLongArray, long value);
+    method @RequiresApi(18) public static inline void forEach(android.util.SparseLongArray, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Long,kotlin.Unit> action);
+    method @RequiresApi(18) public static inline long getOrDefault(android.util.SparseLongArray, int key, long defaultValue);
+    method @RequiresApi(18) public static inline long getOrElse(android.util.SparseLongArray, int key, kotlin.jvm.functions.Function0<java.lang.Long> defaultValue);
+    method @RequiresApi(18) public static inline int getSize(android.util.SparseLongArray);
+    method @RequiresApi(18) public static inline boolean isEmpty(android.util.SparseLongArray);
+    method @RequiresApi(18) public static inline boolean isNotEmpty(android.util.SparseLongArray);
+    method @RequiresApi(18) public static kotlin.collections.IntIterator keyIterator(android.util.SparseLongArray);
+    method @RequiresApi(18) public static operator android.util.SparseLongArray plus(android.util.SparseLongArray, android.util.SparseLongArray other);
+    method @RequiresApi(18) public static void putAll(android.util.SparseLongArray, android.util.SparseLongArray other);
+    method @RequiresApi(18) public static boolean remove(android.util.SparseLongArray, int key, long value);
+    method @RequiresApi(18) public static inline operator void set(android.util.SparseLongArray, int key, long value);
+    method @RequiresApi(18) public static kotlin.collections.LongIterator valueIterator(android.util.SparseLongArray);
+  }
+
+}
+
+package androidx.core.view {
+
+  public final class MenuKt {
+    method public static operator boolean contains(android.view.Menu, android.view.MenuItem item);
+    method public static inline void forEach(android.view.Menu, kotlin.jvm.functions.Function1<? super android.view.MenuItem,kotlin.Unit> action);
+    method public static inline void forEachIndexed(android.view.Menu, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super android.view.MenuItem,kotlin.Unit> action);
+    method public static inline operator android.view.MenuItem get(android.view.Menu, int index);
+    method public static kotlin.sequences.Sequence<android.view.MenuItem> getChildren(android.view.Menu);
+    method public static inline int getSize(android.view.Menu);
+    method public static inline boolean isEmpty(android.view.Menu);
+    method public static inline boolean isNotEmpty(android.view.Menu);
+    method public static operator java.util.Iterator<android.view.MenuItem> iterator(android.view.Menu);
+    method public static inline operator void minusAssign(android.view.Menu, android.view.MenuItem item);
+    method public static inline void removeItemAt(android.view.Menu, int index);
+  }
+
+  public final class ViewGroupKt {
+    method public static inline operator boolean contains(android.view.ViewGroup, android.view.View view);
+    method public static inline void forEach(android.view.ViewGroup, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static inline void forEachIndexed(android.view.ViewGroup, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super android.view.View,kotlin.Unit> action);
+    method public static operator android.view.View get(android.view.ViewGroup, int index);
+    method public static kotlin.sequences.Sequence<android.view.View> getChildren(android.view.ViewGroup);
+    method public static kotlin.sequences.Sequence<android.view.View> getDescendants(android.view.ViewGroup);
+    method public static inline kotlin.ranges.IntRange getIndices(android.view.ViewGroup);
+    method public static inline int getSize(android.view.ViewGroup);
+    method public static inline boolean isEmpty(android.view.ViewGroup);
+    method public static inline boolean isNotEmpty(android.view.ViewGroup);
+    method public static operator java.util.Iterator<android.view.View> iterator(android.view.ViewGroup);
+    method public static inline operator void minusAssign(android.view.ViewGroup, android.view.View view);
+    method public static inline operator void plusAssign(android.view.ViewGroup, android.view.View view);
+    method public static inline void setMargins(android.view.ViewGroup.MarginLayoutParams, @Px int size);
+    method public static inline void updateMargins(android.view.ViewGroup.MarginLayoutParams, optional @Px int left, optional @Px int top, optional @Px int right, optional @Px int bottom);
+    method @RequiresApi(17) public static inline void updateMarginsRelative(android.view.ViewGroup.MarginLayoutParams, optional @Px int start, optional @Px int top, optional @Px int end, optional @Px int bottom);
+  }
+
+  public final class ViewKt {
+    method public static inline void doOnAttach(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static inline void doOnDetach(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static inline void doOnLayout(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static inline void doOnNextLayout(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static inline androidx.core.view.OneShotPreDrawListener doOnPreDraw(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static android.graphics.Bitmap drawToBitmap(android.view.View, optional android.graphics.Bitmap.Config config);
+    method public static kotlin.sequences.Sequence<android.view.View> getAllViews(android.view.View);
+    method public static kotlin.sequences.Sequence<android.view.ViewParent> getAncestors(android.view.View);
+    method public static inline int getMarginBottom(android.view.View);
+    method public static inline int getMarginEnd(android.view.View);
+    method public static inline int getMarginLeft(android.view.View);
+    method public static inline int getMarginRight(android.view.View);
+    method public static inline int getMarginStart(android.view.View);
+    method public static inline int getMarginTop(android.view.View);
+    method public static inline boolean isGone(android.view.View);
+    method public static inline boolean isInvisible(android.view.View);
+    method public static inline boolean isVisible(android.view.View);
+    method public static inline Runnable postDelayed(android.view.View, long delayInMillis, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method @RequiresApi(16) public static Runnable postOnAnimationDelayed(android.view.View, long delayInMillis, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public static inline void setGone(android.view.View, boolean);
+    method public static inline void setInvisible(android.view.View, boolean);
+    method public static inline void setPadding(android.view.View, @Px int size);
+    method public static inline void setVisible(android.view.View, boolean);
+    method public static inline void updateLayoutParams(android.view.View, kotlin.jvm.functions.Function1<? super android.view.ViewGroup.LayoutParams,kotlin.Unit> block);
+    method public static inline <reified T extends android.view.ViewGroup.LayoutParams> void updateLayoutParamsTyped(android.view.View, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> block);
+    method public static inline void updatePadding(android.view.View, optional @Px int left, optional @Px int top, optional @Px int right, optional @Px int bottom);
+    method @RequiresApi(17) public static inline void updatePaddingRelative(android.view.View, optional @Px int start, optional @Px int top, optional @Px int end, optional @Px int bottom);
+  }
+
+}
+
+package androidx.core.widget {
+
+  public final class TextViewKt {
+    method public static inline android.text.TextWatcher addTextChangedListener(android.widget.TextView, optional kotlin.jvm.functions.Function4<? super java.lang.CharSequence,? super java.lang.Integer,? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> beforeTextChanged, optional kotlin.jvm.functions.Function4<? super java.lang.CharSequence,? super java.lang.Integer,? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> onTextChanged, optional kotlin.jvm.functions.Function1<? super android.text.Editable,kotlin.Unit> afterTextChanged);
+    method public static inline android.text.TextWatcher doAfterTextChanged(android.widget.TextView, kotlin.jvm.functions.Function1<? super android.text.Editable,kotlin.Unit> action);
+    method public static inline android.text.TextWatcher doBeforeTextChanged(android.widget.TextView, kotlin.jvm.functions.Function4<? super java.lang.CharSequence,? super java.lang.Integer,? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> action);
+    method public static inline android.text.TextWatcher doOnTextChanged(android.widget.TextView, kotlin.jvm.functions.Function4<? super java.lang.CharSequence,? super java.lang.Integer,? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> action);
+  }
+
+}
+
diff --git a/core/core-ktx/api/res-1.12.0-beta01.txt b/core/core-ktx/api/res-1.12.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/core/core-ktx/api/res-1.12.0-beta01.txt
diff --git a/core/core-ktx/api/restricted_1.12.0-beta01.txt b/core/core-ktx/api/restricted_1.12.0-beta01.txt
new file mode 100644
index 0000000..7a12effb
--- /dev/null
+++ b/core/core-ktx/api/restricted_1.12.0-beta01.txt
@@ -0,0 +1,634 @@
+// Signature format: 4.0
+package androidx.core.animation {
+
+  public final class AnimatorKt {
+    method public static inline android.animation.Animator.AnimatorListener addListener(android.animation.Animator, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onEnd, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onStart, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onCancel, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onRepeat);
+    method @RequiresApi(19) public static android.animation.Animator.AnimatorPauseListener addPauseListener(android.animation.Animator, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onResume, optional kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> onPause);
+    method public static inline android.animation.Animator.AnimatorListener doOnCancel(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method public static inline android.animation.Animator.AnimatorListener doOnEnd(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method @RequiresApi(19) public static android.animation.Animator.AnimatorPauseListener doOnPause(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method public static inline android.animation.Animator.AnimatorListener doOnRepeat(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method @RequiresApi(19) public static android.animation.Animator.AnimatorPauseListener doOnResume(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+    method public static inline android.animation.Animator.AnimatorListener doOnStart(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit> action);
+  }
+
+}
+
+package androidx.core.content {
+
+  public final class ContentValuesKt {
+    method public static android.content.ContentValues contentValuesOf(kotlin.Pair<java.lang.String,?>... pairs);
+  }
+
+  public final class ContextKt {
+    method public static inline <reified T> T! getSystemService(android.content.Context);
+    method public static inline void withStyledAttributes(android.content.Context, optional android.util.AttributeSet? set, int[] attrs, optional @AttrRes int defStyleAttr, optional @StyleRes int defStyleRes, kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,kotlin.Unit> block);
+    method public static inline void withStyledAttributes(android.content.Context, @StyleRes int resourceId, int[] attrs, kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,kotlin.Unit> block);
+  }
+
+  public final class SharedPreferencesKt {
+    method public static inline void edit(android.content.SharedPreferences, optional boolean commit, kotlin.jvm.functions.Function1<? super android.content.SharedPreferences.Editor,kotlin.Unit> action);
+  }
+
+}
+
+package androidx.core.content.res {
+
+  public final class TypedArrayKt {
+    method public static boolean getBooleanOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @ColorInt public static int getColorOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static android.content.res.ColorStateList getColorStateListOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static float getDimensionOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @Dimension public static int getDimensionPixelOffsetOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @Dimension public static int getDimensionPixelSizeOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static android.graphics.drawable.Drawable getDrawableOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static float getFloatOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @RequiresApi(26) public static android.graphics.Typeface getFontOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static int getIntOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static int getIntegerOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method @AnyRes public static int getResourceIdOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static String getStringOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static CharSequence![] getTextArrayOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static CharSequence getTextOrThrow(android.content.res.TypedArray, @StyleableRes int index);
+    method public static inline <R> R use(android.content.res.TypedArray, kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,? extends R> block);
+  }
+
+}
+
+package androidx.core.database {
+
+  public final class CursorKt {
+    method public static inline byte[]? getBlobOrNull(android.database.Cursor, int index);
+    method public static inline Double? getDoubleOrNull(android.database.Cursor, int index);
+    method public static inline Float? getFloatOrNull(android.database.Cursor, int index);
+    method public static inline Integer? getIntOrNull(android.database.Cursor, int index);
+    method public static inline Long? getLongOrNull(android.database.Cursor, int index);
+    method public static inline Short? getShortOrNull(android.database.Cursor, int index);
+    method public static inline String? getStringOrNull(android.database.Cursor, int index);
+  }
+
+}
+
+package androidx.core.database.sqlite {
+
+  public final class SQLiteDatabaseKt {
+    method public static inline <T> T transaction(android.database.sqlite.SQLiteDatabase, optional boolean exclusive, kotlin.jvm.functions.Function1<? super android.database.sqlite.SQLiteDatabase,? extends T> body);
+  }
+
+}
+
+package androidx.core.graphics {
+
+  public final class BitmapKt {
+    method public static inline android.graphics.Bitmap applyCanvas(android.graphics.Bitmap, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline operator boolean contains(android.graphics.Bitmap, android.graphics.Point p);
+    method public static inline operator boolean contains(android.graphics.Bitmap, android.graphics.PointF p);
+    method public static inline android.graphics.Bitmap createBitmap(int width, int height, optional android.graphics.Bitmap.Config config);
+    method @RequiresApi(26) public static inline android.graphics.Bitmap createBitmap(int width, int height, optional android.graphics.Bitmap.Config config, optional boolean hasAlpha, optional android.graphics.ColorSpace colorSpace);
+    method public static inline operator int get(android.graphics.Bitmap, int x, int y);
+    method public static inline android.graphics.Bitmap scale(android.graphics.Bitmap, int width, int height, optional boolean filter);
+    method public static inline operator void set(android.graphics.Bitmap, int x, int y, @ColorInt int color);
+  }
+
+  public final class CanvasKt {
+    method public static inline void withClip(android.graphics.Canvas, android.graphics.Path clipPath, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withClip(android.graphics.Canvas, android.graphics.Rect clipRect, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withClip(android.graphics.Canvas, android.graphics.RectF clipRect, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withClip(android.graphics.Canvas, float left, float top, float right, float bottom, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withClip(android.graphics.Canvas, int left, int top, int right, int bottom, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withMatrix(android.graphics.Canvas, optional android.graphics.Matrix matrix, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withRotation(android.graphics.Canvas, optional float degrees, optional float pivotX, optional float pivotY, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withSave(android.graphics.Canvas, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withScale(android.graphics.Canvas, optional float x, optional float y, optional float pivotX, optional float pivotY, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withSkew(android.graphics.Canvas, optional float x, optional float y, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+    method public static inline void withTranslation(android.graphics.Canvas, optional float x, optional float y, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+  }
+
+  public final class ColorKt {
+    method @RequiresApi(26) public static inline operator float component1(android.graphics.Color);
+    method public static inline operator int component1(@ColorInt int);
+    method @RequiresApi(26) public static inline operator float component1(@ColorLong long);
+    method @RequiresApi(26) public static inline operator float component2(android.graphics.Color);
+    method public static inline operator int component2(@ColorInt int);
+    method @RequiresApi(26) public static inline operator float component2(@ColorLong long);
+    method @RequiresApi(26) public static inline operator float component3(android.graphics.Color);
+    method public static inline operator int component3(@ColorInt int);
+    method @RequiresApi(26) public static inline operator float component3(@ColorLong long);
+    method @RequiresApi(26) public static inline operator float component4(android.graphics.Color);
+    method public static inline operator int component4(@ColorInt int);
+    method @RequiresApi(26) public static inline operator float component4(@ColorLong long);
+    method @RequiresApi(26) public static inline infix android.graphics.Color convertTo(android.graphics.Color, android.graphics.ColorSpace colorSpace);
+    method @RequiresApi(26) public static inline infix android.graphics.Color convertTo(android.graphics.Color, android.graphics.ColorSpace.Named colorSpace);
+    method @ColorLong @RequiresApi(26) public static inline infix long convertTo(@ColorInt int, android.graphics.ColorSpace colorSpace);
+    method @ColorLong @RequiresApi(26) public static inline infix long convertTo(@ColorInt int, android.graphics.ColorSpace.Named colorSpace);
+    method @ColorLong @RequiresApi(26) public static inline infix long convertTo(@ColorLong long, android.graphics.ColorSpace colorSpace);
+    method @ColorLong @RequiresApi(26) public static inline infix long convertTo(@ColorLong long, android.graphics.ColorSpace.Named colorSpace);
+    method public static inline int getAlpha(@ColorInt int);
+    method @RequiresApi(26) public static inline float getAlpha(@ColorLong long);
+    method public static inline int getBlue(@ColorInt int);
+    method @RequiresApi(26) public static inline float getBlue(@ColorLong long);
+    method @RequiresApi(26) public static inline android.graphics.ColorSpace getColorSpace(@ColorLong long);
+    method public static inline int getGreen(@ColorInt int);
+    method @RequiresApi(26) public static inline float getGreen(@ColorLong long);
+    method @RequiresApi(26) public static inline float getLuminance(@ColorInt int);
+    method @RequiresApi(26) public static inline float getLuminance(@ColorLong long);
+    method public static inline int getRed(@ColorInt int);
+    method @RequiresApi(26) public static inline float getRed(@ColorLong long);
+    method @RequiresApi(26) public static inline boolean isSrgb(@ColorLong long);
+    method @RequiresApi(26) public static inline boolean isWideGamut(@ColorLong long);
+    method @RequiresApi(26) public static operator android.graphics.Color plus(android.graphics.Color, android.graphics.Color c);
+    method @RequiresApi(26) public static inline android.graphics.Color toColor(@ColorInt int);
+    method @RequiresApi(26) public static inline android.graphics.Color toColor(@ColorLong long);
+    method @ColorInt public static inline int toColorInt(String);
+    method @ColorInt @RequiresApi(26) public static inline int toColorInt(@ColorLong long);
+    method @ColorLong @RequiresApi(26) public static inline long toColorLong(@ColorInt int);
+  }
+
+  public final class ImageDecoderKt {
+    method @RequiresApi(28) public static inline android.graphics.Bitmap decodeBitmap(android.graphics.ImageDecoder.Source, kotlin.jvm.functions.Function3<? super android.graphics.ImageDecoder,? super android.graphics.ImageDecoder.ImageInfo,? super android.graphics.ImageDecoder.Source,kotlin.Unit> action);
+    method @RequiresApi(28) public static inline android.graphics.drawable.Drawable decodeDrawable(android.graphics.ImageDecoder.Source, kotlin.jvm.functions.Function3<? super android.graphics.ImageDecoder,? super android.graphics.ImageDecoder.ImageInfo,? super android.graphics.ImageDecoder.Source,kotlin.Unit> action);
+  }
+
+  public final class MatrixKt {
+    method public static android.graphics.Matrix rotationMatrix(float degrees, optional float px, optional float py);
+    method public static android.graphics.Matrix scaleMatrix(optional float sx, optional float sy);
+    method public static inline operator android.graphics.Matrix times(android.graphics.Matrix, android.graphics.Matrix m);
+    method public static android.graphics.Matrix translationMatrix(optional float tx, optional float ty);
+    method public static inline float[] values(android.graphics.Matrix);
+  }
+
+  public final class PaintKt {
+    method public static inline boolean setBlendMode(android.graphics.Paint, androidx.core.graphics.BlendModeCompat? blendModeCompat);
+  }
+
+  public final class PathKt {
+    method @RequiresApi(19) public static inline infix android.graphics.Path and(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(26) public static Iterable<androidx.core.graphics.PathSegment> flatten(android.graphics.Path, optional float error);
+    method @RequiresApi(19) public static inline operator android.graphics.Path minus(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(19) public static inline infix android.graphics.Path or(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(19) public static inline operator android.graphics.Path plus(android.graphics.Path, android.graphics.Path p);
+    method @RequiresApi(19) public static inline infix android.graphics.Path xor(android.graphics.Path, android.graphics.Path p);
+  }
+
+  public final class PictureKt {
+    method public static inline android.graphics.Picture record(android.graphics.Picture, int width, int height, kotlin.jvm.functions.Function1<? super android.graphics.Canvas,kotlin.Unit> block);
+  }
+
+  public final class PointKt {
+    method public static inline operator int component1(android.graphics.Point);
+    method public static inline operator float component1(android.graphics.PointF);
+    method public static inline operator int component2(android.graphics.Point);
+    method public static inline operator float component2(android.graphics.PointF);
+    method public static inline operator android.graphics.Point div(android.graphics.Point, float scalar);
+    method public static inline operator android.graphics.PointF div(android.graphics.PointF, float scalar);
+    method public static inline operator android.graphics.Point minus(android.graphics.Point, android.graphics.Point p);
+    method public static inline operator android.graphics.Point minus(android.graphics.Point, int xy);
+    method public static inline operator android.graphics.PointF minus(android.graphics.PointF, android.graphics.PointF p);
+    method public static inline operator android.graphics.PointF minus(android.graphics.PointF, float xy);
+    method public static inline operator android.graphics.Point plus(android.graphics.Point, android.graphics.Point p);
+    method public static inline operator android.graphics.Point plus(android.graphics.Point, int xy);
+    method public static inline operator android.graphics.PointF plus(android.graphics.PointF, android.graphics.PointF p);
+    method public static inline operator android.graphics.PointF plus(android.graphics.PointF, float xy);
+    method public static inline operator android.graphics.Point times(android.graphics.Point, float scalar);
+    method public static inline operator android.graphics.PointF times(android.graphics.PointF, float scalar);
+    method public static inline android.graphics.Point toPoint(android.graphics.PointF);
+    method public static inline android.graphics.PointF toPointF(android.graphics.Point);
+    method public static inline operator android.graphics.Point unaryMinus(android.graphics.Point);
+    method public static inline operator android.graphics.PointF unaryMinus(android.graphics.PointF);
+  }
+
+  public final class PorterDuffKt {
+    method public static inline android.graphics.PorterDuffColorFilter toColorFilter(android.graphics.PorterDuff.Mode, int color);
+    method public static inline android.graphics.PorterDuffXfermode toXfermode(android.graphics.PorterDuff.Mode);
+  }
+
+  public final class RectKt {
+    method public static inline infix android.graphics.Rect and(android.graphics.Rect, android.graphics.Rect r);
+    method public static inline infix android.graphics.RectF and(android.graphics.RectF, android.graphics.RectF r);
+    method public static inline operator int component1(android.graphics.Rect);
+    method public static inline operator float component1(android.graphics.RectF);
+    method public static inline operator int component2(android.graphics.Rect);
+    method public static inline operator float component2(android.graphics.RectF);
+    method public static inline operator int component3(android.graphics.Rect);
+    method public static inline operator float component3(android.graphics.RectF);
+    method public static inline operator int component4(android.graphics.Rect);
+    method public static inline operator float component4(android.graphics.RectF);
+    method public static inline operator boolean contains(android.graphics.Rect, android.graphics.Point p);
+    method public static inline operator boolean contains(android.graphics.RectF, android.graphics.PointF p);
+    method public static inline operator android.graphics.Rect minus(android.graphics.Rect, android.graphics.Point xy);
+    method public static inline operator android.graphics.Region minus(android.graphics.Rect, android.graphics.Rect r);
+    method public static inline operator android.graphics.Rect minus(android.graphics.Rect, int xy);
+    method public static inline operator android.graphics.RectF minus(android.graphics.RectF, android.graphics.PointF xy);
+    method public static inline operator android.graphics.Region minus(android.graphics.RectF, android.graphics.RectF r);
+    method public static inline operator android.graphics.RectF minus(android.graphics.RectF, float xy);
+    method public static inline infix android.graphics.Rect or(android.graphics.Rect, android.graphics.Rect r);
+    method public static inline infix android.graphics.RectF or(android.graphics.RectF, android.graphics.RectF r);
+    method public static inline operator android.graphics.Rect plus(android.graphics.Rect, android.graphics.Point xy);
+    method public static inline operator android.graphics.Rect plus(android.graphics.Rect, android.graphics.Rect r);
+    method public static inline operator android.graphics.Rect plus(android.graphics.Rect, int xy);
+    method public static inline operator android.graphics.RectF plus(android.graphics.RectF, android.graphics.PointF xy);
+    method public static inline operator android.graphics.RectF plus(android.graphics.RectF, android.graphics.RectF r);
+    method public static inline operator android.graphics.RectF plus(android.graphics.RectF, float xy);
+    method public static inline operator android.graphics.Rect times(android.graphics.Rect, int factor);
+    method public static inline operator android.graphics.RectF times(android.graphics.RectF, float factor);
+    method public static inline operator android.graphics.RectF times(android.graphics.RectF, int factor);
+    method public static inline android.graphics.Rect toRect(android.graphics.RectF);
+    method public static inline android.graphics.RectF toRectF(android.graphics.Rect);
+    method public static inline android.graphics.Region toRegion(android.graphics.Rect);
+    method public static inline android.graphics.Region toRegion(android.graphics.RectF);
+    method public static inline android.graphics.RectF transform(android.graphics.RectF, android.graphics.Matrix m);
+    method public static inline infix android.graphics.Region xor(android.graphics.Rect, android.graphics.Rect r);
+    method public static inline infix android.graphics.Region xor(android.graphics.RectF, android.graphics.RectF r);
+  }
+
+  public final class RegionKt {
+    method public static inline infix android.graphics.Region and(android.graphics.Region, android.graphics.Rect r);
+    method public static inline infix android.graphics.Region and(android.graphics.Region, android.graphics.Region r);
+    method public static inline operator boolean contains(android.graphics.Region, android.graphics.Point p);
+    method public static inline void forEach(android.graphics.Region, kotlin.jvm.functions.Function1<? super android.graphics.Rect,kotlin.Unit> action);
+    method public static operator java.util.Iterator<android.graphics.Rect> iterator(android.graphics.Region);
+    method public static inline operator android.graphics.Region minus(android.graphics.Region, android.graphics.Rect r);
+    method public static inline operator android.graphics.Region minus(android.graphics.Region, android.graphics.Region r);
+    method public static inline operator android.graphics.Region not(android.graphics.Region);
+    method public static inline infix android.graphics.Region or(android.graphics.Region, android.graphics.Rect r);
+    method public static inline infix android.graphics.Region or(android.graphics.Region, android.graphics.Region r);
+    method public static inline operator android.graphics.Region plus(android.graphics.Region, android.graphics.Rect r);
+    method public static inline operator android.graphics.Region plus(android.graphics.Region, android.graphics.Region r);
+    method public static inline operator android.graphics.Region unaryMinus(android.graphics.Region);
+    method public static inline infix android.graphics.Region xor(android.graphics.Region, android.graphics.Rect r);
+    method public static inline infix android.graphics.Region xor(android.graphics.Region, android.graphics.Region r);
+  }
+
+  public final class ShaderKt {
+    method public static inline void transform(android.graphics.Shader, kotlin.jvm.functions.Function1<? super android.graphics.Matrix,kotlin.Unit> block);
+  }
+
+}
+
+package androidx.core.graphics.drawable {
+
+  public final class BitmapDrawableKt {
+    method public static inline android.graphics.drawable.BitmapDrawable toDrawable(android.graphics.Bitmap, android.content.res.Resources resources);
+  }
+
+  public final class ColorDrawableKt {
+    method @RequiresApi(26) public static inline android.graphics.drawable.ColorDrawable toDrawable(android.graphics.Color);
+    method public static inline android.graphics.drawable.ColorDrawable toDrawable(@ColorInt int);
+  }
+
+  public final class DrawableKt {
+    method public static android.graphics.Bitmap toBitmap(android.graphics.drawable.Drawable, optional @Px int width, optional @Px int height, optional android.graphics.Bitmap.Config? config);
+    method public static android.graphics.Bitmap? toBitmapOrNull(android.graphics.drawable.Drawable, optional @Px int width, optional @Px int height, optional android.graphics.Bitmap.Config? config);
+    method public static void updateBounds(android.graphics.drawable.Drawable, optional @Px int left, optional @Px int top, optional @Px int right, optional @Px int bottom);
+  }
+
+  public final class IconKt {
+    method @RequiresApi(26) public static inline android.graphics.drawable.Icon toAdaptiveIcon(android.graphics.Bitmap);
+    method @RequiresApi(26) public static inline android.graphics.drawable.Icon toIcon(android.graphics.Bitmap);
+    method @RequiresApi(26) public static inline android.graphics.drawable.Icon toIcon(android.net.Uri);
+    method @RequiresApi(26) public static inline android.graphics.drawable.Icon toIcon(byte[]);
+  }
+
+}
+
+package androidx.core.location {
+
+  public final class LocationKt {
+    method public static inline operator double component1(android.location.Location);
+    method public static inline operator double component2(android.location.Location);
+  }
+
+}
+
+package androidx.core.net {
+
+  public final class UriKt {
+    method public static java.io.File toFile(android.net.Uri);
+    method public static inline android.net.Uri toUri(java.io.File);
+    method public static inline android.net.Uri toUri(String);
+  }
+
+}
+
+package androidx.core.os {
+
+  public final class BundleKt {
+    method public static android.os.Bundle bundleOf();
+    method public static android.os.Bundle bundleOf(kotlin.Pair<java.lang.String,?>... pairs);
+  }
+
+  public final class HandlerKt {
+    method public static inline Runnable postAtTime(android.os.Handler, long uptimeMillis, optional Object? token, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public static inline Runnable postDelayed(android.os.Handler, long delayInMillis, optional Object? token, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+  }
+
+  @RequiresApi(31) public final class OutcomeReceiverKt {
+    method @RequiresApi(31) public static <R, E extends java.lang.Throwable> android.os.OutcomeReceiver<R,E> asOutcomeReceiver(kotlin.coroutines.Continuation<? super R>);
+  }
+
+  public final class PersistableBundleKt {
+    method @RequiresApi(21) public static android.os.PersistableBundle persistableBundleOf();
+    method @RequiresApi(21) public static android.os.PersistableBundle persistableBundleOf(kotlin.Pair<java.lang.String,?>... pairs);
+    method @RequiresApi(21) public static android.os.PersistableBundle toPersistableBundle(java.util.Map<java.lang.String,?>);
+  }
+
+  public final class TraceKt {
+    method @Deprecated public static inline <T> T trace(String sectionName, kotlin.jvm.functions.Function0<? extends T> block);
+  }
+
+}
+
+package androidx.core.text {
+
+  public final class CharSequenceKt {
+    method public static inline boolean isDigitsOnly(CharSequence);
+    method public static inline int trimmedLength(CharSequence);
+  }
+
+  public final class HtmlKt {
+    method public static inline android.text.Spanned parseAsHtml(String, optional int flags, optional android.text.Html.ImageGetter? imageGetter, optional android.text.Html.TagHandler? tagHandler);
+    method public static inline String toHtml(android.text.Spanned, optional int option);
+  }
+
+  public final class LocaleKt {
+    method @RequiresApi(17) public static inline int getLayoutDirection(java.util.Locale);
+  }
+
+  public final class SpannableStringBuilderKt {
+    method public static inline android.text.SpannableStringBuilder backgroundColor(android.text.SpannableStringBuilder, @ColorInt int color, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder bold(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannedString buildSpannedString(kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder color(android.text.SpannableStringBuilder, @ColorInt int color, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder inSpans(android.text.SpannableStringBuilder, Object span, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder inSpans(android.text.SpannableStringBuilder, Object![] spans, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder italic(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder scale(android.text.SpannableStringBuilder, float proportion, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder strikeThrough(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder subscript(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder superscript(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+    method public static inline android.text.SpannableStringBuilder underline(android.text.SpannableStringBuilder, kotlin.jvm.functions.Function1<? super android.text.SpannableStringBuilder,kotlin.Unit> builderAction);
+  }
+
+  public final class SpannableStringKt {
+    method public static inline void clearSpans(android.text.Spannable);
+    method public static inline operator void set(android.text.Spannable, int start, int end, Object span);
+    method public static inline operator void set(android.text.Spannable, kotlin.ranges.IntRange range, Object span);
+    method public static inline android.text.Spannable toSpannable(CharSequence);
+  }
+
+  public final class SpannedStringKt {
+    method public static inline <reified T> T![] getSpans(android.text.Spanned, optional int start, optional int end);
+    method public static inline android.text.Spanned toSpanned(CharSequence);
+  }
+
+  public final class StringKt {
+    method public static inline String htmlEncode(String);
+  }
+
+}
+
+package androidx.core.transition {
+
+  public final class TransitionKt {
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener addListener(android.transition.Transition, optional kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> onEnd, optional kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> onStart, optional kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> onCancel, optional kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> onResume, optional kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> onPause);
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener doOnCancel(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener doOnEnd(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener doOnPause(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener doOnResume(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+    method @RequiresApi(19) public static inline android.transition.Transition.TransitionListener doOnStart(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit> action);
+  }
+
+}
+
+package androidx.core.util {
+
+  public final class AndroidXConsumerKt {
+    method public static <T> androidx.core.util.Consumer<T> asAndroidXConsumer(kotlin.coroutines.Continuation<? super T>);
+  }
+
+  public final class AtomicFileKt {
+    method @RequiresApi(17) public static inline byte[] readBytes(android.util.AtomicFile);
+    method @RequiresApi(17) public static String readText(android.util.AtomicFile, optional java.nio.charset.Charset charset);
+    method @RequiresApi(17) public static inline void tryWrite(android.util.AtomicFile, kotlin.jvm.functions.Function1<? super java.io.FileOutputStream,kotlin.Unit> block);
+    method @RequiresApi(17) public static void writeBytes(android.util.AtomicFile, byte[] array);
+    method @RequiresApi(17) public static void writeText(android.util.AtomicFile, String text, optional java.nio.charset.Charset charset);
+  }
+
+  @RequiresApi(24) public final class ConsumerKt {
+    method @RequiresApi(24) public static <T> java.util.function.Consumer<T> asConsumer(kotlin.coroutines.Continuation<? super T>);
+  }
+
+  public final class HalfKt {
+    method @RequiresApi(26) public static inline android.util.Half toHalf(double);
+    method @RequiresApi(26) public static inline android.util.Half toHalf(float);
+    method @RequiresApi(26) public static inline android.util.Half toHalf(String);
+    method @RequiresApi(26) public static inline android.util.Half toHalf(@HalfFloat short);
+  }
+
+  public final class LongSparseArrayKt {
+    method @RequiresApi(16) public static inline operator <T> boolean contains(android.util.LongSparseArray<T>, long key);
+    method @RequiresApi(16) public static inline <T> boolean containsKey(android.util.LongSparseArray<T>, long key);
+    method @RequiresApi(16) public static inline <T> boolean containsValue(android.util.LongSparseArray<T>, T value);
+    method @RequiresApi(16) public static inline <T> void forEach(android.util.LongSparseArray<T>, kotlin.jvm.functions.Function2<? super java.lang.Long,? super T,kotlin.Unit> action);
+    method @RequiresApi(16) public static inline <T> T getOrDefault(android.util.LongSparseArray<T>, long key, T defaultValue);
+    method @RequiresApi(16) public static inline <T> T getOrElse(android.util.LongSparseArray<T>, long key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+    method @RequiresApi(16) public static inline <T> int getSize(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static inline <T> boolean isEmpty(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static inline <T> boolean isNotEmpty(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static <T> kotlin.collections.LongIterator keyIterator(android.util.LongSparseArray<T>);
+    method @RequiresApi(16) public static operator <T> android.util.LongSparseArray<T> plus(android.util.LongSparseArray<T>, android.util.LongSparseArray<T> other);
+    method @RequiresApi(16) public static <T> void putAll(android.util.LongSparseArray<T>, android.util.LongSparseArray<T> other);
+    method @RequiresApi(16) public static <T> boolean remove(android.util.LongSparseArray<T>, long key, T value);
+    method @RequiresApi(16) public static inline operator <T> void set(android.util.LongSparseArray<T>, long key, T value);
+    method @RequiresApi(16) public static <T> java.util.Iterator<T> valueIterator(android.util.LongSparseArray<T>);
+  }
+
+  public final class LruCacheKt {
+    method public static inline <K, V> android.util.LruCache<K,V> lruCache(int maxSize, optional kotlin.jvm.functions.Function2<? super K,? super V,java.lang.Integer> sizeOf, optional kotlin.jvm.functions.Function1<? super K,? extends V> create, optional kotlin.jvm.functions.Function4<? super java.lang.Boolean,? super K,? super V,? super V,kotlin.Unit> onEntryRemoved);
+  }
+
+  public final class PairKt {
+    method public static inline operator <F, S> F component1(android.util.Pair<F,S>);
+    method public static inline operator <F, S> F component1(androidx.core.util.Pair<F,S>);
+    method public static inline operator <F, S> S component2(android.util.Pair<F,S>);
+    method public static inline operator <F, S> S component2(androidx.core.util.Pair<F,S>);
+    method public static inline <F, S> android.util.Pair<F,S> toAndroidPair(kotlin.Pair<? extends F,? extends S>);
+    method public static inline <F, S> androidx.core.util.Pair<F,S> toAndroidXPair(kotlin.Pair<? extends F,? extends S>);
+    method public static inline <F, S> kotlin.Pair<F,S> toKotlinPair(android.util.Pair<F,S>);
+    method public static inline <F, S> kotlin.Pair<F,S> toKotlinPair(androidx.core.util.Pair<F,S>);
+  }
+
+  public final class RangeKt {
+    method @RequiresApi(21) public static inline infix <T extends java.lang.Comparable<? super T>> android.util.Range<T> and(android.util.Range<T>, android.util.Range<T> other);
+    method @RequiresApi(21) public static inline operator <T extends java.lang.Comparable<? super T>> android.util.Range<T> plus(android.util.Range<T>, android.util.Range<T> other);
+    method @RequiresApi(21) public static inline operator <T extends java.lang.Comparable<? super T>> android.util.Range<T> plus(android.util.Range<T>, T value);
+    method @RequiresApi(21) public static inline infix <T extends java.lang.Comparable<? super T>> android.util.Range<T> rangeTo(T, T that);
+    method @RequiresApi(21) public static <T extends java.lang.Comparable<? super T>> kotlin.ranges.ClosedRange<T> toClosedRange(android.util.Range<T>);
+    method @RequiresApi(21) public static <T extends java.lang.Comparable<? super T>> android.util.Range<T> toRange(kotlin.ranges.ClosedRange<T>);
+  }
+
+  public final class RunnableKt {
+    method public static Runnable asRunnable(kotlin.coroutines.Continuation<? super kotlin.Unit>);
+  }
+
+  public final class SizeKt {
+    method @RequiresApi(21) public static inline operator int component1(android.util.Size);
+    method @RequiresApi(21) public static inline operator float component1(android.util.SizeF);
+    method public static inline operator float component1(androidx.core.util.SizeFCompat);
+    method @RequiresApi(21) public static inline operator int component2(android.util.Size);
+    method @RequiresApi(21) public static inline operator float component2(android.util.SizeF);
+    method public static inline operator float component2(androidx.core.util.SizeFCompat);
+  }
+
+  public final class SparseArrayKt {
+    method public static inline operator <T> boolean contains(android.util.SparseArray<T>, int key);
+    method public static inline <T> boolean containsKey(android.util.SparseArray<T>, int key);
+    method public static inline <T> boolean containsValue(android.util.SparseArray<T>, T value);
+    method public static inline <T> void forEach(android.util.SparseArray<T>, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super T,kotlin.Unit> action);
+    method public static inline <T> T getOrDefault(android.util.SparseArray<T>, int key, T defaultValue);
+    method public static inline <T> T getOrElse(android.util.SparseArray<T>, int key, kotlin.jvm.functions.Function0<? extends T> defaultValue);
+    method public static inline <T> int getSize(android.util.SparseArray<T>);
+    method public static inline <T> boolean isEmpty(android.util.SparseArray<T>);
+    method public static inline <T> boolean isNotEmpty(android.util.SparseArray<T>);
+    method public static <T> kotlin.collections.IntIterator keyIterator(android.util.SparseArray<T>);
+    method public static operator <T> android.util.SparseArray<T> plus(android.util.SparseArray<T>, android.util.SparseArray<T> other);
+    method public static <T> void putAll(android.util.SparseArray<T>, android.util.SparseArray<T> other);
+    method public static <T> boolean remove(android.util.SparseArray<T>, int key, T value);
+    method public static inline operator <T> void set(android.util.SparseArray<T>, int key, T value);
+    method public static <T> java.util.Iterator<T> valueIterator(android.util.SparseArray<T>);
+  }
+
+  public final class SparseBooleanArrayKt {
+    method public static inline operator boolean contains(android.util.SparseBooleanArray, int key);
+    method public static inline boolean containsKey(android.util.SparseBooleanArray, int key);
+    method public static inline boolean containsValue(android.util.SparseBooleanArray, boolean value);
+    method public static inline void forEach(android.util.SparseBooleanArray, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Boolean,kotlin.Unit> action);
+    method public static inline boolean getOrDefault(android.util.SparseBooleanArray, int key, boolean defaultValue);
+    method public static inline boolean getOrElse(android.util.SparseBooleanArray, int key, kotlin.jvm.functions.Function0<java.lang.Boolean> defaultValue);
+    method public static inline int getSize(android.util.SparseBooleanArray);
+    method public static inline boolean isEmpty(android.util.SparseBooleanArray);
+    method public static inline boolean isNotEmpty(android.util.SparseBooleanArray);
+    method public static kotlin.collections.IntIterator keyIterator(android.util.SparseBooleanArray);
+    method public static operator android.util.SparseBooleanArray plus(android.util.SparseBooleanArray, android.util.SparseBooleanArray other);
+    method public static void putAll(android.util.SparseBooleanArray, android.util.SparseBooleanArray other);
+    method public static boolean remove(android.util.SparseBooleanArray, int key, boolean value);
+    method public static inline operator void set(android.util.SparseBooleanArray, int key, boolean value);
+    method public static kotlin.collections.BooleanIterator valueIterator(android.util.SparseBooleanArray);
+  }
+
+  public final class SparseIntArrayKt {
+    method public static inline operator boolean contains(android.util.SparseIntArray, int key);
+    method public static inline boolean containsKey(android.util.SparseIntArray, int key);
+    method public static inline boolean containsValue(android.util.SparseIntArray, int value);
+    method public static inline void forEach(android.util.SparseIntArray, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> action);
+    method public static inline int getOrDefault(android.util.SparseIntArray, int key, int defaultValue);
+    method public static inline int getOrElse(android.util.SparseIntArray, int key, kotlin.jvm.functions.Function0<java.lang.Integer> defaultValue);
+    method public static inline int getSize(android.util.SparseIntArray);
+    method public static inline boolean isEmpty(android.util.SparseIntArray);
+    method public static inline boolean isNotEmpty(android.util.SparseIntArray);
+    method public static kotlin.collections.IntIterator keyIterator(android.util.SparseIntArray);
+    method public static operator android.util.SparseIntArray plus(android.util.SparseIntArray, android.util.SparseIntArray other);
+    method public static void putAll(android.util.SparseIntArray, android.util.SparseIntArray other);
+    method public static boolean remove(android.util.SparseIntArray, int key, int value);
+    method public static inline operator void set(android.util.SparseIntArray, int key, int value);
+    method public static kotlin.collections.IntIterator valueIterator(android.util.SparseIntArray);
+  }
+
+  public final class SparseLongArrayKt {
+    method @RequiresApi(18) public static inline operator boolean contains(android.util.SparseLongArray, int key);
+    method @RequiresApi(18) public static inline boolean containsKey(android.util.SparseLongArray, int key);
+    method @RequiresApi(18) public static inline boolean containsValue(android.util.SparseLongArray, long value);
+    method @RequiresApi(18) public static inline void forEach(android.util.SparseLongArray, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Long,kotlin.Unit> action);
+    method @RequiresApi(18) public static inline long getOrDefault(android.util.SparseLongArray, int key, long defaultValue);
+    method @RequiresApi(18) public static inline long getOrElse(android.util.SparseLongArray, int key, kotlin.jvm.functions.Function0<java.lang.Long> defaultValue);
+    method @RequiresApi(18) public static inline int getSize(android.util.SparseLongArray);
+    method @RequiresApi(18) public static inline boolean isEmpty(android.util.SparseLongArray);
+    method @RequiresApi(18) public static inline boolean isNotEmpty(android.util.SparseLongArray);
+    method @RequiresApi(18) public static kotlin.collections.IntIterator keyIterator(android.util.SparseLongArray);
+    method @RequiresApi(18) public static operator android.util.SparseLongArray plus(android.util.SparseLongArray, android.util.SparseLongArray other);
+    method @RequiresApi(18) public static void putAll(android.util.SparseLongArray, android.util.SparseLongArray other);
+    method @RequiresApi(18) public static boolean remove(android.util.SparseLongArray, int key, long value);
+    method @RequiresApi(18) public static inline operator void set(android.util.SparseLongArray, int key, long value);
+    method @RequiresApi(18) public static kotlin.collections.LongIterator valueIterator(android.util.SparseLongArray);
+  }
+
+}
+
+package androidx.core.view {
+
+  public final class MenuKt {
+    method public static operator boolean contains(android.view.Menu, android.view.MenuItem item);
+    method public static inline void forEach(android.view.Menu, kotlin.jvm.functions.Function1<? super android.view.MenuItem,kotlin.Unit> action);
+    method public static inline void forEachIndexed(android.view.Menu, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super android.view.MenuItem,kotlin.Unit> action);
+    method public static inline operator android.view.MenuItem get(android.view.Menu, int index);
+    method public static kotlin.sequences.Sequence<android.view.MenuItem> getChildren(android.view.Menu);
+    method public static inline int getSize(android.view.Menu);
+    method public static inline boolean isEmpty(android.view.Menu);
+    method public static inline boolean isNotEmpty(android.view.Menu);
+    method public static operator java.util.Iterator<android.view.MenuItem> iterator(android.view.Menu);
+    method public static inline operator void minusAssign(android.view.Menu, android.view.MenuItem item);
+    method public static inline void removeItemAt(android.view.Menu, int index);
+  }
+
+  public final class ViewGroupKt {
+    method public static inline operator boolean contains(android.view.ViewGroup, android.view.View view);
+    method public static inline void forEach(android.view.ViewGroup, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static inline void forEachIndexed(android.view.ViewGroup, kotlin.jvm.functions.Function2<? super java.lang.Integer,? super android.view.View,kotlin.Unit> action);
+    method public static operator android.view.View get(android.view.ViewGroup, int index);
+    method public static kotlin.sequences.Sequence<android.view.View> getChildren(android.view.ViewGroup);
+    method public static kotlin.sequences.Sequence<android.view.View> getDescendants(android.view.ViewGroup);
+    method public static inline kotlin.ranges.IntRange getIndices(android.view.ViewGroup);
+    method public static inline int getSize(android.view.ViewGroup);
+    method public static inline boolean isEmpty(android.view.ViewGroup);
+    method public static inline boolean isNotEmpty(android.view.ViewGroup);
+    method public static operator java.util.Iterator<android.view.View> iterator(android.view.ViewGroup);
+    method public static inline operator void minusAssign(android.view.ViewGroup, android.view.View view);
+    method public static inline operator void plusAssign(android.view.ViewGroup, android.view.View view);
+    method public static inline void setMargins(android.view.ViewGroup.MarginLayoutParams, @Px int size);
+    method public static inline void updateMargins(android.view.ViewGroup.MarginLayoutParams, optional @Px int left, optional @Px int top, optional @Px int right, optional @Px int bottom);
+    method @RequiresApi(17) public static inline void updateMarginsRelative(android.view.ViewGroup.MarginLayoutParams, optional @Px int start, optional @Px int top, optional @Px int end, optional @Px int bottom);
+  }
+
+  public final class ViewKt {
+    method public static inline void doOnAttach(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static inline void doOnDetach(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static inline void doOnLayout(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static inline void doOnNextLayout(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static inline androidx.core.view.OneShotPreDrawListener doOnPreDraw(android.view.View, kotlin.jvm.functions.Function1<? super android.view.View,kotlin.Unit> action);
+    method public static android.graphics.Bitmap drawToBitmap(android.view.View, optional android.graphics.Bitmap.Config config);
+    method public static kotlin.sequences.Sequence<android.view.View> getAllViews(android.view.View);
+    method public static kotlin.sequences.Sequence<android.view.ViewParent> getAncestors(android.view.View);
+    method public static inline int getMarginBottom(android.view.View);
+    method public static inline int getMarginEnd(android.view.View);
+    method public static inline int getMarginLeft(android.view.View);
+    method public static inline int getMarginRight(android.view.View);
+    method public static inline int getMarginStart(android.view.View);
+    method public static inline int getMarginTop(android.view.View);
+    method public static inline boolean isGone(android.view.View);
+    method public static inline boolean isInvisible(android.view.View);
+    method public static inline boolean isVisible(android.view.View);
+    method public static inline Runnable postDelayed(android.view.View, long delayInMillis, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method @RequiresApi(16) public static Runnable postOnAnimationDelayed(android.view.View, long delayInMillis, kotlin.jvm.functions.Function0<kotlin.Unit> action);
+    method public static inline void setGone(android.view.View, boolean);
+    method public static inline void setInvisible(android.view.View, boolean);
+    method public static inline void setPadding(android.view.View, @Px int size);
+    method public static inline void setVisible(android.view.View, boolean);
+    method public static inline void updateLayoutParams(android.view.View, kotlin.jvm.functions.Function1<? super android.view.ViewGroup.LayoutParams,kotlin.Unit> block);
+    method public static inline <reified T extends android.view.ViewGroup.LayoutParams> void updateLayoutParamsTyped(android.view.View, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> block);
+    method public static inline void updatePadding(android.view.View, optional @Px int left, optional @Px int top, optional @Px int right, optional @Px int bottom);
+    method @RequiresApi(17) public static inline void updatePaddingRelative(android.view.View, optional @Px int start, optional @Px int top, optional @Px int end, optional @Px int bottom);
+  }
+
+}
+
+package androidx.core.widget {
+
+  public final class TextViewKt {
+    method public static inline android.text.TextWatcher addTextChangedListener(android.widget.TextView, optional kotlin.jvm.functions.Function4<? super java.lang.CharSequence,? super java.lang.Integer,? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> beforeTextChanged, optional kotlin.jvm.functions.Function4<? super java.lang.CharSequence,? super java.lang.Integer,? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> onTextChanged, optional kotlin.jvm.functions.Function1<? super android.text.Editable,kotlin.Unit> afterTextChanged);
+    method public static inline android.text.TextWatcher doAfterTextChanged(android.widget.TextView, kotlin.jvm.functions.Function1<? super android.text.Editable,kotlin.Unit> action);
+    method public static inline android.text.TextWatcher doBeforeTextChanged(android.widget.TextView, kotlin.jvm.functions.Function4<? super java.lang.CharSequence,? super java.lang.Integer,? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> action);
+    method public static inline android.text.TextWatcher doOnTextChanged(android.widget.TextView, kotlin.jvm.functions.Function4<? super java.lang.CharSequence,? super java.lang.Integer,? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> action);
+  }
+
+}
+
diff --git a/core/core-performance-play-services/api/current.txt b/core/core-performance-play-services/api/current.txt
index 85c5ff4..0c72140 100644
--- a/core/core-performance-play-services/api/current.txt
+++ b/core/core-performance-play-services/api/current.txt
@@ -1,6 +1,19 @@
 // Signature format: 4.0
 package androidx.core.performance.play.services {
 
+  public final class PlayServicesDevicePerformanceRetriever implements androidx.core.performance.DevicePerformanceRetriever {
+    ctor public PlayServicesDevicePerformanceRetriever(android.content.Context context);
+    method public android.content.Context getContext();
+    method public int getPerformanceClass();
+    method public static int getPerformanceClass(android.content.Context context);
+    property public final android.content.Context context;
+    field public static final androidx.core.performance.play.services.PlayServicesDevicePerformanceRetriever.Companion Companion;
+  }
+
+  public static final class PlayServicesDevicePerformanceRetriever.Companion {
+    method public int getPerformanceClass(android.content.Context context);
+  }
+
   public final class PlayServicesDevicePerformanceSupplier implements androidx.core.performance.DevicePerformanceSupplier {
     ctor public PlayServicesDevicePerformanceSupplier();
     method public static androidx.core.performance.DevicePerformance createDevicePerformance(android.content.Context context);
diff --git a/core/core-performance-play-services/api/restricted_current.txt b/core/core-performance-play-services/api/restricted_current.txt
index 85c5ff4..0c72140 100644
--- a/core/core-performance-play-services/api/restricted_current.txt
+++ b/core/core-performance-play-services/api/restricted_current.txt
@@ -1,6 +1,19 @@
 // Signature format: 4.0
 package androidx.core.performance.play.services {
 
+  public final class PlayServicesDevicePerformanceRetriever implements androidx.core.performance.DevicePerformanceRetriever {
+    ctor public PlayServicesDevicePerformanceRetriever(android.content.Context context);
+    method public android.content.Context getContext();
+    method public int getPerformanceClass();
+    method public static int getPerformanceClass(android.content.Context context);
+    property public final android.content.Context context;
+    field public static final androidx.core.performance.play.services.PlayServicesDevicePerformanceRetriever.Companion Companion;
+  }
+
+  public static final class PlayServicesDevicePerformanceRetriever.Companion {
+    method public int getPerformanceClass(android.content.Context context);
+  }
+
   public final class PlayServicesDevicePerformanceSupplier implements androidx.core.performance.DevicePerformanceSupplier {
     ctor public PlayServicesDevicePerformanceSupplier();
     method public static androidx.core.performance.DevicePerformance createDevicePerformance(android.content.Context context);
diff --git a/core/core-performance-play-services/build.gradle b/core/core-performance-play-services/build.gradle
index f6643d6..42cab7be 100644
--- a/core/core-performance-play-services/build.gradle
+++ b/core/core-performance-play-services/build.gradle
@@ -25,7 +25,12 @@
 dependencies {
     api(libs.kotlinStdlib)
 
+    implementation(libs.playServicesDevicePerformance)
+
+    // Coroutines
     implementation(libs.kotlinCoroutinesCore)
+    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.1.1")
+
     implementation(project(":core:core-performance"))
 
     testImplementation(libs.testCore)
@@ -37,6 +42,9 @@
 }
 
 android {
+    defaultConfig {
+        minSdkVersion 24
+    }
     namespace "androidx.core.performance.play.services"
 }
 
diff --git a/core/core-performance-play-services/src/main/java/androidx/core/performance/play/services/PlayServicesDevicePerformanceRetriever.kt b/core/core-performance-play-services/src/main/java/androidx/core/performance/play/services/PlayServicesDevicePerformanceRetriever.kt
new file mode 100644
index 0000000..ea1ea30
--- /dev/null
+++ b/core/core-performance-play-services/src/main/java/androidx/core/performance/play/services/PlayServicesDevicePerformanceRetriever.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.performance.play.services
+
+import android.content.Context
+import androidx.core.performance.DevicePerformanceRetriever
+import com.google.android.gms.deviceperformance.DevicePerformanceClient
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.tasks.await
+
+/**
+ * A DevicePerformanceRetriever that uses Google Play Services to retrieve media performance class data.
+ *
+ * @param context The application context value to use.
+ */
+class PlayServicesDevicePerformanceRetriever(val context: Context) : DevicePerformanceRetriever {
+        override fun getPerformanceClass(): Int =
+            PlayServicesDevicePerformanceRetriever.getPerformanceClass(
+                context
+            );
+
+        companion object {
+            @JvmStatic
+            fun getPerformanceClass(context: Context): Int {
+                val client: DevicePerformanceClient =
+                    com.google.android.gms.deviceperformance.DevicePerformance.getClient(context)
+                return runBlocking { client.mediaPerformanceClass().await() }
+            }
+        }
+}
diff --git a/core/core-performance-testing/api/current.txt b/core/core-performance-testing/api/current.txt
index f184012..73eaa82 100644
--- a/core/core-performance-testing/api/current.txt
+++ b/core/core-performance-testing/api/current.txt
@@ -1,6 +1,13 @@
 // Signature format: 4.0
 package androidx.core.performance.testing {
 
+  public final class FakeDevicePerformanceRetriever implements androidx.core.performance.DevicePerformanceRetriever {
+    ctor public FakeDevicePerformanceRetriever(int mediaPerformanceClass);
+    method public int getMediaPerformanceClass();
+    method public int getPerformanceClass();
+    property public final int mediaPerformanceClass;
+  }
+
   public final class FakeDevicePerformanceSupplier implements androidx.core.performance.DevicePerformanceSupplier {
     ctor public FakeDevicePerformanceSupplier(int mediaPerformanceClass);
     method public kotlinx.coroutines.flow.Flow<java.lang.Integer> getMediaPerformanceClassFlow();
diff --git a/core/core-performance-testing/api/restricted_current.txt b/core/core-performance-testing/api/restricted_current.txt
index f184012..73eaa82 100644
--- a/core/core-performance-testing/api/restricted_current.txt
+++ b/core/core-performance-testing/api/restricted_current.txt
@@ -1,6 +1,13 @@
 // Signature format: 4.0
 package androidx.core.performance.testing {
 
+  public final class FakeDevicePerformanceRetriever implements androidx.core.performance.DevicePerformanceRetriever {
+    ctor public FakeDevicePerformanceRetriever(int mediaPerformanceClass);
+    method public int getMediaPerformanceClass();
+    method public int getPerformanceClass();
+    property public final int mediaPerformanceClass;
+  }
+
   public final class FakeDevicePerformanceSupplier implements androidx.core.performance.DevicePerformanceSupplier {
     ctor public FakeDevicePerformanceSupplier(int mediaPerformanceClass);
     method public kotlinx.coroutines.flow.Flow<java.lang.Integer> getMediaPerformanceClassFlow();
diff --git a/core/core-performance-testing/build.gradle b/core/core-performance-testing/build.gradle
index 2109a27..39b4dd1 100644
--- a/core/core-performance-testing/build.gradle
+++ b/core/core-performance-testing/build.gradle
@@ -36,6 +36,9 @@
 }
 
 android {
+    defaultConfig {
+        minSdkVersion 24
+    }
     namespace "androidx.core.performance.testing"
 }
 
diff --git a/core/core-performance-testing/src/main/java/androidx/core/performance/testing/FakeDevicePerformanceRetriever.kt b/core/core-performance-testing/src/main/java/androidx/core/performance/testing/FakeDevicePerformanceRetriever.kt
new file mode 100644
index 0000000..284a229
--- /dev/null
+++ b/core/core-performance-testing/src/main/java/androidx/core/performance/testing/FakeDevicePerformanceRetriever.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.performance.testing
+
+import androidx.core.performance.DevicePerformanceRetriever
+
+/**
+ * A DevicePerformanceRetriever that immediately returns the `mediaPerformanceClass`.
+ *
+ * @param mediaPerformanceClass The media performance class value to return.
+ */
+class FakeDevicePerformanceRetriever(val mediaPerformanceClass: Int) : DevicePerformanceRetriever {
+    override fun getPerformanceClass(): Int = mediaPerformanceClass
+}
diff --git a/core/core-performance-testing/src/test/java/androidx/core/performance/testing/FakeDevicePerformanceRetrieverTest.kt b/core/core-performance-testing/src/test/java/androidx/core/performance/testing/FakeDevicePerformanceRetrieverTest.kt
new file mode 100644
index 0000000..b45d2d7
--- /dev/null
+++ b/core/core-performance-testing/src/test/java/androidx/core/performance/testing/FakeDevicePerformanceRetrieverTest.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.performance.testing
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+/** Unit test for [FakeDevicePerformanceRetriever]. */
+class FakeDevicePerformanceRetrieverTest {
+
+    @Test
+    fun mediaPerformanceClass_30() {
+        val retriever = FakeDevicePerformanceRetriever(30)
+        val mpc = retriever.getPerformanceClass()
+        assertThat(mpc).isEqualTo(30)
+    }
+}
diff --git a/core/core-performance/api/current.txt b/core/core-performance/api/current.txt
index 0861435..c85cbcb 100644
--- a/core/core-performance/api/current.txt
+++ b/core/core-performance/api/current.txt
@@ -1,6 +1,11 @@
 // Signature format: 4.0
 package androidx.core.performance {
 
+  public final class DefaultDevicePerformanceRetriever implements androidx.core.performance.DevicePerformanceRetriever {
+    ctor public DefaultDevicePerformanceRetriever();
+    method public int getPerformanceClass();
+  }
+
   @kotlin.jvm.JvmDefaultWithCompatibility public interface DevicePerformance {
     method public static androidx.core.performance.DevicePerformance create(androidx.core.performance.DevicePerformanceSupplier devicePerformanceSupplier);
     method public int getMediaPerformanceClass();
@@ -12,11 +17,24 @@
     method public androidx.core.performance.DevicePerformance create(androidx.core.performance.DevicePerformanceSupplier devicePerformanceSupplier);
   }
 
+  public interface DevicePerformanceRetriever {
+    method public int getPerformanceClass();
+  }
+
   public interface DevicePerformanceSupplier {
     method public kotlinx.coroutines.flow.Flow<java.lang.Integer> getMediaPerformanceClassFlow();
     property public abstract kotlinx.coroutines.flow.Flow<java.lang.Integer> mediaPerformanceClassFlow;
   }
 
+  public interface MediaPerformance {
+    method public static int getPerformanceClass(optional androidx.core.performance.DevicePerformanceRetriever retriever);
+    field public static final androidx.core.performance.MediaPerformance.Companion Companion;
+  }
+
+  public static final class MediaPerformance.Companion {
+    method public int getPerformanceClass(optional androidx.core.performance.DevicePerformanceRetriever retriever);
+  }
+
   public final class StaticDevicePerformanceSupplier implements androidx.core.performance.DevicePerformanceSupplier {
     ctor public StaticDevicePerformanceSupplier();
     method public static androidx.core.performance.DevicePerformance createDevicePerformance();
diff --git a/core/core-performance/api/restricted_current.txt b/core/core-performance/api/restricted_current.txt
index 0861435..c85cbcb 100644
--- a/core/core-performance/api/restricted_current.txt
+++ b/core/core-performance/api/restricted_current.txt
@@ -1,6 +1,11 @@
 // Signature format: 4.0
 package androidx.core.performance {
 
+  public final class DefaultDevicePerformanceRetriever implements androidx.core.performance.DevicePerformanceRetriever {
+    ctor public DefaultDevicePerformanceRetriever();
+    method public int getPerformanceClass();
+  }
+
   @kotlin.jvm.JvmDefaultWithCompatibility public interface DevicePerformance {
     method public static androidx.core.performance.DevicePerformance create(androidx.core.performance.DevicePerformanceSupplier devicePerformanceSupplier);
     method public int getMediaPerformanceClass();
@@ -12,11 +17,24 @@
     method public androidx.core.performance.DevicePerformance create(androidx.core.performance.DevicePerformanceSupplier devicePerformanceSupplier);
   }
 
+  public interface DevicePerformanceRetriever {
+    method public int getPerformanceClass();
+  }
+
   public interface DevicePerformanceSupplier {
     method public kotlinx.coroutines.flow.Flow<java.lang.Integer> getMediaPerformanceClassFlow();
     property public abstract kotlinx.coroutines.flow.Flow<java.lang.Integer> mediaPerformanceClassFlow;
   }
 
+  public interface MediaPerformance {
+    method public static int getPerformanceClass(optional androidx.core.performance.DevicePerformanceRetriever retriever);
+    field public static final androidx.core.performance.MediaPerformance.Companion Companion;
+  }
+
+  public static final class MediaPerformance.Companion {
+    method public int getPerformanceClass(optional androidx.core.performance.DevicePerformanceRetriever retriever);
+  }
+
   public final class StaticDevicePerformanceSupplier implements androidx.core.performance.DevicePerformanceSupplier {
     ctor public StaticDevicePerformanceSupplier();
     method public static androidx.core.performance.DevicePerformance createDevicePerformance();
diff --git a/core/core-performance/build.gradle b/core/core-performance/build.gradle
index 044a5ad..3dd2a66 100644
--- a/core/core-performance/build.gradle
+++ b/core/core-performance/build.gradle
@@ -47,5 +47,8 @@
 }
 
 android {
+    defaultConfig {
+        minSdkVersion 24
+    }
     namespace "androidx.core.performance"
 }
diff --git a/core/core-performance/samples/build.gradle b/core/core-performance/samples/build.gradle
index cbd177a..0087990 100644
--- a/core/core-performance/samples/build.gradle
+++ b/core/core-performance/samples/build.gradle
@@ -38,5 +38,8 @@
 }
 
 android {
+    defaultConfig {
+        minSdkVersion 24
+    }
     namespace "androidx.core.performance.samples"
 }
diff --git a/core/core-performance/src/main/java/androidx/core/performance/MediaPerformance.kt b/core/core-performance/src/main/java/androidx/core/performance/MediaPerformance.kt
new file mode 100644
index 0000000..35282c9
--- /dev/null
+++ b/core/core-performance/src/main/java/androidx/core/performance/MediaPerformance.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.performance
+
+/**
+ * Interface implemented by all device performance retrievers.
+ */
+interface DevicePerformanceRetriever {
+    fun getPerformanceClass(): Int
+}
+
+/**
+ * Default implementation for a device performance retriever.
+ */
+class DefaultDevicePerformanceRetriever : DevicePerformanceRetriever {
+    override fun getPerformanceClass(): Int = 0
+}
+
+/**
+ * Main interface used to get the performance class using a specific retriever.
+ */
+interface MediaPerformance {
+    companion object {
+        @JvmStatic
+        fun getPerformanceClass(
+            retriever: DevicePerformanceRetriever =
+                DefaultDevicePerformanceRetriever()
+        ): Int = retriever.getPerformanceClass()
+    }
+}
diff --git a/core/core-performance/src/test/java/androidx/core/performance/MediaPerformanceTest.kt b/core/core-performance/src/test/java/androidx/core/performance/MediaPerformanceTest.kt
new file mode 100644
index 0000000..2f30c43
--- /dev/null
+++ b/core/core-performance/src/test/java/androidx/core/performance/MediaPerformanceTest.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.core.performance
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+/** Unit tests for [MediaPerformance]. */
+class MediaPerformanceTest {
+
+    @Test
+    fun mediaPerformanceClassWithUnspecifiedRetriever() {
+        val mpc = MediaPerformance.getPerformanceClass()
+        assertThat(mpc).isEqualTo(0)
+    }
+
+    @Test
+    fun mediaPerformanceClassWithSpecifiedDefaultRetriever() {
+        val mpc = MediaPerformance.getPerformanceClass(
+            DefaultDevicePerformanceRetriever()
+        )
+        assertThat(mpc).isEqualTo(0)
+    }
+}
diff --git a/core/core-telecom/api/current.txt b/core/core-telecom/api/current.txt
index aa73740..28e309e 100644
--- a/core/core-telecom/api/current.txt
+++ b/core/core-telecom/api/current.txt
@@ -33,7 +33,7 @@
     method public suspend Object? onSetInactive(kotlin.coroutines.Continuation<? super java.lang.Boolean>);
   }
 
-  public interface CallControlScope {
+  public interface CallControlScope extends kotlinx.coroutines.CoroutineScope {
     method public suspend Object? answer(int callType, kotlin.coroutines.Continuation<? super java.lang.Boolean>);
     method public suspend Object? disconnect(android.telecom.DisconnectCause disconnectCause, kotlin.coroutines.Continuation<? super java.lang.Boolean>);
     method public kotlinx.coroutines.flow.Flow<java.util.List<androidx.core.telecom.CallEndpointCompat>> getAvailableEndpoints();
diff --git a/core/core-telecom/api/restricted_current.txt b/core/core-telecom/api/restricted_current.txt
index 8c6ce9f..c8a7138 100644
--- a/core/core-telecom/api/restricted_current.txt
+++ b/core/core-telecom/api/restricted_current.txt
@@ -42,7 +42,7 @@
     method public suspend Object? onSetInactive(kotlin.coroutines.Continuation<? super java.lang.Boolean>);
   }
 
-  public interface CallControlScope {
+  public interface CallControlScope extends kotlinx.coroutines.CoroutineScope {
     method public suspend Object? answer(@androidx.core.telecom.CallAttributesCompat.Companion.CallType int callType, kotlin.coroutines.Continuation<? super java.lang.Boolean>);
     method public suspend Object? disconnect(android.telecom.DisconnectCause disconnectCause, kotlin.coroutines.Continuation<? super java.lang.Boolean>);
     method public kotlinx.coroutines.flow.Flow<java.util.List<androidx.core.telecom.CallEndpointCompat>> getAvailableEndpoints();
diff --git a/core/core-telecom/integration-tests/testapp/src/main/java/androidx/core/telecom/test/CallingMainActivity.kt b/core/core-telecom/integration-tests/testapp/src/main/java/androidx/core/telecom/test/CallingMainActivity.kt
index f1cebe4..986476e 100644
--- a/core/core-telecom/integration-tests/testapp/src/main/java/androidx/core/telecom/test/CallingMainActivity.kt
+++ b/core/core-telecom/integration-tests/testapp/src/main/java/androidx/core/telecom/test/CallingMainActivity.kt
@@ -32,8 +32,6 @@
 import kotlinx.coroutines.CancellationException
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.launch
 
 @RequiresApi(34)
@@ -120,7 +118,6 @@
         val callObject = VoipCall()
 
         CoroutineScope(Dispatchers.IO).launch {
-            val coroutineScope = this
             try {
                 mCallsManager!!.addCall(attributes) {
                     // set the client callback implementation
@@ -131,17 +128,23 @@
                     callObject.setCallControl(this)
 
                     // Collect updates
-                    currentCallEndpoint
-                        .onEach { callObject.onCallEndpointChanged(it) }
-                        .launchIn(coroutineScope)
+                    launch {
+                        currentCallEndpoint.collect {
+                            callObject.onCallEndpointChanged(it)
+                        }
+                    }
 
-                    availableEndpoints
-                        .onEach { callObject.onAvailableCallEndpointsChanged(it) }
-                        .launchIn(coroutineScope)
+                    launch {
+                        availableEndpoints.collect {
+                            callObject.onAvailableCallEndpointsChanged(it)
+                        }
+                    }
 
-                    isMuted
-                        .onEach { callObject.onMuteStateChanged(it) }
-                        .launchIn(coroutineScope)
+                    launch {
+                        isMuted.collect {
+                            callObject.onMuteStateChanged(it)
+                        }
+                    }
                 }
                 addCallRow(callObject)
             } catch (e: CancellationException) {
diff --git a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/InCallAudioTest.kt b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/InCallAudioTest.kt
index 453ec4d..280fbdb 100644
--- a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/InCallAudioTest.kt
+++ b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/InCallAudioTest.kt
@@ -19,10 +19,12 @@
 import android.media.AudioManager.MODE_IN_COMMUNICATION
 import android.os.Build
 import android.telecom.DisconnectCause
+import android.util.Log
 import androidx.annotation.RequiresApi
 import androidx.core.telecom.internal.utils.Utils
 import androidx.core.telecom.test.utils.BaseTelecomTest
 import androidx.core.telecom.test.utils.TestUtils
+import androidx.core.telecom.test.utils.TestUtils.getAudioModeName
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.filters.SdkSuppress
@@ -55,6 +57,7 @@
 @RequiresApi(Build.VERSION_CODES.O)
 @RunWith(AndroidJUnit4::class)
 class InCallAudioTest : BaseTelecomTest() {
+    val LOG_TAG = "InCallAudioTest"
     @Before
     fun setUp() {
         Utils.resetUtils()
@@ -117,8 +120,12 @@
             val deferred = CompletableDeferred<Unit>()
             assertWithinTimeout_addCall(deferred, TestUtils.OUTGOING_CALL_ATTRIBUTES) {
                 launch {
+                    Log.i(LOG_TAG, "runBlocking_addCall_assertAudioModeInCommunication: " +
+                        "initial AudioManager mode = ${getAudioModeName(mAudioManager.mode)}")
                     while (isActive /* aka  within timeout window */ &&
                         mAudioManager.mode != MODE_IN_COMMUNICATION) {
+                        Log.d(LOG_TAG, "runBlocking_addCall_assertAudioModeInCommunication: " +
+                            "current AudioManager mode = ${getAudioModeName(mAudioManager.mode)}")
                         yield() // mechanism to stop the while loop if the coroutine is dead
                         delay(1) // sleep x millisecond(s) instead of spamming check
                     }
diff --git a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/utils/BaseTelecomTest.kt b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/utils/BaseTelecomTest.kt
index e758e7b..20abe0a 100644
--- a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/utils/BaseTelecomTest.kt
+++ b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/utils/BaseTelecomTest.kt
@@ -20,6 +20,7 @@
 import android.content.pm.PackageManager
 import android.media.AudioManager
 import android.os.Build
+import android.telecom.DisconnectCause
 import android.telecom.PhoneAccountHandle
 import android.telecom.TelecomManager
 import android.util.Log
@@ -113,7 +114,11 @@
 
     private fun maybeCleanupStuckCalls() {
         JetpackConnectionService.mPendingConnectionRequests.clear()
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+            ManagedConnectionService.mPendingConnectionRequests.clear()
+        }
         MockInCallService.destroyAllCalls()
+        TestUtils.runShellCommand(TestUtils.COMMAND_CLEANUP_STUCK_CALLS)
     }
 
     private fun isInCallFromTelDumpsys(telecomDumpsysString: String): Pair<Boolean, String> {
@@ -143,11 +148,16 @@
         setCallback: Boolean = true,
         assertBlock: CallControlScope.() -> (Unit)
     ) {
+        Log.i(TestUtils.LOG_TAG, "assertWithinTimeout_addCall")
+        var callControlScope: CallControlScope? = null
         try {
             withTimeout(TestUtils.WAIT_ON_ASSERTS_TO_FINISH_TIMEOUT) {
                 mCallsManager.addCall(attributes) {
+                    callControlScope = this
                     if (setCallback) {
                         setCallback(TestUtils.mCallControlCallbacksImpl)
+                        Log.i(TestUtils.LOG_TAG, "assertWithinTimeout_addCall: setCallback " +
+                            "to ${TestUtils.mCallControlCallbacksImpl}")
                     }
                     assertBlock()
                 }
@@ -158,6 +168,7 @@
         } catch (timeout: TimeoutCancellationException) {
             Log.i(TestUtils.LOG_TAG, "assertWithinTimeout: reached timeout; dumping telecom")
             TestUtils.dumpTelecom()
+            callControlScope?.disconnect(DisconnectCause(DisconnectCause.LOCAL, "timeout in test"))
             Assert.fail(TestUtils.VERIFICATION_TIMEOUT_MSG)
         }
     }
diff --git a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/utils/TestUtils.kt b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/utils/TestUtils.kt
index d38fd18..68c9de5 100644
--- a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/utils/TestUtils.kt
+++ b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/utils/TestUtils.kt
@@ -17,6 +17,7 @@
 package androidx.core.telecom.test.utils
 
 import android.content.Context
+import android.media.AudioManager
 import android.net.Uri
 import android.os.Build.VERSION_CODES
 import android.os.UserHandle
@@ -46,8 +47,8 @@
     const val TEST_PACKAGE = "androidx.core.telecom.test"
     const val COMMAND_SET_DEFAULT_DIALER = "telecom set-default-dialer " // DO NOT REMOVE SPACE
     const val COMMAND_GET_DEFAULT_DIALER = "telecom get-default-dialer"
-    const val COMMAND_CLEANUP_STUCK_CALLS = "cleanup-stuck-calls"
     const val COMMAND_ENABLE_PHONE_ACCOUNT = "telecom set-phone-account-enabled "
+    const val COMMAND_CLEANUP_STUCK_CALLS = "telecom cleanup-stuck-calls"
     const val COMMAND_DUMP_TELECOM = "dumpsys telecom"
     const val TEST_CALL_ATTRIB_NAME = "Elon Musk"
     const val OUTGOING_NAME = "Larry Page"
@@ -225,6 +226,19 @@
         )
     }
 
+    fun getAudioModeName(mode: Int): String {
+        return when (mode) {
+            AudioManager.MODE_NORMAL -> "MODE_NORMAL"
+            AudioManager.MODE_RINGTONE -> "MODE_RINGTONE"
+            AudioManager.MODE_IN_CALL -> "MODE_IN_CALL"
+            AudioManager.MODE_IN_COMMUNICATION -> "MODE_IN_COMMUNICATION"
+            AudioManager.MODE_CALL_SCREENING -> "MODE_CALL_SCREENING"
+            AudioManager.MODE_CALL_REDIRECT -> "MODE_CALL_REDIRECT"
+            AudioManager.MODE_COMMUNICATION_REDIRECT -> "MODE_COMMUNICATION_REDIRECT"
+            else -> "UNKNOWN mode = <$mode>"
+        }
+    }
+
     fun enablePhoneAccountHandle(context: Context, phoneAccountHandle: PhoneAccountHandle) {
         val pn = phoneAccountHandle.componentName.packageName
         val cn = phoneAccountHandle.componentName.className
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/CallControlScope.kt b/core/core-telecom/src/main/java/androidx/core/telecom/CallControlScope.kt
index 3ab71f1..0714c39 100644
--- a/core/core-telecom/src/main/java/androidx/core/telecom/CallControlScope.kt
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/CallControlScope.kt
@@ -17,6 +17,7 @@
 package androidx.core.telecom
 
 import android.os.ParcelUuid
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
 
 /**
@@ -36,13 +37,13 @@
  *         }
  *
  *         // Collect updates
- *         currentCallEndpoint
- *           .onEach { // access the new [CallEndpoint] here }
- *           .launchIn(coroutineScope)
+ *         launch {
+ *             currentCallEndpoint.collect { // access the new [CallEndpoint] here }
+ *         }
  *     }
  *
  */
-interface CallControlScope {
+interface CallControlScope : CoroutineScope {
     /**
      * This method should be the first method called within the [CallControlScope] and your VoIP
      * application should pass in a valid implementation of [CallControlCallback].  Failing to call
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/CallsManager.kt b/core/core-telecom/src/main/java/androidx/core/telecom/CallsManager.kt
index fa5b8609..e1c3a64 100644
--- a/core/core-telecom/src/main/java/androidx/core/telecom/CallsManager.kt
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/CallsManager.kt
@@ -167,12 +167,16 @@
      * disconnected, use the [CallControlScope.disconnect].
      *
      * <b>Call Lifecycle</b>: Your app is given foreground execution priority as long as you have an
-     * ongoing call and are posting a [android.app.Notification.CallStyle] notification.
-     * When your application is given foreground execution priority, your app is treated as a
-     * foreground service. Foreground execution priority will prevent the
-     * [android.app.ActivityManager] from killing your application when it is placed the
-     * background. Foreground execution priority is removed from your app when all of your app's
-     * calls terminate or your app no longer posts a valid notification.
+     * ongoing call and are posting a [android.app.Notification.CallStyle] notification within 5
+     * seconds of adding the call via this method. When your application is given foreground
+     * execution priority, your app is treated as a foreground service. Foreground execution
+     * priority will prevent the [android.app.ActivityManager] from killing your application when
+     * it is placed the background. Foreground execution priority is removed from your app when all
+     * of your app's calls terminate or your app no longer posts a valid notification.
+     *
+     * Note: For outgoing calls, your application should either immediately post a
+     * [android.app.Notification.CallStyle] notification or delay adding the call via this
+     * addCall method until the remote side is ready.
      *
      * @param callAttributes     attributes of the new call (incoming or outgoing, address, etc. )
      * @param block              DSL interface block that will run when the call is ready
@@ -236,7 +240,11 @@
 
             /* at this point in time we have CallControl object */
             val scope =
-                CallSession.CallControlScopeImpl(openResult.getCompleted(), callChannels)
+                CallSession.CallControlScopeImpl(
+                    openResult.getCompleted(),
+                    callChannels,
+                    coroutineContext
+                )
 
             // Run the clients code with the session active and exposed via the CallControlScope
             // interface implementation declared above.
@@ -255,8 +263,11 @@
 
             pauseExecutionUntilCallIsReady_orTimeout(openResult, request)
 
-            val scope =
-                CallSessionLegacy.CallControlScopeImpl(openResult.getCompleted(), callChannels)
+            val scope = CallSessionLegacy.CallControlScopeImpl(
+                openResult.getCompleted(),
+                callChannels,
+                coroutineContext
+            )
 
             // Run the clients code with the session active and exposed via the
             // CallControlScope interface implementation declared above.
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/internal/CallSession.kt b/core/core-telecom/src/main/java/androidx/core/telecom/internal/CallSession.kt
index d464886..0f2b720 100644
--- a/core/core-telecom/src/main/java/androidx/core/telecom/internal/CallSession.kt
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/internal/CallSession.kt
@@ -219,7 +219,8 @@
      */
     class CallControlScopeImpl(
         private val session: CallSession,
-        callChannels: CallChannels
+        callChannels: CallChannels,
+        override val coroutineContext: CoroutineContext
     ) : CallControlScope {
         //  handle actionable/handshake events that originate in the platform
         //  and require a response from the client
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/internal/CallSessionLegacy.kt b/core/core-telecom/src/main/java/androidx/core/telecom/internal/CallSessionLegacy.kt
index 5f3d00d..0ce89f4 100644
--- a/core/core-telecom/src/main/java/androidx/core/telecom/internal/CallSessionLegacy.kt
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/internal/CallSessionLegacy.kt
@@ -309,7 +309,8 @@
      */
     class CallControlScopeImpl(
         private val session: CallSessionLegacy,
-        callChannels: CallChannels
+        callChannels: CallChannels,
+        override val coroutineContext: CoroutineContext
     ) : CallControlScope {
         //  handle actionable/handshake events that originate in the platform
         //  and require a response from the client
diff --git a/core/core-testing/api/1.12.0-beta01.txt b/core/core-testing/api/1.12.0-beta01.txt
new file mode 100644
index 0000000..40c6d07
--- /dev/null
+++ b/core/core-testing/api/1.12.0-beta01.txt
@@ -0,0 +1,11 @@
+// Signature format: 4.0
+package androidx.core.testing.util {
+
+  public final class TestConsumer<T> implements androidx.core.util.Consumer<T> {
+    ctor public TestConsumer();
+    method public void accept(T t);
+    method public void assertValues(java.util.List<? extends T> values);
+  }
+
+}
+
diff --git a/core/core-testing/api/res-1.12.0-beta01.txt b/core/core-testing/api/res-1.12.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/core/core-testing/api/res-1.12.0-beta01.txt
diff --git a/core/core-testing/api/restricted_1.12.0-beta01.txt b/core/core-testing/api/restricted_1.12.0-beta01.txt
new file mode 100644
index 0000000..40c6d07
--- /dev/null
+++ b/core/core-testing/api/restricted_1.12.0-beta01.txt
@@ -0,0 +1,11 @@
+// Signature format: 4.0
+package androidx.core.testing.util {
+
+  public final class TestConsumer<T> implements androidx.core.util.Consumer<T> {
+    ctor public TestConsumer();
+    method public void accept(T t);
+    method public void assertValues(java.util.List<? extends T> values);
+  }
+
+}
+
diff --git a/core/core/api/1.12.0-beta01.txt b/core/core/api/1.12.0-beta01.txt
new file mode 100644
index 0000000..7ff5c2a
--- /dev/null
+++ b/core/core/api/1.12.0-beta01.txt
@@ -0,0 +1,4226 @@
+// Signature format: 4.0
+package androidx.core.accessibilityservice {
+
+  public final class AccessibilityServiceInfoCompat {
+    method public static String capabilityToString(int);
+    method public static String feedbackTypeToString(int);
+    method public static String? flagToString(int);
+    method public static int getCapabilities(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static String? loadDescription(android.accessibilityservice.AccessibilityServiceInfo, android.content.pm.PackageManager);
+    field public static final int CAPABILITY_CAN_FILTER_KEY_EVENTS = 8; // 0x8
+    field public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 4; // 0x4
+    field public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 2; // 0x2
+    field public static final int CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 1; // 0x1
+    field public static final int FEEDBACK_ALL_MASK = -1; // 0xffffffff
+    field public static final int FEEDBACK_BRAILLE = 32; // 0x20
+    field public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 2; // 0x2
+    field public static final int FLAG_REPORT_VIEW_IDS = 16; // 0x10
+    field public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 8; // 0x8
+    field public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 32; // 0x20
+    field public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 4; // 0x4
+  }
+
+}
+
+package androidx.core.app {
+
+  public class ActivityCompat extends androidx.core.content.ContextCompat {
+    ctor protected ActivityCompat();
+    method public static void finishAffinity(android.app.Activity);
+    method public static void finishAfterTransition(android.app.Activity);
+    method public static android.net.Uri? getReferrer(android.app.Activity);
+    method @Deprecated public static boolean invalidateOptionsMenu(android.app.Activity!);
+    method public static boolean isLaunchedFromBubble(android.app.Activity);
+    method public static void postponeEnterTransition(android.app.Activity);
+    method public static void recreate(android.app.Activity);
+    method public static androidx.core.view.DragAndDropPermissionsCompat? requestDragAndDropPermissions(android.app.Activity, android.view.DragEvent);
+    method public static void requestPermissions(android.app.Activity, String![], @IntRange(from=0) int);
+    method public static <T extends android.view.View> T requireViewById(android.app.Activity, @IdRes int);
+    method public static void setEnterSharedElementCallback(android.app.Activity, androidx.core.app.SharedElementCallback?);
+    method public static void setExitSharedElementCallback(android.app.Activity, androidx.core.app.SharedElementCallback?);
+    method public static void setLocusContext(android.app.Activity, androidx.core.content.LocusIdCompat?, android.os.Bundle?);
+    method public static void setPermissionCompatDelegate(androidx.core.app.ActivityCompat.PermissionCompatDelegate?);
+    method public static boolean shouldShowRequestPermissionRationale(android.app.Activity, String);
+    method public static void startActivityForResult(android.app.Activity, android.content.Intent, int, android.os.Bundle?);
+    method public static void startIntentSenderForResult(android.app.Activity, android.content.IntentSender, int, android.content.Intent?, int, int, int, android.os.Bundle?) throws android.content.IntentSender.SendIntentException;
+    method public static void startPostponedEnterTransition(android.app.Activity);
+  }
+
+  public static interface ActivityCompat.OnRequestPermissionsResultCallback {
+    method public void onRequestPermissionsResult(int, String![], int[]);
+  }
+
+  public static interface ActivityCompat.PermissionCompatDelegate {
+    method public boolean onActivityResult(android.app.Activity, @IntRange(from=0) int, int, android.content.Intent?);
+    method public boolean requestPermissions(android.app.Activity, String![], @IntRange(from=0) int);
+  }
+
+  public final class ActivityManagerCompat {
+    method public static boolean isLowRamDevice(android.app.ActivityManager);
+  }
+
+  public class ActivityOptionsCompat {
+    ctor protected ActivityOptionsCompat();
+    method public android.graphics.Rect? getLaunchBounds();
+    method public static androidx.core.app.ActivityOptionsCompat makeBasic();
+    method public static androidx.core.app.ActivityOptionsCompat makeClipRevealAnimation(android.view.View, int, int, int, int);
+    method public static androidx.core.app.ActivityOptionsCompat makeCustomAnimation(android.content.Context, int, int);
+    method public static androidx.core.app.ActivityOptionsCompat makeScaleUpAnimation(android.view.View, int, int, int, int);
+    method public static androidx.core.app.ActivityOptionsCompat makeSceneTransitionAnimation(android.app.Activity, android.view.View, String);
+    method public static androidx.core.app.ActivityOptionsCompat makeSceneTransitionAnimation(android.app.Activity, androidx.core.util.Pair<android.view.View!,java.lang.String!>!...);
+    method public static androidx.core.app.ActivityOptionsCompat makeTaskLaunchBehind();
+    method public static androidx.core.app.ActivityOptionsCompat makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int);
+    method public void requestUsageTimeReport(android.app.PendingIntent);
+    method public androidx.core.app.ActivityOptionsCompat setLaunchBounds(android.graphics.Rect?);
+    method public android.os.Bundle? toBundle();
+    method public void update(androidx.core.app.ActivityOptionsCompat);
+    field public static final String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
+    field public static final String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages";
+  }
+
+  public final class AlarmManagerCompat {
+    method public static void setAlarmClock(android.app.AlarmManager, long, android.app.PendingIntent, android.app.PendingIntent);
+    method public static void setAndAllowWhileIdle(android.app.AlarmManager, int, long, android.app.PendingIntent);
+    method public static void setExact(android.app.AlarmManager, int, long, android.app.PendingIntent);
+    method public static void setExactAndAllowWhileIdle(android.app.AlarmManager, int, long, android.app.PendingIntent);
+  }
+
+  @RequiresApi(28) public class AppComponentFactory extends android.app.AppComponentFactory {
+    ctor public AppComponentFactory();
+    method public final android.app.Activity instantiateActivity(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public android.app.Activity instantiateActivityCompat(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public final android.app.Application instantiateApplication(ClassLoader, String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public android.app.Application instantiateApplicationCompat(ClassLoader, String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public final android.content.ContentProvider instantiateProvider(ClassLoader, String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public android.content.ContentProvider instantiateProviderCompat(ClassLoader, String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public final android.content.BroadcastReceiver instantiateReceiver(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public android.content.BroadcastReceiver instantiateReceiverCompat(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public final android.app.Service instantiateService(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public android.app.Service instantiateServiceCompat(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+  }
+
+  public class AppLaunchChecker {
+    ctor @Deprecated public AppLaunchChecker();
+    method public static boolean hasStartedFromLauncher(android.content.Context);
+    method public static void onActivityCreate(android.app.Activity);
+  }
+
+  public final class AppOpsManagerCompat {
+    method public static int checkOrNoteProxyOp(android.content.Context, int, String, String);
+    method public static int noteOp(android.content.Context, String, int, String);
+    method public static int noteOpNoThrow(android.content.Context, String, int, String);
+    method public static int noteProxyOp(android.content.Context, String, String);
+    method public static int noteProxyOpNoThrow(android.content.Context, String, String);
+    method public static String? permissionToOp(String);
+    field public static final int MODE_ALLOWED = 0; // 0x0
+    field public static final int MODE_DEFAULT = 3; // 0x3
+    field public static final int MODE_ERRORED = 2; // 0x2
+    field public static final int MODE_IGNORED = 1; // 0x1
+  }
+
+  @Deprecated public final class BundleCompat {
+    method @Deprecated public static android.os.IBinder? getBinder(android.os.Bundle, String?);
+    method @Deprecated public static void putBinder(android.os.Bundle, String?, android.os.IBinder?);
+  }
+
+  public class DialogCompat {
+    method public static android.view.View requireViewById(android.app.Dialog, int);
+  }
+
+  public class FrameMetricsAggregator {
+    ctor public FrameMetricsAggregator();
+    ctor public FrameMetricsAggregator(int);
+    method public void add(android.app.Activity);
+    method public android.util.SparseIntArray![]? getMetrics();
+    method public android.util.SparseIntArray![]? remove(android.app.Activity);
+    method public android.util.SparseIntArray![]? reset();
+    method public android.util.SparseIntArray![]? stop();
+    field public static final int ANIMATION_DURATION = 256; // 0x100
+    field public static final int ANIMATION_INDEX = 8; // 0x8
+    field public static final int COMMAND_DURATION = 32; // 0x20
+    field public static final int COMMAND_INDEX = 5; // 0x5
+    field public static final int DELAY_DURATION = 128; // 0x80
+    field public static final int DELAY_INDEX = 7; // 0x7
+    field public static final int DRAW_DURATION = 8; // 0x8
+    field public static final int DRAW_INDEX = 3; // 0x3
+    field public static final int EVERY_DURATION = 511; // 0x1ff
+    field public static final int INPUT_DURATION = 2; // 0x2
+    field public static final int INPUT_INDEX = 1; // 0x1
+    field public static final int LAYOUT_MEASURE_DURATION = 4; // 0x4
+    field public static final int LAYOUT_MEASURE_INDEX = 2; // 0x2
+    field public static final int SWAP_DURATION = 64; // 0x40
+    field public static final int SWAP_INDEX = 6; // 0x6
+    field public static final int SYNC_DURATION = 16; // 0x10
+    field public static final int SYNC_INDEX = 4; // 0x4
+    field public static final int TOTAL_DURATION = 1; // 0x1
+    field public static final int TOTAL_INDEX = 0; // 0x0
+  }
+
+  public final class GrammaticalInflectionManagerCompat {
+    method @AnyThread public static int getApplicationGrammaticalGender(android.content.Context);
+    method @AnyThread public static void setRequestedApplicationGrammaticalGender(android.content.Context, int);
+    field public static final int GRAMMATICAL_GENDER_FEMININE = 2; // 0x2
+    field public static final int GRAMMATICAL_GENDER_MASCULINE = 3; // 0x3
+    field public static final int GRAMMATICAL_GENDER_NEUTRAL = 1; // 0x1
+    field public static final int GRAMMATICAL_GENDER_NOT_SPECIFIED = 0; // 0x0
+  }
+
+  @Deprecated public abstract class JobIntentService extends android.app.Service {
+    ctor @Deprecated public JobIntentService();
+    method @Deprecated public static void enqueueWork(android.content.Context, android.content.ComponentName, int, android.content.Intent);
+    method @Deprecated public static void enqueueWork(android.content.Context, Class<?>, int, android.content.Intent);
+    method @Deprecated public boolean isStopped();
+    method @Deprecated public android.os.IBinder! onBind(android.content.Intent);
+    method @Deprecated protected abstract void onHandleWork(android.content.Intent);
+    method @Deprecated public boolean onStopCurrentWork();
+    method @Deprecated public void setInterruptIfStopped(boolean);
+  }
+
+  public final class LocaleManagerCompat {
+    method @AnyThread public static androidx.core.os.LocaleListCompat getApplicationLocales(android.content.Context);
+    method @AnyThread public static androidx.core.os.LocaleListCompat getSystemLocales(android.content.Context);
+  }
+
+  public final class MultiWindowModeChangedInfo {
+    ctor public MultiWindowModeChangedInfo(boolean);
+    ctor @RequiresApi(26) public MultiWindowModeChangedInfo(boolean, android.content.res.Configuration);
+    method @RequiresApi(26) public android.content.res.Configuration getNewConfig();
+    method public boolean isInMultiWindowMode();
+  }
+
+  public final class NavUtils {
+    method public static android.content.Intent? getParentActivityIntent(android.app.Activity);
+    method public static android.content.Intent? getParentActivityIntent(android.content.Context, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static android.content.Intent? getParentActivityIntent(android.content.Context, Class<?>) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static String? getParentActivityName(android.app.Activity);
+    method public static String? getParentActivityName(android.content.Context, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static void navigateUpFromSameTask(android.app.Activity);
+    method public static void navigateUpTo(android.app.Activity, android.content.Intent);
+    method public static boolean shouldUpRecreateTask(android.app.Activity, android.content.Intent);
+    field public static final String PARENT_ACTIVITY = "android.support.PARENT_ACTIVITY";
+  }
+
+  public class NotificationChannelCompat {
+    method public boolean canBubble();
+    method public boolean canBypassDnd();
+    method public boolean canShowBadge();
+    method public android.media.AudioAttributes? getAudioAttributes();
+    method public String? getConversationId();
+    method public String? getDescription();
+    method public String? getGroup();
+    method public String getId();
+    method public int getImportance();
+    method public int getLightColor();
+    method public int getLockscreenVisibility();
+    method public CharSequence? getName();
+    method public String? getParentChannelId();
+    method public android.net.Uri? getSound();
+    method public long[]? getVibrationPattern();
+    method public boolean isImportantConversation();
+    method public boolean shouldShowLights();
+    method public boolean shouldVibrate();
+    method public androidx.core.app.NotificationChannelCompat.Builder toBuilder();
+    field public static final String DEFAULT_CHANNEL_ID = "miscellaneous";
+  }
+
+  public static class NotificationChannelCompat.Builder {
+    ctor public NotificationChannelCompat.Builder(String, int);
+    method public androidx.core.app.NotificationChannelCompat build();
+    method public androidx.core.app.NotificationChannelCompat.Builder setConversationId(String, String);
+    method public androidx.core.app.NotificationChannelCompat.Builder setDescription(String?);
+    method public androidx.core.app.NotificationChannelCompat.Builder setGroup(String?);
+    method public androidx.core.app.NotificationChannelCompat.Builder setImportance(int);
+    method public androidx.core.app.NotificationChannelCompat.Builder setLightColor(int);
+    method public androidx.core.app.NotificationChannelCompat.Builder setLightsEnabled(boolean);
+    method public androidx.core.app.NotificationChannelCompat.Builder setName(CharSequence?);
+    method public androidx.core.app.NotificationChannelCompat.Builder setShowBadge(boolean);
+    method public androidx.core.app.NotificationChannelCompat.Builder setSound(android.net.Uri?, android.media.AudioAttributes?);
+    method public androidx.core.app.NotificationChannelCompat.Builder setVibrationEnabled(boolean);
+    method public androidx.core.app.NotificationChannelCompat.Builder setVibrationPattern(long[]?);
+  }
+
+  public class NotificationChannelGroupCompat {
+    method public java.util.List<androidx.core.app.NotificationChannelCompat!> getChannels();
+    method public String? getDescription();
+    method public String getId();
+    method public CharSequence? getName();
+    method public boolean isBlocked();
+    method public androidx.core.app.NotificationChannelGroupCompat.Builder toBuilder();
+  }
+
+  public static class NotificationChannelGroupCompat.Builder {
+    ctor public NotificationChannelGroupCompat.Builder(String);
+    method public androidx.core.app.NotificationChannelGroupCompat build();
+    method public androidx.core.app.NotificationChannelGroupCompat.Builder setDescription(String?);
+    method public androidx.core.app.NotificationChannelGroupCompat.Builder setName(CharSequence?);
+  }
+
+  public class NotificationCompat {
+    ctor @Deprecated public NotificationCompat();
+    method public static androidx.core.app.NotificationCompat.Action? getAction(android.app.Notification, int);
+    method public static int getActionCount(android.app.Notification);
+    method public static boolean getAllowSystemGeneratedContextualActions(android.app.Notification);
+    method public static boolean getAutoCancel(android.app.Notification);
+    method public static int getBadgeIconType(android.app.Notification);
+    method public static androidx.core.app.NotificationCompat.BubbleMetadata? getBubbleMetadata(android.app.Notification);
+    method public static String? getCategory(android.app.Notification);
+    method public static String? getChannelId(android.app.Notification);
+    method public static int getColor(android.app.Notification);
+    method @RequiresApi(19) public static CharSequence? getContentInfo(android.app.Notification);
+    method @RequiresApi(19) public static CharSequence? getContentText(android.app.Notification);
+    method @RequiresApi(19) public static CharSequence? getContentTitle(android.app.Notification);
+    method public static android.os.Bundle? getExtras(android.app.Notification);
+    method public static String? getGroup(android.app.Notification);
+    method public static int getGroupAlertBehavior(android.app.Notification);
+    method @RequiresApi(21) public static java.util.List<androidx.core.app.NotificationCompat.Action!> getInvisibleActions(android.app.Notification);
+    method public static boolean getLocalOnly(android.app.Notification);
+    method public static androidx.core.content.LocusIdCompat? getLocusId(android.app.Notification);
+    method public static boolean getOngoing(android.app.Notification);
+    method public static boolean getOnlyAlertOnce(android.app.Notification);
+    method public static java.util.List<androidx.core.app.Person!> getPeople(android.app.Notification);
+    method public static android.app.Notification? getPublicVersion(android.app.Notification);
+    method public static CharSequence? getSettingsText(android.app.Notification);
+    method public static String? getShortcutId(android.app.Notification);
+    method @RequiresApi(19) public static boolean getShowWhen(android.app.Notification);
+    method public static String? getSortKey(android.app.Notification);
+    method @RequiresApi(19) public static CharSequence? getSubText(android.app.Notification);
+    method public static long getTimeoutAfter(android.app.Notification);
+    method @RequiresApi(19) public static boolean getUsesChronometer(android.app.Notification);
+    method public static int getVisibility(android.app.Notification);
+    method public static boolean isGroupSummary(android.app.Notification);
+    method public static android.graphics.Bitmap? reduceLargeIconSize(android.content.Context, android.graphics.Bitmap?);
+    field public static final int BADGE_ICON_LARGE = 2; // 0x2
+    field public static final int BADGE_ICON_NONE = 0; // 0x0
+    field public static final int BADGE_ICON_SMALL = 1; // 0x1
+    field public static final String CATEGORY_ALARM = "alarm";
+    field public static final String CATEGORY_CALL = "call";
+    field public static final String CATEGORY_EMAIL = "email";
+    field public static final String CATEGORY_ERROR = "err";
+    field public static final String CATEGORY_EVENT = "event";
+    field public static final String CATEGORY_LOCATION_SHARING = "location_sharing";
+    field public static final String CATEGORY_MESSAGE = "msg";
+    field public static final String CATEGORY_MISSED_CALL = "missed_call";
+    field public static final String CATEGORY_NAVIGATION = "navigation";
+    field public static final String CATEGORY_PROGRESS = "progress";
+    field public static final String CATEGORY_PROMO = "promo";
+    field public static final String CATEGORY_RECOMMENDATION = "recommendation";
+    field public static final String CATEGORY_REMINDER = "reminder";
+    field public static final String CATEGORY_SERVICE = "service";
+    field public static final String CATEGORY_SOCIAL = "social";
+    field public static final String CATEGORY_STATUS = "status";
+    field public static final String CATEGORY_STOPWATCH = "stopwatch";
+    field public static final String CATEGORY_SYSTEM = "sys";
+    field public static final String CATEGORY_TRANSPORT = "transport";
+    field public static final String CATEGORY_WORKOUT = "workout";
+    field @ColorInt public static final int COLOR_DEFAULT = 0; // 0x0
+    field public static final int DEFAULT_ALL = -1; // 0xffffffff
+    field public static final int DEFAULT_LIGHTS = 4; // 0x4
+    field public static final int DEFAULT_SOUND = 1; // 0x1
+    field public static final int DEFAULT_VIBRATE = 2; // 0x2
+    field public static final String EXTRA_ANSWER_COLOR = "android.answerColor";
+    field public static final String EXTRA_ANSWER_INTENT = "android.answerIntent";
+    field public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
+    field public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
+    field public static final String EXTRA_BIG_TEXT = "android.bigText";
+    field public static final String EXTRA_CALL_IS_VIDEO = "android.callIsVideo";
+    field public static final String EXTRA_CALL_PERSON = "android.callPerson";
+    field public static final String EXTRA_CALL_PERSON_COMPAT = "android.callPersonCompat";
+    field public static final String EXTRA_CALL_TYPE = "android.callType";
+    field public static final String EXTRA_CHANNEL_GROUP_ID = "android.intent.extra.CHANNEL_GROUP_ID";
+    field public static final String EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID";
+    field public static final String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
+    field public static final String EXTRA_COLORIZED = "android.colorized";
+    field public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
+    field public static final String EXTRA_COMPAT_TEMPLATE = "androidx.core.app.extra.COMPAT_TEMPLATE";
+    field public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
+    field public static final String EXTRA_DECLINE_COLOR = "android.declineColor";
+    field public static final String EXTRA_DECLINE_INTENT = "android.declineIntent";
+    field public static final String EXTRA_HANG_UP_INTENT = "android.hangUpIntent";
+    field public static final String EXTRA_HIDDEN_CONVERSATION_TITLE = "android.hiddenConversationTitle";
+    field public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
+    field public static final String EXTRA_INFO_TEXT = "android.infoText";
+    field public static final String EXTRA_IS_GROUP_CONVERSATION = "android.isGroupConversation";
+    field public static final String EXTRA_LARGE_ICON = "android.largeIcon";
+    field public static final String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
+    field public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
+    field public static final String EXTRA_MESSAGES = "android.messages";
+    field public static final String EXTRA_MESSAGING_STYLE_USER = "android.messagingStyleUser";
+    field public static final String EXTRA_NOTIFICATION_ID = "android.intent.extra.NOTIFICATION_ID";
+    field public static final String EXTRA_NOTIFICATION_TAG = "android.intent.extra.NOTIFICATION_TAG";
+    field @Deprecated public static final String EXTRA_PEOPLE = "android.people";
+    field public static final String EXTRA_PEOPLE_LIST = "android.people.list";
+    field public static final String EXTRA_PICTURE = "android.picture";
+    field public static final String EXTRA_PICTURE_CONTENT_DESCRIPTION = "android.pictureContentDescription";
+    field public static final String EXTRA_PICTURE_ICON = "android.pictureIcon";
+    field public static final String EXTRA_PROGRESS = "android.progress";
+    field public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
+    field public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
+    field public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
+    field public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
+    field public static final String EXTRA_SHOW_BIG_PICTURE_WHEN_COLLAPSED = "android.showBigPictureWhenCollapsed";
+    field public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
+    field public static final String EXTRA_SHOW_WHEN = "android.showWhen";
+    field public static final String EXTRA_SMALL_ICON = "android.icon";
+    field public static final String EXTRA_SUB_TEXT = "android.subText";
+    field public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
+    field public static final String EXTRA_TEMPLATE = "android.template";
+    field public static final String EXTRA_TEXT = "android.text";
+    field public static final String EXTRA_TEXT_LINES = "android.textLines";
+    field public static final String EXTRA_TITLE = "android.title";
+    field public static final String EXTRA_TITLE_BIG = "android.title.big";
+    field public static final String EXTRA_VERIFICATION_ICON = "android.verificationIcon";
+    field public static final String EXTRA_VERIFICATION_ICON_COMPAT = "android.verificationIconCompat";
+    field public static final String EXTRA_VERIFICATION_TEXT = "android.verificationText";
+    field public static final int FLAG_AUTO_CANCEL = 16; // 0x10
+    field public static final int FLAG_BUBBLE = 4096; // 0x1000
+    field public static final int FLAG_FOREGROUND_SERVICE = 64; // 0x40
+    field public static final int FLAG_GROUP_SUMMARY = 512; // 0x200
+    field @Deprecated public static final int FLAG_HIGH_PRIORITY = 128; // 0x80
+    field public static final int FLAG_INSISTENT = 4; // 0x4
+    field public static final int FLAG_LOCAL_ONLY = 256; // 0x100
+    field public static final int FLAG_NO_CLEAR = 32; // 0x20
+    field public static final int FLAG_ONGOING_EVENT = 2; // 0x2
+    field public static final int FLAG_ONLY_ALERT_ONCE = 8; // 0x8
+    field public static final int FLAG_SHOW_LIGHTS = 1; // 0x1
+    field public static final int FOREGROUND_SERVICE_DEFAULT = 0; // 0x0
+    field public static final int FOREGROUND_SERVICE_DEFERRED = 2; // 0x2
+    field public static final int FOREGROUND_SERVICE_IMMEDIATE = 1; // 0x1
+    field public static final int GROUP_ALERT_ALL = 0; // 0x0
+    field public static final int GROUP_ALERT_CHILDREN = 2; // 0x2
+    field public static final int GROUP_ALERT_SUMMARY = 1; // 0x1
+    field public static final String GROUP_KEY_SILENT = "silent";
+    field public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES = "android.intent.category.NOTIFICATION_PREFERENCES";
+    field public static final int PRIORITY_DEFAULT = 0; // 0x0
+    field public static final int PRIORITY_HIGH = 1; // 0x1
+    field public static final int PRIORITY_LOW = -1; // 0xffffffff
+    field public static final int PRIORITY_MAX = 2; // 0x2
+    field public static final int PRIORITY_MIN = -2; // 0xfffffffe
+    field public static final int STREAM_DEFAULT = -1; // 0xffffffff
+    field public static final int VISIBILITY_PRIVATE = 0; // 0x0
+    field public static final int VISIBILITY_PUBLIC = 1; // 0x1
+    field public static final int VISIBILITY_SECRET = -1; // 0xffffffff
+  }
+
+  public static class NotificationCompat.Action {
+    ctor public NotificationCompat.Action(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
+    ctor public NotificationCompat.Action(int, CharSequence?, android.app.PendingIntent?);
+    method public android.app.PendingIntent? getActionIntent();
+    method public boolean getAllowGeneratedReplies();
+    method public androidx.core.app.RemoteInput![]? getDataOnlyRemoteInputs();
+    method public android.os.Bundle getExtras();
+    method @Deprecated public int getIcon();
+    method public androidx.core.graphics.drawable.IconCompat? getIconCompat();
+    method public androidx.core.app.RemoteInput![]? getRemoteInputs();
+    method @androidx.core.app.NotificationCompat.Action.SemanticAction public int getSemanticAction();
+    method public boolean getShowsUserInterface();
+    method public CharSequence? getTitle();
+    method public boolean isAuthenticationRequired();
+    method public boolean isContextual();
+    field public static final int SEMANTIC_ACTION_ARCHIVE = 5; // 0x5
+    field public static final int SEMANTIC_ACTION_CALL = 10; // 0xa
+    field public static final int SEMANTIC_ACTION_DELETE = 4; // 0x4
+    field public static final int SEMANTIC_ACTION_MARK_AS_READ = 2; // 0x2
+    field public static final int SEMANTIC_ACTION_MARK_AS_UNREAD = 3; // 0x3
+    field public static final int SEMANTIC_ACTION_MUTE = 6; // 0x6
+    field public static final int SEMANTIC_ACTION_NONE = 0; // 0x0
+    field public static final int SEMANTIC_ACTION_REPLY = 1; // 0x1
+    field public static final int SEMANTIC_ACTION_THUMBS_DOWN = 9; // 0x9
+    field public static final int SEMANTIC_ACTION_THUMBS_UP = 8; // 0x8
+    field public static final int SEMANTIC_ACTION_UNMUTE = 7; // 0x7
+    field public android.app.PendingIntent? actionIntent;
+    field @Deprecated public int icon;
+    field public CharSequence! title;
+  }
+
+  public static final class NotificationCompat.Action.Builder {
+    ctor public NotificationCompat.Action.Builder(androidx.core.app.NotificationCompat.Action);
+    ctor public NotificationCompat.Action.Builder(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
+    ctor public NotificationCompat.Action.Builder(int, CharSequence?, android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.Action.Builder addExtras(android.os.Bundle?);
+    method public androidx.core.app.NotificationCompat.Action.Builder addRemoteInput(androidx.core.app.RemoteInput?);
+    method public androidx.core.app.NotificationCompat.Action build();
+    method public androidx.core.app.NotificationCompat.Action.Builder extend(androidx.core.app.NotificationCompat.Action.Extender);
+    method public android.os.Bundle getExtras();
+    method public androidx.core.app.NotificationCompat.Action.Builder setAllowGeneratedReplies(boolean);
+    method public androidx.core.app.NotificationCompat.Action.Builder setAuthenticationRequired(boolean);
+    method public androidx.core.app.NotificationCompat.Action.Builder setContextual(boolean);
+    method public androidx.core.app.NotificationCompat.Action.Builder setSemanticAction(@androidx.core.app.NotificationCompat.Action.SemanticAction int);
+    method public androidx.core.app.NotificationCompat.Action.Builder setShowsUserInterface(boolean);
+  }
+
+  public static interface NotificationCompat.Action.Extender {
+    method public androidx.core.app.NotificationCompat.Action.Builder extend(androidx.core.app.NotificationCompat.Action.Builder);
+  }
+
+  @IntDef({androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_NONE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_REPLY, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_READ, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_UNREAD, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_DELETE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_ARCHIVE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_MUTE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_UNMUTE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_THUMBS_UP, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_THUMBS_DOWN, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_CALL}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NotificationCompat.Action.SemanticAction {
+  }
+
+  public static final class NotificationCompat.Action.WearableExtender implements androidx.core.app.NotificationCompat.Action.Extender {
+    ctor public NotificationCompat.Action.WearableExtender();
+    ctor public NotificationCompat.Action.WearableExtender(androidx.core.app.NotificationCompat.Action);
+    method public androidx.core.app.NotificationCompat.Action.WearableExtender clone();
+    method public androidx.core.app.NotificationCompat.Action.Builder extend(androidx.core.app.NotificationCompat.Action.Builder);
+    method @Deprecated public CharSequence? getCancelLabel();
+    method @Deprecated public CharSequence? getConfirmLabel();
+    method public boolean getHintDisplayActionInline();
+    method public boolean getHintLaunchesActivity();
+    method @Deprecated public CharSequence? getInProgressLabel();
+    method public boolean isAvailableOffline();
+    method public androidx.core.app.NotificationCompat.Action.WearableExtender setAvailableOffline(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.Action.WearableExtender setCancelLabel(CharSequence?);
+    method @Deprecated public androidx.core.app.NotificationCompat.Action.WearableExtender setConfirmLabel(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Action.WearableExtender setHintDisplayActionInline(boolean);
+    method public androidx.core.app.NotificationCompat.Action.WearableExtender setHintLaunchesActivity(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.Action.WearableExtender setInProgressLabel(CharSequence?);
+  }
+
+  public static class NotificationCompat.BigPictureStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor public NotificationCompat.BigPictureStyle();
+    ctor public NotificationCompat.BigPictureStyle(androidx.core.app.NotificationCompat.Builder?);
+    method public androidx.core.app.NotificationCompat.BigPictureStyle bigLargeIcon(android.graphics.Bitmap?);
+    method @RequiresApi(23) public androidx.core.app.NotificationCompat.BigPictureStyle bigLargeIcon(android.graphics.drawable.Icon?);
+    method public androidx.core.app.NotificationCompat.BigPictureStyle bigPicture(android.graphics.Bitmap?);
+    method @RequiresApi(31) public androidx.core.app.NotificationCompat.BigPictureStyle bigPicture(android.graphics.drawable.Icon?);
+    method public androidx.core.app.NotificationCompat.BigPictureStyle setBigContentTitle(CharSequence?);
+    method @RequiresApi(31) public androidx.core.app.NotificationCompat.BigPictureStyle setContentDescription(CharSequence?);
+    method public androidx.core.app.NotificationCompat.BigPictureStyle setSummaryText(CharSequence?);
+    method @RequiresApi(31) public androidx.core.app.NotificationCompat.BigPictureStyle showBigPictureWhenCollapsed(boolean);
+  }
+
+  public static class NotificationCompat.BigTextStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor public NotificationCompat.BigTextStyle();
+    ctor public NotificationCompat.BigTextStyle(androidx.core.app.NotificationCompat.Builder?);
+    method public androidx.core.app.NotificationCompat.BigTextStyle bigText(CharSequence?);
+    method public androidx.core.app.NotificationCompat.BigTextStyle setBigContentTitle(CharSequence?);
+    method public androidx.core.app.NotificationCompat.BigTextStyle setSummaryText(CharSequence?);
+  }
+
+  public static final class NotificationCompat.BubbleMetadata {
+    method public static androidx.core.app.NotificationCompat.BubbleMetadata? fromPlatform(android.app.Notification.BubbleMetadata?);
+    method public boolean getAutoExpandBubble();
+    method public android.app.PendingIntent? getDeleteIntent();
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public int getDesiredHeight();
+    method @DimenRes public int getDesiredHeightResId();
+    method public androidx.core.graphics.drawable.IconCompat? getIcon();
+    method public android.app.PendingIntent? getIntent();
+    method public String? getShortcutId();
+    method public boolean isNotificationSuppressed();
+    method public static android.app.Notification.BubbleMetadata? toPlatform(androidx.core.app.NotificationCompat.BubbleMetadata?);
+  }
+
+  public static final class NotificationCompat.BubbleMetadata.Builder {
+    ctor @Deprecated public NotificationCompat.BubbleMetadata.Builder();
+    ctor public NotificationCompat.BubbleMetadata.Builder(android.app.PendingIntent, androidx.core.graphics.drawable.IconCompat);
+    ctor @RequiresApi(30) public NotificationCompat.BubbleMetadata.Builder(String);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata build();
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setAutoExpandBubble(boolean);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setDeleteIntent(android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setDesiredHeight(@Dimension(unit=androidx.annotation.Dimension.DP) int);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setDesiredHeightResId(@DimenRes int);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setIcon(androidx.core.graphics.drawable.IconCompat);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setIntent(android.app.PendingIntent);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setSuppressNotification(boolean);
+  }
+
+  public static class NotificationCompat.Builder {
+    ctor @Deprecated public NotificationCompat.Builder(android.content.Context);
+    ctor @RequiresApi(19) public NotificationCompat.Builder(android.content.Context, android.app.Notification);
+    ctor public NotificationCompat.Builder(android.content.Context, String);
+    method public androidx.core.app.NotificationCompat.Builder addAction(androidx.core.app.NotificationCompat.Action?);
+    method public androidx.core.app.NotificationCompat.Builder addAction(int, CharSequence?, android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.Builder addExtras(android.os.Bundle?);
+    method @RequiresApi(21) public androidx.core.app.NotificationCompat.Builder addInvisibleAction(androidx.core.app.NotificationCompat.Action?);
+    method @RequiresApi(21) public androidx.core.app.NotificationCompat.Builder addInvisibleAction(int, CharSequence?, android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.Builder addPerson(androidx.core.app.Person?);
+    method @Deprecated public androidx.core.app.NotificationCompat.Builder addPerson(String?);
+    method public android.app.Notification build();
+    method public androidx.core.app.NotificationCompat.Builder clearActions();
+    method public androidx.core.app.NotificationCompat.Builder clearInvisibleActions();
+    method public androidx.core.app.NotificationCompat.Builder clearPeople();
+    method public android.widget.RemoteViews? createBigContentView();
+    method public android.widget.RemoteViews? createContentView();
+    method public android.widget.RemoteViews? createHeadsUpContentView();
+    method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Extender);
+    method public android.os.Bundle getExtras();
+    method @Deprecated public android.app.Notification getNotification();
+    method protected static CharSequence? limitCharSequenceLength(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setAllowSystemGeneratedContextualActions(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setAutoCancel(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setBadgeIconType(int);
+    method public androidx.core.app.NotificationCompat.Builder setBubbleMetadata(androidx.core.app.NotificationCompat.BubbleMetadata?);
+    method public androidx.core.app.NotificationCompat.Builder setCategory(String?);
+    method public androidx.core.app.NotificationCompat.Builder setChannelId(String);
+    method @RequiresApi(24) public androidx.core.app.NotificationCompat.Builder setChronometerCountDown(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setColor(@ColorInt int);
+    method public androidx.core.app.NotificationCompat.Builder setColorized(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setContent(android.widget.RemoteViews?);
+    method public androidx.core.app.NotificationCompat.Builder setContentInfo(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setContentIntent(android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.Builder setContentText(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setContentTitle(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setCustomBigContentView(android.widget.RemoteViews?);
+    method public androidx.core.app.NotificationCompat.Builder setCustomContentView(android.widget.RemoteViews?);
+    method public androidx.core.app.NotificationCompat.Builder setCustomHeadsUpContentView(android.widget.RemoteViews?);
+    method public androidx.core.app.NotificationCompat.Builder setDefaults(int);
+    method public androidx.core.app.NotificationCompat.Builder setDeleteIntent(android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.Builder setExtras(android.os.Bundle?);
+    method public androidx.core.app.NotificationCompat.Builder setForegroundServiceBehavior(int);
+    method public androidx.core.app.NotificationCompat.Builder setFullScreenIntent(android.app.PendingIntent?, boolean);
+    method public androidx.core.app.NotificationCompat.Builder setGroup(String?);
+    method public androidx.core.app.NotificationCompat.Builder setGroupAlertBehavior(int);
+    method public androidx.core.app.NotificationCompat.Builder setGroupSummary(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setLargeIcon(android.graphics.Bitmap?);
+    method @RequiresApi(23) public androidx.core.app.NotificationCompat.Builder setLargeIcon(android.graphics.drawable.Icon?);
+    method public androidx.core.app.NotificationCompat.Builder setLights(@ColorInt int, int, int);
+    method public androidx.core.app.NotificationCompat.Builder setLocalOnly(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setLocusId(androidx.core.content.LocusIdCompat?);
+    method @Deprecated public androidx.core.app.NotificationCompat.Builder setNotificationSilent();
+    method public androidx.core.app.NotificationCompat.Builder setNumber(int);
+    method public androidx.core.app.NotificationCompat.Builder setOngoing(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setOnlyAlertOnce(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setPriority(int);
+    method public androidx.core.app.NotificationCompat.Builder setProgress(int, int, boolean);
+    method public androidx.core.app.NotificationCompat.Builder setPublicVersion(android.app.Notification?);
+    method public androidx.core.app.NotificationCompat.Builder setRemoteInputHistory(CharSequence![]?);
+    method public androidx.core.app.NotificationCompat.Builder setSettingsText(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setShortcutId(String?);
+    method public androidx.core.app.NotificationCompat.Builder setShortcutInfo(androidx.core.content.pm.ShortcutInfoCompat?);
+    method public androidx.core.app.NotificationCompat.Builder setShowWhen(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setSilent(boolean);
+    method @RequiresApi(23) public androidx.core.app.NotificationCompat.Builder setSmallIcon(androidx.core.graphics.drawable.IconCompat);
+    method public androidx.core.app.NotificationCompat.Builder setSmallIcon(int);
+    method public androidx.core.app.NotificationCompat.Builder setSmallIcon(int, int);
+    method public androidx.core.app.NotificationCompat.Builder setSortKey(String?);
+    method public androidx.core.app.NotificationCompat.Builder setSound(android.net.Uri?);
+    method public androidx.core.app.NotificationCompat.Builder setSound(android.net.Uri?, int);
+    method public androidx.core.app.NotificationCompat.Builder setStyle(androidx.core.app.NotificationCompat.Style?);
+    method public androidx.core.app.NotificationCompat.Builder setSubText(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setTicker(CharSequence?);
+    method @Deprecated public androidx.core.app.NotificationCompat.Builder setTicker(CharSequence?, android.widget.RemoteViews?);
+    method public androidx.core.app.NotificationCompat.Builder setTimeoutAfter(long);
+    method public androidx.core.app.NotificationCompat.Builder setUsesChronometer(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setVibrate(long[]?);
+    method public androidx.core.app.NotificationCompat.Builder setVisibility(int);
+    method public androidx.core.app.NotificationCompat.Builder setWhen(long);
+    field @Deprecated public java.util.ArrayList<java.lang.String!>! mPeople;
+  }
+
+  public static class NotificationCompat.CallStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor public NotificationCompat.CallStyle();
+    ctor public NotificationCompat.CallStyle(androidx.core.app.NotificationCompat.Builder?);
+    method public static androidx.core.app.NotificationCompat.CallStyle forIncomingCall(androidx.core.app.Person, android.app.PendingIntent, android.app.PendingIntent);
+    method public static androidx.core.app.NotificationCompat.CallStyle forOngoingCall(androidx.core.app.Person, android.app.PendingIntent);
+    method public static androidx.core.app.NotificationCompat.CallStyle forScreeningCall(androidx.core.app.Person, android.app.PendingIntent, android.app.PendingIntent);
+    method public androidx.core.app.NotificationCompat.CallStyle setAnswerButtonColorHint(@ColorInt int);
+    method public androidx.core.app.NotificationCompat.CallStyle setDeclineButtonColorHint(@ColorInt int);
+    method public androidx.core.app.NotificationCompat.CallStyle setIsVideo(boolean);
+    method public androidx.core.app.NotificationCompat.CallStyle setVerificationIcon(android.graphics.Bitmap?);
+    method @RequiresApi(23) public androidx.core.app.NotificationCompat.CallStyle setVerificationIcon(android.graphics.drawable.Icon?);
+    method public androidx.core.app.NotificationCompat.CallStyle setVerificationText(CharSequence?);
+    field public static final int CALL_TYPE_INCOMING = 1; // 0x1
+    field public static final int CALL_TYPE_ONGOING = 2; // 0x2
+    field public static final int CALL_TYPE_SCREENING = 3; // 0x3
+    field public static final int CALL_TYPE_UNKNOWN = 0; // 0x0
+  }
+
+  public static final class NotificationCompat.CarExtender implements androidx.core.app.NotificationCompat.Extender {
+    ctor public NotificationCompat.CarExtender();
+    ctor public NotificationCompat.CarExtender(android.app.Notification);
+    method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Builder);
+    method @ColorInt public int getColor();
+    method public android.graphics.Bitmap? getLargeIcon();
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation? getUnreadConversation();
+    method public androidx.core.app.NotificationCompat.CarExtender setColor(@ColorInt int);
+    method public androidx.core.app.NotificationCompat.CarExtender setLargeIcon(android.graphics.Bitmap?);
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender setUnreadConversation(androidx.core.app.NotificationCompat.CarExtender.UnreadConversation?);
+  }
+
+  @Deprecated public static class NotificationCompat.CarExtender.UnreadConversation {
+    method @Deprecated public long getLatestTimestamp();
+    method @Deprecated public String![]? getMessages();
+    method @Deprecated public String? getParticipant();
+    method @Deprecated public String![]? getParticipants();
+    method @Deprecated public android.app.PendingIntent? getReadPendingIntent();
+    method @Deprecated public androidx.core.app.RemoteInput? getRemoteInput();
+    method @Deprecated public android.app.PendingIntent? getReplyPendingIntent();
+  }
+
+  @Deprecated public static class NotificationCompat.CarExtender.UnreadConversation.Builder {
+    ctor @Deprecated public NotificationCompat.CarExtender.UnreadConversation.Builder(String);
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation.Builder addMessage(String?);
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation build();
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation.Builder setLatestTimestamp(long);
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation.Builder setReadPendingIntent(android.app.PendingIntent?);
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation.Builder setReplyAction(android.app.PendingIntent?, androidx.core.app.RemoteInput?);
+  }
+
+  public static class NotificationCompat.DecoratedCustomViewStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor public NotificationCompat.DecoratedCustomViewStyle();
+  }
+
+  public static interface NotificationCompat.Extender {
+    method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Builder);
+  }
+
+  public static class NotificationCompat.InboxStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor public NotificationCompat.InboxStyle();
+    ctor public NotificationCompat.InboxStyle(androidx.core.app.NotificationCompat.Builder?);
+    method public androidx.core.app.NotificationCompat.InboxStyle addLine(CharSequence?);
+    method public androidx.core.app.NotificationCompat.InboxStyle setBigContentTitle(CharSequence?);
+    method public androidx.core.app.NotificationCompat.InboxStyle setSummaryText(CharSequence?);
+  }
+
+  public static class NotificationCompat.MessagingStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor public NotificationCompat.MessagingStyle(androidx.core.app.Person);
+    ctor @Deprecated public NotificationCompat.MessagingStyle(CharSequence);
+    method public void addCompatExtras(android.os.Bundle);
+    method public androidx.core.app.NotificationCompat.MessagingStyle addHistoricMessage(androidx.core.app.NotificationCompat.MessagingStyle.Message?);
+    method public androidx.core.app.NotificationCompat.MessagingStyle addMessage(androidx.core.app.NotificationCompat.MessagingStyle.Message?);
+    method public androidx.core.app.NotificationCompat.MessagingStyle addMessage(CharSequence?, long, androidx.core.app.Person?);
+    method @Deprecated public androidx.core.app.NotificationCompat.MessagingStyle addMessage(CharSequence?, long, CharSequence?);
+    method public static androidx.core.app.NotificationCompat.MessagingStyle? extractMessagingStyleFromNotification(android.app.Notification);
+    method public CharSequence? getConversationTitle();
+    method public java.util.List<androidx.core.app.NotificationCompat.MessagingStyle.Message!> getHistoricMessages();
+    method public java.util.List<androidx.core.app.NotificationCompat.MessagingStyle.Message!> getMessages();
+    method public androidx.core.app.Person getUser();
+    method @Deprecated public CharSequence? getUserDisplayName();
+    method public boolean isGroupConversation();
+    method public androidx.core.app.NotificationCompat.MessagingStyle setConversationTitle(CharSequence?);
+    method public androidx.core.app.NotificationCompat.MessagingStyle setGroupConversation(boolean);
+    field public static final int MAXIMUM_RETAINED_MESSAGES = 25; // 0x19
+  }
+
+  public static final class NotificationCompat.MessagingStyle.Message {
+    ctor public NotificationCompat.MessagingStyle.Message(CharSequence?, long, androidx.core.app.Person?);
+    ctor @Deprecated public NotificationCompat.MessagingStyle.Message(CharSequence?, long, CharSequence?);
+    method public String? getDataMimeType();
+    method public android.net.Uri? getDataUri();
+    method public android.os.Bundle getExtras();
+    method public androidx.core.app.Person? getPerson();
+    method @Deprecated public CharSequence? getSender();
+    method public CharSequence? getText();
+    method public long getTimestamp();
+    method public androidx.core.app.NotificationCompat.MessagingStyle.Message setData(String?, android.net.Uri?);
+  }
+
+  public abstract static class NotificationCompat.Style {
+    ctor public NotificationCompat.Style();
+    method public android.app.Notification? build();
+    method public void setBuilder(androidx.core.app.NotificationCompat.Builder?);
+  }
+
+  public static final class NotificationCompat.TvExtender implements androidx.core.app.NotificationCompat.Extender {
+    ctor public NotificationCompat.TvExtender();
+    ctor public NotificationCompat.TvExtender(android.app.Notification);
+    method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Builder);
+    method public String? getChannelId();
+    method public android.app.PendingIntent? getContentIntent();
+    method public android.app.PendingIntent? getDeleteIntent();
+    method public boolean isAvailableOnTv();
+    method public boolean isSuppressShowOverApps();
+    method public androidx.core.app.NotificationCompat.TvExtender setChannelId(String?);
+    method public androidx.core.app.NotificationCompat.TvExtender setContentIntent(android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.TvExtender setDeleteIntent(android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.TvExtender setSuppressShowOverApps(boolean);
+  }
+
+  public static final class NotificationCompat.WearableExtender implements androidx.core.app.NotificationCompat.Extender {
+    ctor public NotificationCompat.WearableExtender();
+    ctor public NotificationCompat.WearableExtender(android.app.Notification);
+    method public androidx.core.app.NotificationCompat.WearableExtender addAction(androidx.core.app.NotificationCompat.Action);
+    method public androidx.core.app.NotificationCompat.WearableExtender addActions(java.util.List<androidx.core.app.NotificationCompat.Action!>);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender addPage(android.app.Notification);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender addPages(java.util.List<android.app.Notification!>);
+    method public androidx.core.app.NotificationCompat.WearableExtender clearActions();
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender clearPages();
+    method public androidx.core.app.NotificationCompat.WearableExtender clone();
+    method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Builder);
+    method public java.util.List<androidx.core.app.NotificationCompat.Action!> getActions();
+    method @Deprecated public android.graphics.Bitmap? getBackground();
+    method public String? getBridgeTag();
+    method public int getContentAction();
+    method @Deprecated public int getContentIcon();
+    method @Deprecated public int getContentIconGravity();
+    method public boolean getContentIntentAvailableOffline();
+    method @Deprecated public int getCustomContentHeight();
+    method @Deprecated public int getCustomSizePreset();
+    method public String? getDismissalId();
+    method @Deprecated public android.app.PendingIntent? getDisplayIntent();
+    method @Deprecated public int getGravity();
+    method @Deprecated public boolean getHintAmbientBigPicture();
+    method @Deprecated public boolean getHintAvoidBackgroundClipping();
+    method public boolean getHintContentIntentLaunchesActivity();
+    method @Deprecated public boolean getHintHideIcon();
+    method @Deprecated public int getHintScreenTimeout();
+    method @Deprecated public boolean getHintShowBackgroundOnly();
+    method @Deprecated public java.util.List<android.app.Notification!> getPages();
+    method public boolean getStartScrollBottom();
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setBackground(android.graphics.Bitmap?);
+    method public androidx.core.app.NotificationCompat.WearableExtender setBridgeTag(String?);
+    method public androidx.core.app.NotificationCompat.WearableExtender setContentAction(int);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setContentIcon(int);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setContentIconGravity(int);
+    method public androidx.core.app.NotificationCompat.WearableExtender setContentIntentAvailableOffline(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setCustomContentHeight(int);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setCustomSizePreset(int);
+    method public androidx.core.app.NotificationCompat.WearableExtender setDismissalId(String?);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setDisplayIntent(android.app.PendingIntent?);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setGravity(int);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintAmbientBigPicture(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintAvoidBackgroundClipping(boolean);
+    method public androidx.core.app.NotificationCompat.WearableExtender setHintContentIntentLaunchesActivity(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintHideIcon(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintScreenTimeout(int);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintShowBackgroundOnly(boolean);
+    method public androidx.core.app.NotificationCompat.WearableExtender setStartScrollBottom(boolean);
+    field @Deprecated public static final int SCREEN_TIMEOUT_LONG = -1; // 0xffffffff
+    field @Deprecated public static final int SCREEN_TIMEOUT_SHORT = 0; // 0x0
+    field @Deprecated public static final int SIZE_DEFAULT = 0; // 0x0
+    field @Deprecated public static final int SIZE_FULL_SCREEN = 5; // 0x5
+    field @Deprecated public static final int SIZE_LARGE = 4; // 0x4
+    field @Deprecated public static final int SIZE_MEDIUM = 3; // 0x3
+    field @Deprecated public static final int SIZE_SMALL = 2; // 0x2
+    field @Deprecated public static final int SIZE_XSMALL = 1; // 0x1
+    field public static final int UNSET_ACTION_INDEX = -1; // 0xffffffff
+  }
+
+  public final class NotificationCompatExtras {
+    field public static final String EXTRA_ACTION_EXTRAS = "android.support.actionExtras";
+    field public static final String EXTRA_GROUP_KEY = "android.support.groupKey";
+    field public static final String EXTRA_GROUP_SUMMARY = "android.support.isGroupSummary";
+    field public static final String EXTRA_LOCAL_ONLY = "android.support.localOnly";
+    field public static final String EXTRA_REMOTE_INPUTS = "android.support.remoteInputs";
+    field public static final String EXTRA_SORT_KEY = "android.support.sortKey";
+  }
+
+  public abstract class NotificationCompatSideChannelService extends android.app.Service {
+    ctor public NotificationCompatSideChannelService();
+    method public abstract void cancel(String!, int, String!);
+    method public abstract void cancelAll(String!);
+    method public abstract void notify(String!, int, String!, android.app.Notification!);
+    method public android.os.IBinder! onBind(android.content.Intent!);
+  }
+
+  public final class NotificationManagerCompat {
+    method public boolean areNotificationsEnabled();
+    method public boolean canUseFullScreenIntent();
+    method public void cancel(int);
+    method public void cancel(String?, int);
+    method public void cancelAll();
+    method public void createNotificationChannel(android.app.NotificationChannel);
+    method public void createNotificationChannel(androidx.core.app.NotificationChannelCompat);
+    method public void createNotificationChannelGroup(android.app.NotificationChannelGroup);
+    method public void createNotificationChannelGroup(androidx.core.app.NotificationChannelGroupCompat);
+    method public void createNotificationChannelGroups(java.util.List<android.app.NotificationChannelGroup!>);
+    method public void createNotificationChannelGroupsCompat(java.util.List<androidx.core.app.NotificationChannelGroupCompat!>);
+    method public void createNotificationChannels(java.util.List<android.app.NotificationChannel!>);
+    method public void createNotificationChannelsCompat(java.util.List<androidx.core.app.NotificationChannelCompat!>);
+    method public void deleteNotificationChannel(String);
+    method public void deleteNotificationChannelGroup(String);
+    method public void deleteUnlistedNotificationChannels(java.util.Collection<java.lang.String!>);
+    method public static androidx.core.app.NotificationManagerCompat from(android.content.Context);
+    method public java.util.List<android.service.notification.StatusBarNotification!> getActiveNotifications();
+    method public int getCurrentInterruptionFilter();
+    method public static java.util.Set<java.lang.String!> getEnabledListenerPackages(android.content.Context);
+    method public int getImportance();
+    method public android.app.NotificationChannel? getNotificationChannel(String);
+    method public android.app.NotificationChannel? getNotificationChannel(String, String);
+    method public androidx.core.app.NotificationChannelCompat? getNotificationChannelCompat(String);
+    method public androidx.core.app.NotificationChannelCompat? getNotificationChannelCompat(String, String);
+    method public android.app.NotificationChannelGroup? getNotificationChannelGroup(String);
+    method public androidx.core.app.NotificationChannelGroupCompat? getNotificationChannelGroupCompat(String);
+    method public java.util.List<android.app.NotificationChannelGroup!> getNotificationChannelGroups();
+    method public java.util.List<androidx.core.app.NotificationChannelGroupCompat!> getNotificationChannelGroupsCompat();
+    method public java.util.List<android.app.NotificationChannel!> getNotificationChannels();
+    method public java.util.List<androidx.core.app.NotificationChannelCompat!> getNotificationChannelsCompat();
+    method @RequiresPermission(android.Manifest.permission.POST_NOTIFICATIONS) public void notify(int, android.app.Notification);
+    method @RequiresPermission(android.Manifest.permission.POST_NOTIFICATIONS) public void notify(String?, int, android.app.Notification);
+    method @RequiresPermission(android.Manifest.permission.POST_NOTIFICATIONS) public void notify(java.util.List<androidx.core.app.NotificationManagerCompat.NotificationWithIdAndTag!>);
+    field public static final String ACTION_BIND_SIDE_CHANNEL = "android.support.BIND_NOTIFICATION_SIDE_CHANNEL";
+    field public static final String EXTRA_USE_SIDE_CHANNEL = "android.support.useSideChannel";
+    field public static final int IMPORTANCE_DEFAULT = 3; // 0x3
+    field public static final int IMPORTANCE_HIGH = 4; // 0x4
+    field public static final int IMPORTANCE_LOW = 2; // 0x2
+    field public static final int IMPORTANCE_MAX = 5; // 0x5
+    field public static final int IMPORTANCE_MIN = 1; // 0x1
+    field public static final int IMPORTANCE_NONE = 0; // 0x0
+    field public static final int IMPORTANCE_UNSPECIFIED = -1000; // 0xfffffc18
+    field public static final int INTERRUPTION_FILTER_ALARMS = 4; // 0x4
+    field public static final int INTERRUPTION_FILTER_ALL = 1; // 0x1
+    field public static final int INTERRUPTION_FILTER_NONE = 3; // 0x3
+    field public static final int INTERRUPTION_FILTER_PRIORITY = 2; // 0x2
+    field public static final int INTERRUPTION_FILTER_UNKNOWN = 0; // 0x0
+  }
+
+  public static class NotificationManagerCompat.NotificationWithIdAndTag {
+    ctor public NotificationManagerCompat.NotificationWithIdAndTag(int, android.app.Notification);
+    ctor public NotificationManagerCompat.NotificationWithIdAndTag(String?, int, android.app.Notification);
+  }
+
+  public interface OnMultiWindowModeChangedProvider {
+    method public void addOnMultiWindowModeChangedListener(androidx.core.util.Consumer<androidx.core.app.MultiWindowModeChangedInfo!>);
+    method public void removeOnMultiWindowModeChangedListener(androidx.core.util.Consumer<androidx.core.app.MultiWindowModeChangedInfo!>);
+  }
+
+  public interface OnNewIntentProvider {
+    method public void addOnNewIntentListener(androidx.core.util.Consumer<android.content.Intent!>);
+    method public void removeOnNewIntentListener(androidx.core.util.Consumer<android.content.Intent!>);
+  }
+
+  public interface OnPictureInPictureModeChangedProvider {
+    method public void addOnPictureInPictureModeChangedListener(androidx.core.util.Consumer<androidx.core.app.PictureInPictureModeChangedInfo!>);
+    method public void removeOnPictureInPictureModeChangedListener(androidx.core.util.Consumer<androidx.core.app.PictureInPictureModeChangedInfo!>);
+  }
+
+  public final class PendingIntentCompat {
+    method public static android.app.PendingIntent getActivities(android.content.Context, int, android.content.Intent![], int, android.os.Bundle?, boolean);
+    method public static android.app.PendingIntent getActivities(android.content.Context, int, android.content.Intent![], int, boolean);
+    method public static android.app.PendingIntent? getActivity(android.content.Context, int, android.content.Intent, int, android.os.Bundle?, boolean);
+    method public static android.app.PendingIntent? getActivity(android.content.Context, int, android.content.Intent, int, boolean);
+    method public static android.app.PendingIntent? getBroadcast(android.content.Context, int, android.content.Intent, int, boolean);
+    method @RequiresApi(26) public static android.app.PendingIntent getForegroundService(android.content.Context, int, android.content.Intent, int, boolean);
+    method public static android.app.PendingIntent? getService(android.content.Context, int, android.content.Intent, int, boolean);
+    method public static void send(android.app.PendingIntent, android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished?, android.os.Handler?) throws android.app.PendingIntent.CanceledException;
+    method public static void send(android.app.PendingIntent, android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished?, android.os.Handler?, String?, android.os.Bundle?) throws android.app.PendingIntent.CanceledException;
+    method public static void send(android.app.PendingIntent, int, android.app.PendingIntent.OnFinished?, android.os.Handler?) throws android.app.PendingIntent.CanceledException;
+  }
+
+  public class Person {
+    method public static androidx.core.app.Person fromBundle(android.os.Bundle);
+    method public androidx.core.graphics.drawable.IconCompat? getIcon();
+    method public String? getKey();
+    method public CharSequence? getName();
+    method public String? getUri();
+    method public boolean isBot();
+    method public boolean isImportant();
+    method public androidx.core.app.Person.Builder toBuilder();
+    method public android.os.Bundle toBundle();
+  }
+
+  public static class Person.Builder {
+    ctor public Person.Builder();
+    method public androidx.core.app.Person build();
+    method public androidx.core.app.Person.Builder setBot(boolean);
+    method public androidx.core.app.Person.Builder setIcon(androidx.core.graphics.drawable.IconCompat?);
+    method public androidx.core.app.Person.Builder setImportant(boolean);
+    method public androidx.core.app.Person.Builder setKey(String?);
+    method public androidx.core.app.Person.Builder setName(CharSequence?);
+    method public androidx.core.app.Person.Builder setUri(String?);
+  }
+
+  public final class PictureInPictureModeChangedInfo {
+    ctor public PictureInPictureModeChangedInfo(boolean);
+    ctor @RequiresApi(26) public PictureInPictureModeChangedInfo(boolean, android.content.res.Configuration);
+    method @RequiresApi(26) public android.content.res.Configuration getNewConfig();
+    method public boolean isInPictureInPictureMode();
+  }
+
+  public final class RemoteActionCompat implements androidx.versionedparcelable.VersionedParcelable {
+    ctor public RemoteActionCompat(androidx.core.app.RemoteActionCompat);
+    ctor public RemoteActionCompat(androidx.core.graphics.drawable.IconCompat, CharSequence, CharSequence, android.app.PendingIntent);
+    method @RequiresApi(26) public static androidx.core.app.RemoteActionCompat createFromRemoteAction(android.app.RemoteAction);
+    method public android.app.PendingIntent getActionIntent();
+    method public CharSequence getContentDescription();
+    method public androidx.core.graphics.drawable.IconCompat getIcon();
+    method public CharSequence getTitle();
+    method public boolean isEnabled();
+    method public void setEnabled(boolean);
+    method public void setShouldShowIcon(boolean);
+    method public boolean shouldShowIcon();
+    method @RequiresApi(26) public android.app.RemoteAction toRemoteAction();
+  }
+
+  public final class RemoteInput {
+    method public static void addDataResultToIntent(androidx.core.app.RemoteInput, android.content.Intent, java.util.Map<java.lang.String!,android.net.Uri!>);
+    method public static void addResultsToIntent(androidx.core.app.RemoteInput![], android.content.Intent, android.os.Bundle);
+    method public boolean getAllowFreeFormInput();
+    method public java.util.Set<java.lang.String!>? getAllowedDataTypes();
+    method public CharSequence![]? getChoices();
+    method public static java.util.Map<java.lang.String!,android.net.Uri!>? getDataResultsFromIntent(android.content.Intent, String);
+    method public int getEditChoicesBeforeSending();
+    method public android.os.Bundle getExtras();
+    method public CharSequence? getLabel();
+    method public String getResultKey();
+    method public static android.os.Bundle? getResultsFromIntent(android.content.Intent);
+    method public static int getResultsSource(android.content.Intent);
+    method public boolean isDataOnly();
+    method public static void setResultsSource(android.content.Intent, int);
+    field public static final int EDIT_CHOICES_BEFORE_SENDING_AUTO = 0; // 0x0
+    field public static final int EDIT_CHOICES_BEFORE_SENDING_DISABLED = 1; // 0x1
+    field public static final int EDIT_CHOICES_BEFORE_SENDING_ENABLED = 2; // 0x2
+    field public static final String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
+    field public static final String RESULTS_CLIP_LABEL = "android.remoteinput.results";
+    field public static final int SOURCE_CHOICE = 1; // 0x1
+    field public static final int SOURCE_FREE_FORM_INPUT = 0; // 0x0
+  }
+
+  public static final class RemoteInput.Builder {
+    ctor public RemoteInput.Builder(String);
+    method public androidx.core.app.RemoteInput.Builder addExtras(android.os.Bundle);
+    method public androidx.core.app.RemoteInput build();
+    method public android.os.Bundle getExtras();
+    method public androidx.core.app.RemoteInput.Builder setAllowDataType(String, boolean);
+    method public androidx.core.app.RemoteInput.Builder setAllowFreeFormInput(boolean);
+    method public androidx.core.app.RemoteInput.Builder setChoices(CharSequence![]?);
+    method public androidx.core.app.RemoteInput.Builder setEditChoicesBeforeSending(int);
+    method public androidx.core.app.RemoteInput.Builder setLabel(CharSequence?);
+  }
+
+  public final class ServiceCompat {
+    method public static void startForeground(android.app.Service, int, android.app.Notification, int);
+    method public static void stopForeground(android.app.Service, int);
+    field public static final int START_STICKY = 1; // 0x1
+    field public static final int STOP_FOREGROUND_DETACH = 2; // 0x2
+    field public static final int STOP_FOREGROUND_REMOVE = 1; // 0x1
+  }
+
+  public final class ShareCompat {
+    method @Deprecated public static void configureMenuItem(android.view.Menu, @IdRes int, androidx.core.app.ShareCompat.IntentBuilder);
+    method @Deprecated public static void configureMenuItem(android.view.MenuItem, androidx.core.app.ShareCompat.IntentBuilder);
+    method public static android.content.ComponentName? getCallingActivity(android.app.Activity);
+    method public static String? getCallingPackage(android.app.Activity);
+    field public static final String EXTRA_CALLING_ACTIVITY = "androidx.core.app.EXTRA_CALLING_ACTIVITY";
+    field public static final String EXTRA_CALLING_ACTIVITY_INTEROP = "android.support.v4.app.EXTRA_CALLING_ACTIVITY";
+    field public static final String EXTRA_CALLING_PACKAGE = "androidx.core.app.EXTRA_CALLING_PACKAGE";
+    field public static final String EXTRA_CALLING_PACKAGE_INTEROP = "android.support.v4.app.EXTRA_CALLING_PACKAGE";
+  }
+
+  public static class ShareCompat.IntentBuilder {
+    ctor public ShareCompat.IntentBuilder(android.content.Context);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailBcc(String);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailBcc(String![]);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailCc(String);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailCc(String![]);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailTo(String);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailTo(String![]);
+    method public androidx.core.app.ShareCompat.IntentBuilder addStream(android.net.Uri);
+    method public android.content.Intent createChooserIntent();
+    method @Deprecated public static androidx.core.app.ShareCompat.IntentBuilder from(android.app.Activity);
+    method public android.content.Intent getIntent();
+    method public androidx.core.app.ShareCompat.IntentBuilder setChooserTitle(@StringRes int);
+    method public androidx.core.app.ShareCompat.IntentBuilder setChooserTitle(CharSequence?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setEmailBcc(String![]?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setEmailCc(String![]?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setEmailTo(String![]?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setHtmlText(String?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setStream(android.net.Uri?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setSubject(String?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setText(CharSequence?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setType(String?);
+    method public void startChooser();
+  }
+
+  public static class ShareCompat.IntentReader {
+    ctor public ShareCompat.IntentReader(android.app.Activity);
+    ctor public ShareCompat.IntentReader(android.content.Context, android.content.Intent);
+    method @Deprecated public static androidx.core.app.ShareCompat.IntentReader from(android.app.Activity);
+    method public android.content.ComponentName? getCallingActivity();
+    method public android.graphics.drawable.Drawable? getCallingActivityIcon();
+    method public android.graphics.drawable.Drawable? getCallingApplicationIcon();
+    method public CharSequence? getCallingApplicationLabel();
+    method public String? getCallingPackage();
+    method public String![]? getEmailBcc();
+    method public String![]? getEmailCc();
+    method public String![]? getEmailTo();
+    method public String? getHtmlText();
+    method public android.net.Uri? getStream();
+    method public android.net.Uri? getStream(int);
+    method public int getStreamCount();
+    method public String? getSubject();
+    method public CharSequence? getText();
+    method public String? getType();
+    method public boolean isMultipleShare();
+    method public boolean isShareIntent();
+    method public boolean isSingleShare();
+  }
+
+  public abstract class SharedElementCallback {
+    ctor public SharedElementCallback();
+    method public android.os.Parcelable! onCaptureSharedElementSnapshot(android.view.View!, android.graphics.Matrix!, android.graphics.RectF!);
+    method public android.view.View! onCreateSnapshotView(android.content.Context!, android.os.Parcelable!);
+    method public void onMapSharedElements(java.util.List<java.lang.String!>!, java.util.Map<java.lang.String!,android.view.View!>!);
+    method public void onRejectSharedElements(java.util.List<android.view.View!>!);
+    method public void onSharedElementEnd(java.util.List<java.lang.String!>!, java.util.List<android.view.View!>!, java.util.List<android.view.View!>!);
+    method public void onSharedElementStart(java.util.List<java.lang.String!>!, java.util.List<android.view.View!>!, java.util.List<android.view.View!>!);
+    method public void onSharedElementsArrived(java.util.List<java.lang.String!>!, java.util.List<android.view.View!>!, androidx.core.app.SharedElementCallback.OnSharedElementsReadyListener!);
+  }
+
+  public static interface SharedElementCallback.OnSharedElementsReadyListener {
+    method public void onSharedElementsReady();
+  }
+
+  public final class TaskStackBuilder implements java.lang.Iterable<android.content.Intent> {
+    method public androidx.core.app.TaskStackBuilder addNextIntent(android.content.Intent);
+    method public androidx.core.app.TaskStackBuilder addNextIntentWithParentStack(android.content.Intent);
+    method public androidx.core.app.TaskStackBuilder addParentStack(android.app.Activity);
+    method public androidx.core.app.TaskStackBuilder addParentStack(android.content.ComponentName);
+    method public androidx.core.app.TaskStackBuilder addParentStack(Class<?>);
+    method public static androidx.core.app.TaskStackBuilder create(android.content.Context);
+    method public android.content.Intent? editIntentAt(int);
+    method @Deprecated public static androidx.core.app.TaskStackBuilder! from(android.content.Context!);
+    method @Deprecated public android.content.Intent! getIntent(int);
+    method public int getIntentCount();
+    method public android.content.Intent![] getIntents();
+    method public android.app.PendingIntent? getPendingIntent(int, int);
+    method public android.app.PendingIntent? getPendingIntent(int, int, android.os.Bundle?);
+    method @Deprecated public java.util.Iterator<android.content.Intent!> iterator();
+    method public void startActivities();
+    method public void startActivities(android.os.Bundle?);
+  }
+
+  public static interface TaskStackBuilder.SupportParentable {
+    method public android.content.Intent? getSupportParentActivityIntent();
+  }
+
+}
+
+package androidx.core.content {
+
+  public final class ContentProviderCompat {
+    method public static android.content.Context requireContext(android.content.ContentProvider);
+  }
+
+  public final class ContentResolverCompat {
+    method public static android.database.Cursor? query(android.content.ContentResolver, android.net.Uri, String![]?, String?, String![]?, String?, androidx.core.os.CancellationSignal?);
+  }
+
+  public class ContextCompat {
+    ctor protected ContextCompat();
+    method public static int checkSelfPermission(android.content.Context, String);
+    method public static android.content.Context? createDeviceProtectedStorageContext(android.content.Context);
+    method public static String? getAttributionTag(android.content.Context);
+    method public static java.io.File getCodeCacheDir(android.content.Context);
+    method @ColorInt public static int getColor(android.content.Context, @ColorRes int);
+    method public static android.content.res.ColorStateList? getColorStateList(android.content.Context, @ColorRes int);
+    method public static android.content.Context getContextForLanguage(android.content.Context);
+    method public static java.io.File? getDataDir(android.content.Context);
+    method public static android.view.Display getDisplayOrDefault(@DisplayContext android.content.Context);
+    method public static android.graphics.drawable.Drawable? getDrawable(android.content.Context, @DrawableRes int);
+    method public static java.io.File![] getExternalCacheDirs(android.content.Context);
+    method public static java.io.File![] getExternalFilesDirs(android.content.Context, String?);
+    method public static java.util.concurrent.Executor getMainExecutor(android.content.Context);
+    method public static java.io.File? getNoBackupFilesDir(android.content.Context);
+    method public static java.io.File![] getObbDirs(android.content.Context);
+    method public static String getString(android.content.Context, int);
+    method public static <T> T? getSystemService(android.content.Context, Class<T!>);
+    method public static String? getSystemServiceName(android.content.Context, Class<?>);
+    method public static boolean isDeviceProtectedStorage(android.content.Context);
+    method public static android.content.Intent? registerReceiver(android.content.Context, android.content.BroadcastReceiver?, android.content.IntentFilter, int);
+    method public static android.content.Intent? registerReceiver(android.content.Context, android.content.BroadcastReceiver?, android.content.IntentFilter, String?, android.os.Handler?, int);
+    method public static boolean startActivities(android.content.Context, android.content.Intent![]);
+    method public static boolean startActivities(android.content.Context, android.content.Intent![], android.os.Bundle?);
+    method public static void startActivity(android.content.Context, android.content.Intent, android.os.Bundle?);
+    method public static void startForegroundService(android.content.Context, android.content.Intent);
+    field public static final int RECEIVER_EXPORTED = 2; // 0x2
+    field public static final int RECEIVER_NOT_EXPORTED = 4; // 0x4
+    field public static final int RECEIVER_VISIBLE_TO_INSTANT_APPS = 1; // 0x1
+  }
+
+  public class FileProvider extends android.content.ContentProvider {
+    ctor public FileProvider();
+    ctor protected FileProvider(@XmlRes int);
+    method public int delete(android.net.Uri, String?, String![]?);
+    method public String? getType(android.net.Uri);
+    method public static android.net.Uri! getUriForFile(android.content.Context, String, java.io.File);
+    method public static android.net.Uri getUriForFile(android.content.Context, String, java.io.File, String);
+    method public android.net.Uri! insert(android.net.Uri, android.content.ContentValues);
+    method public boolean onCreate();
+    method public android.database.Cursor query(android.net.Uri, String![]?, String?, String![]?, String?);
+    method public int update(android.net.Uri, android.content.ContentValues, String?, String![]?);
+  }
+
+  public final class IntentCompat {
+    method public static android.content.Intent createManageUnusedAppRestrictionsIntent(android.content.Context, String);
+    method public static android.os.Parcelable![]? getParcelableArrayExtra(android.content.Intent, String?, Class<? extends android.os.Parcelable>);
+    method public static <T> java.util.ArrayList<T!>? getParcelableArrayListExtra(android.content.Intent, String?, Class<? extends T>);
+    method public static <T> T? getParcelableExtra(android.content.Intent, String?, Class<T!>);
+    method public static android.content.Intent makeMainSelectorActivity(String, String);
+    field public static final String ACTION_CREATE_REMINDER = "android.intent.action.CREATE_REMINDER";
+    field public static final String CATEGORY_LEANBACK_LAUNCHER = "android.intent.category.LEANBACK_LAUNCHER";
+    field public static final String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
+    field public static final String EXTRA_START_PLAYBACK = "android.intent.extra.START_PLAYBACK";
+    field public static final String EXTRA_TIME = "android.intent.extra.TIME";
+  }
+
+  public class IntentSanitizer {
+    method public android.content.Intent sanitize(android.content.Intent, androidx.core.util.Consumer<java.lang.String!>);
+    method public android.content.Intent sanitizeByFiltering(android.content.Intent);
+    method public android.content.Intent sanitizeByThrowing(android.content.Intent);
+  }
+
+  public static final class IntentSanitizer.Builder {
+    ctor public IntentSanitizer.Builder();
+    method public androidx.core.content.IntentSanitizer.Builder allowAction(androidx.core.util.Predicate<java.lang.String!>);
+    method public androidx.core.content.IntentSanitizer.Builder allowAction(String);
+    method public androidx.core.content.IntentSanitizer.Builder allowAnyComponent();
+    method public androidx.core.content.IntentSanitizer.Builder allowCategory(androidx.core.util.Predicate<java.lang.String!>);
+    method public androidx.core.content.IntentSanitizer.Builder allowCategory(String);
+    method public androidx.core.content.IntentSanitizer.Builder allowClipData(androidx.core.util.Predicate<android.content.ClipData!>);
+    method public androidx.core.content.IntentSanitizer.Builder allowClipDataText();
+    method public androidx.core.content.IntentSanitizer.Builder allowClipDataUri(androidx.core.util.Predicate<android.net.Uri!>);
+    method public androidx.core.content.IntentSanitizer.Builder allowClipDataUriWithAuthority(String);
+    method public androidx.core.content.IntentSanitizer.Builder allowComponent(android.content.ComponentName);
+    method public androidx.core.content.IntentSanitizer.Builder allowComponent(androidx.core.util.Predicate<android.content.ComponentName!>);
+    method public androidx.core.content.IntentSanitizer.Builder allowComponentWithPackage(String);
+    method public androidx.core.content.IntentSanitizer.Builder allowData(androidx.core.util.Predicate<android.net.Uri!>);
+    method public androidx.core.content.IntentSanitizer.Builder allowDataWithAuthority(String);
+    method public androidx.core.content.IntentSanitizer.Builder allowExtra(String, androidx.core.util.Predicate<java.lang.Object!>);
+    method public androidx.core.content.IntentSanitizer.Builder allowExtra(String, Class<?>);
+    method public <T> androidx.core.content.IntentSanitizer.Builder allowExtra(String, Class<T!>, androidx.core.util.Predicate<T!>);
+    method public androidx.core.content.IntentSanitizer.Builder allowExtraOutput(androidx.core.util.Predicate<android.net.Uri!>);
+    method public androidx.core.content.IntentSanitizer.Builder allowExtraOutput(String);
+    method public androidx.core.content.IntentSanitizer.Builder allowExtraStream(androidx.core.util.Predicate<android.net.Uri!>);
+    method public androidx.core.content.IntentSanitizer.Builder allowExtraStreamUriWithAuthority(String);
+    method public androidx.core.content.IntentSanitizer.Builder allowFlags(int);
+    method public androidx.core.content.IntentSanitizer.Builder allowHistoryStackFlags();
+    method public androidx.core.content.IntentSanitizer.Builder allowIdentifier();
+    method public androidx.core.content.IntentSanitizer.Builder allowPackage(androidx.core.util.Predicate<java.lang.String!>);
+    method public androidx.core.content.IntentSanitizer.Builder allowPackage(String);
+    method public androidx.core.content.IntentSanitizer.Builder allowReceiverFlags();
+    method public androidx.core.content.IntentSanitizer.Builder allowSelector();
+    method public androidx.core.content.IntentSanitizer.Builder allowSourceBounds();
+    method public androidx.core.content.IntentSanitizer.Builder allowType(androidx.core.util.Predicate<java.lang.String!>);
+    method public androidx.core.content.IntentSanitizer.Builder allowType(String);
+    method public androidx.core.content.IntentSanitizer build();
+  }
+
+  public final class LocusIdCompat {
+    ctor public LocusIdCompat(String);
+    method public String getId();
+    method @RequiresApi(29) public android.content.LocusId toLocusId();
+    method @RequiresApi(29) public static androidx.core.content.LocusIdCompat toLocusIdCompat(android.content.LocusId);
+  }
+
+  public final class MimeTypeFilter {
+    method public static boolean matches(String?, String);
+    method public static String? matches(String?, String![]);
+    method public static String? matches(String![]?, String);
+    method public static String![] matchesMany(String![]?, String);
+  }
+
+  public interface OnConfigurationChangedProvider {
+    method public void addOnConfigurationChangedListener(androidx.core.util.Consumer<android.content.res.Configuration!>);
+    method public void removeOnConfigurationChangedListener(androidx.core.util.Consumer<android.content.res.Configuration!>);
+  }
+
+  public interface OnTrimMemoryProvider {
+    method public void addOnTrimMemoryListener(androidx.core.util.Consumer<java.lang.Integer!>);
+    method public void removeOnTrimMemoryListener(androidx.core.util.Consumer<java.lang.Integer!>);
+  }
+
+  public final class PackageManagerCompat {
+    method public static com.google.common.util.concurrent.ListenableFuture<java.lang.Integer!> getUnusedAppRestrictionsStatus(android.content.Context);
+    field public static final String ACTION_PERMISSION_REVOCATION_SETTINGS = "android.intent.action.AUTO_REVOKE_PERMISSIONS";
+  }
+
+  public final class PermissionChecker {
+    method public static int checkCallingOrSelfPermission(android.content.Context, String);
+    method public static int checkCallingPermission(android.content.Context, String, String?);
+    method public static int checkPermission(android.content.Context, String, int, int, String?);
+    method public static int checkSelfPermission(android.content.Context, String);
+    field public static final int PERMISSION_DENIED = -1; // 0xffffffff
+    field public static final int PERMISSION_DENIED_APP_OP = -2; // 0xfffffffe
+    field public static final int PERMISSION_GRANTED = 0; // 0x0
+  }
+
+  @Deprecated public final class SharedPreferencesCompat {
+  }
+
+  @Deprecated public static final class SharedPreferencesCompat.EditorCompat {
+    method @Deprecated public void apply(android.content.SharedPreferences.Editor);
+    method @Deprecated public static androidx.core.content.SharedPreferencesCompat.EditorCompat! getInstance();
+  }
+
+  public class UnusedAppRestrictionsBackportCallback {
+    method public void onResult(boolean, boolean) throws android.os.RemoteException;
+  }
+
+  public abstract class UnusedAppRestrictionsBackportService extends android.app.Service {
+    ctor public UnusedAppRestrictionsBackportService();
+    method protected abstract void isPermissionRevocationEnabled(androidx.core.content.UnusedAppRestrictionsBackportCallback);
+    method public android.os.IBinder? onBind(android.content.Intent?);
+    field public static final String ACTION_UNUSED_APP_RESTRICTIONS_BACKPORT_CONNECTION = "android.support.unusedapprestrictions.action.CustomUnusedAppRestrictionsBackportService";
+  }
+
+  public final class UnusedAppRestrictionsConstants {
+    field public static final int API_30 = 4; // 0x4
+    field public static final int API_30_BACKPORT = 3; // 0x3
+    field public static final int API_31 = 5; // 0x5
+    field public static final int DISABLED = 2; // 0x2
+    field public static final int ERROR = 0; // 0x0
+    field public static final int FEATURE_NOT_AVAILABLE = 1; // 0x1
+  }
+
+  public class UriMatcherCompat {
+    method public static androidx.core.util.Predicate<android.net.Uri!> asPredicate(android.content.UriMatcher);
+  }
+
+}
+
+package androidx.core.content.pm {
+
+  @Deprecated public final class ActivityInfoCompat {
+    field @Deprecated public static final int CONFIG_UI_MODE = 512; // 0x200
+  }
+
+  public final class PackageInfoCompat {
+    method public static long getLongVersionCode(android.content.pm.PackageInfo);
+    method public static java.util.List<android.content.pm.Signature!> getSignatures(android.content.pm.PackageManager, String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static boolean hasSignatures(android.content.pm.PackageManager, String, @Size(min=1) java.util.Map<byte[]!,java.lang.Integer!>, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
+  }
+
+  public final class PermissionInfoCompat {
+    method public static int getProtection(android.content.pm.PermissionInfo);
+    method public static int getProtectionFlags(android.content.pm.PermissionInfo);
+  }
+
+  public class ShortcutInfoCompat {
+    method public android.content.ComponentName? getActivity();
+    method public java.util.Set<java.lang.String!>? getCategories();
+    method public CharSequence? getDisabledMessage();
+    method public int getDisabledReason();
+    method public int getExcludedFromSurfaces();
+    method public android.os.PersistableBundle? getExtras();
+    method public String getId();
+    method public android.content.Intent getIntent();
+    method public android.content.Intent![] getIntents();
+    method public long getLastChangedTimestamp();
+    method public androidx.core.content.LocusIdCompat? getLocusId();
+    method public CharSequence? getLongLabel();
+    method public String getPackage();
+    method public int getRank();
+    method public CharSequence getShortLabel();
+    method public android.os.UserHandle? getUserHandle();
+    method public boolean hasKeyFieldsOnly();
+    method public boolean isCached();
+    method public boolean isDeclaredInManifest();
+    method public boolean isDynamic();
+    method public boolean isEnabled();
+    method public boolean isExcludedFromSurfaces(int);
+    method public boolean isImmutable();
+    method public boolean isPinned();
+    method @RequiresApi(25) public android.content.pm.ShortcutInfo! toShortcutInfo();
+    field public static final int SURFACE_LAUNCHER = 1; // 0x1
+  }
+
+  public static class ShortcutInfoCompat.Builder {
+    ctor public ShortcutInfoCompat.Builder(android.content.Context, String);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder addCapabilityBinding(String);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder addCapabilityBinding(String, String, java.util.List<java.lang.String!>);
+    method public androidx.core.content.pm.ShortcutInfoCompat build();
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setActivity(android.content.ComponentName);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setAlwaysBadged();
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setCategories(java.util.Set<java.lang.String!>);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setDisabledMessage(CharSequence);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setExcludedFromSurfaces(int);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setExtras(android.os.PersistableBundle);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIcon(androidx.core.graphics.drawable.IconCompat!);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIntent(android.content.Intent);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIntents(android.content.Intent![]);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIsConversation();
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLocusId(androidx.core.content.LocusIdCompat?);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLabel(CharSequence);
+    method @Deprecated public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLived();
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLived(boolean);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setPerson(androidx.core.app.Person);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setPersons(androidx.core.app.Person![]);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setRank(int);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setShortLabel(CharSequence);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setSliceUri(android.net.Uri);
+  }
+
+  public class ShortcutManagerCompat {
+    method public static boolean addDynamicShortcuts(android.content.Context, java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>);
+    method public static android.content.Intent createShortcutResultIntent(android.content.Context, androidx.core.content.pm.ShortcutInfoCompat);
+    method public static void disableShortcuts(android.content.Context, java.util.List<java.lang.String!>, CharSequence?);
+    method public static void enableShortcuts(android.content.Context, java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>);
+    method public static java.util.List<androidx.core.content.pm.ShortcutInfoCompat!> getDynamicShortcuts(android.content.Context);
+    method public static int getIconMaxHeight(android.content.Context);
+    method public static int getIconMaxWidth(android.content.Context);
+    method public static int getMaxShortcutCountPerActivity(android.content.Context);
+    method public static java.util.List<androidx.core.content.pm.ShortcutInfoCompat!> getShortcuts(android.content.Context, int);
+    method public static boolean isRateLimitingActive(android.content.Context);
+    method public static boolean isRequestPinShortcutSupported(android.content.Context);
+    method public static boolean pushDynamicShortcut(android.content.Context, androidx.core.content.pm.ShortcutInfoCompat);
+    method public static void removeAllDynamicShortcuts(android.content.Context);
+    method public static void removeDynamicShortcuts(android.content.Context, java.util.List<java.lang.String!>);
+    method public static void removeLongLivedShortcuts(android.content.Context, java.util.List<java.lang.String!>);
+    method public static void reportShortcutUsed(android.content.Context, String);
+    method public static boolean requestPinShortcut(android.content.Context, androidx.core.content.pm.ShortcutInfoCompat, android.content.IntentSender?);
+    method public static boolean setDynamicShortcuts(android.content.Context, java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>);
+    method public static boolean updateShortcuts(android.content.Context, java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>);
+    field public static final String EXTRA_SHORTCUT_ID = "android.intent.extra.shortcut.ID";
+    field public static final int FLAG_MATCH_CACHED = 8; // 0x8
+    field public static final int FLAG_MATCH_DYNAMIC = 2; // 0x2
+    field public static final int FLAG_MATCH_MANIFEST = 1; // 0x1
+    field public static final int FLAG_MATCH_PINNED = 4; // 0x4
+  }
+
+}
+
+package androidx.core.content.res {
+
+  public final class ConfigurationHelper {
+    method public static int getDensityDpi(android.content.res.Resources);
+  }
+
+  public final class ResourcesCompat {
+    method public static void clearCachesForTheme(android.content.res.Resources.Theme);
+    method public static android.graphics.Typeface? getCachedFont(android.content.Context, @FontRes int) throws android.content.res.Resources.NotFoundException;
+    method @ColorInt public static int getColor(android.content.res.Resources, @ColorRes int, android.content.res.Resources.Theme?) throws android.content.res.Resources.NotFoundException;
+    method public static android.content.res.ColorStateList? getColorStateList(android.content.res.Resources, @ColorRes int, android.content.res.Resources.Theme?) throws android.content.res.Resources.NotFoundException;
+    method public static android.graphics.drawable.Drawable? getDrawable(android.content.res.Resources, @DrawableRes int, android.content.res.Resources.Theme?) throws android.content.res.Resources.NotFoundException;
+    method public static android.graphics.drawable.Drawable? getDrawableForDensity(android.content.res.Resources, @DrawableRes int, int, android.content.res.Resources.Theme?) throws android.content.res.Resources.NotFoundException;
+    method public static float getFloat(android.content.res.Resources, @DimenRes int);
+    method public static android.graphics.Typeface? getFont(android.content.Context, @FontRes int) throws android.content.res.Resources.NotFoundException;
+    method public static void getFont(android.content.Context, @FontRes int, androidx.core.content.res.ResourcesCompat.FontCallback, android.os.Handler?) throws android.content.res.Resources.NotFoundException;
+    field @AnyRes public static final int ID_NULL = 0; // 0x0
+  }
+
+  public abstract static class ResourcesCompat.FontCallback {
+    ctor public ResourcesCompat.FontCallback();
+    method public abstract void onFontRetrievalFailed(int);
+    method public abstract void onFontRetrieved(android.graphics.Typeface);
+  }
+
+  public static final class ResourcesCompat.ThemeCompat {
+    method public static void rebase(android.content.res.Resources.Theme);
+  }
+
+}
+
+package androidx.core.database {
+
+  public final class CursorWindowCompat {
+    method public static android.database.CursorWindow create(String?, long);
+  }
+
+  @Deprecated public final class DatabaseUtilsCompat {
+    method @Deprecated public static String![]! appendSelectionArgs(String![]!, String![]!);
+    method @Deprecated public static String! concatenateWhere(String!, String!);
+  }
+
+}
+
+package androidx.core.database.sqlite {
+
+  public final class SQLiteCursorCompat {
+    method public static void setFillWindowForwardOnly(android.database.sqlite.SQLiteCursor, boolean);
+  }
+
+}
+
+package androidx.core.graphics {
+
+  public final class BitmapCompat {
+    method public static android.graphics.Bitmap createScaledBitmap(android.graphics.Bitmap, int, int, android.graphics.Rect?, boolean);
+    method public static int getAllocationByteCount(android.graphics.Bitmap);
+    method public static boolean hasMipMap(android.graphics.Bitmap);
+    method public static void setHasMipMap(android.graphics.Bitmap, boolean);
+  }
+
+  public class BlendModeColorFilterCompat {
+    method public static android.graphics.ColorFilter? createBlendModeColorFilterCompat(int, androidx.core.graphics.BlendModeCompat);
+  }
+
+  public enum BlendModeCompat {
+    enum_constant public static final androidx.core.graphics.BlendModeCompat CLEAR;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat COLOR;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat COLOR_BURN;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat COLOR_DODGE;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DARKEN;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat DIFFERENCE;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DST;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DST_ATOP;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DST_IN;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DST_OUT;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DST_OVER;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat EXCLUSION;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat HARD_LIGHT;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat HUE;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat LIGHTEN;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat LUMINOSITY;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat MODULATE;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat MULTIPLY;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat OVERLAY;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat PLUS;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat SATURATION;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SCREEN;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat SOFT_LIGHT;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SRC;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SRC_ATOP;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SRC_IN;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SRC_OUT;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SRC_OVER;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat XOR;
+  }
+
+  public final class ColorUtils {
+    method @ColorInt public static int HSLToColor(float[]);
+    method @ColorInt public static int LABToColor(@FloatRange(from=0.0f, to=100) double, @FloatRange(from=0xffffff80, to=127) double, @FloatRange(from=0xffffff80, to=127) double);
+    method public static void LABToXYZ(@FloatRange(from=0.0f, to=100) double, @FloatRange(from=0xffffff80, to=127) double, @FloatRange(from=0xffffff80, to=127) double, double[]);
+    method @ColorInt public static int M3HCTToColor(@FloatRange(from=0.0, to=360, toInclusive=false) float, @FloatRange(from=0.0, to=java.lang.Double.POSITIVE_INFINITY, toInclusive=false) float, @FloatRange(from=0.0, to=100) float);
+    method public static void RGBToHSL(@IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, float[]);
+    method public static void RGBToLAB(@IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, double[]);
+    method public static void RGBToXYZ(@IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, double[]);
+    method @ColorInt public static int XYZToColor(@FloatRange(from=0.0f, to=95.047) double, @FloatRange(from=0.0f, to=0x64) double, @FloatRange(from=0.0f, to=108.883) double);
+    method public static void XYZToLAB(@FloatRange(from=0.0f, to=95.047) double, @FloatRange(from=0.0f, to=0x64) double, @FloatRange(from=0.0f, to=108.883) double, double[]);
+    method @ColorInt public static int blendARGB(@ColorInt int, @ColorInt int, @FloatRange(from=0.0, to=1.0) float);
+    method public static void blendHSL(float[], float[], @FloatRange(from=0.0, to=1.0) float, float[]);
+    method public static void blendLAB(double[], double[], @FloatRange(from=0.0, to=1.0) double, double[]);
+    method public static double calculateContrast(@ColorInt int, @ColorInt int);
+    method @FloatRange(from=0.0, to=1.0) public static double calculateLuminance(@ColorInt int);
+    method public static int calculateMinimumAlpha(@ColorInt int, @ColorInt int, float);
+    method public static void colorToHSL(@ColorInt int, float[]);
+    method public static void colorToLAB(@ColorInt int, double[]);
+    method public static void colorToM3HCT(@ColorInt int, @Size(3) float[]);
+    method public static void colorToXYZ(@ColorInt int, double[]);
+    method @RequiresApi(26) public static android.graphics.Color compositeColors(android.graphics.Color, android.graphics.Color);
+    method public static int compositeColors(@ColorInt int, @ColorInt int);
+    method public static double distanceEuclidean(double[], double[]);
+    method @ColorInt public static int setAlphaComponent(@ColorInt int, @IntRange(from=0, to=255) int);
+  }
+
+  public final class Insets {
+    method public static androidx.core.graphics.Insets add(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+    method public static androidx.core.graphics.Insets max(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+    method public static androidx.core.graphics.Insets min(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+    method public static androidx.core.graphics.Insets of(android.graphics.Rect);
+    method public static androidx.core.graphics.Insets of(int, int, int, int);
+    method public static androidx.core.graphics.Insets subtract(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+    method @RequiresApi(api=29) public static androidx.core.graphics.Insets toCompatInsets(android.graphics.Insets);
+    method @RequiresApi(29) public android.graphics.Insets toPlatformInsets();
+    field public static final androidx.core.graphics.Insets NONE;
+    field public final int bottom;
+    field public final int left;
+    field public final int right;
+    field public final int top;
+  }
+
+  public final class PaintCompat {
+    method public static boolean hasGlyph(android.graphics.Paint, String);
+    method public static boolean setBlendMode(android.graphics.Paint, androidx.core.graphics.BlendModeCompat?);
+  }
+
+  public final class PathSegment {
+    ctor public PathSegment(android.graphics.PointF, float, android.graphics.PointF, float);
+    method public android.graphics.PointF getEnd();
+    method public float getEndFraction();
+    method public android.graphics.PointF getStart();
+    method public float getStartFraction();
+  }
+
+  public final class PathUtils {
+    method @RequiresApi(26) public static java.util.Collection<androidx.core.graphics.PathSegment!> flatten(android.graphics.Path);
+    method @RequiresApi(26) public static java.util.Collection<androidx.core.graphics.PathSegment!> flatten(android.graphics.Path, @FloatRange(from=0) float);
+  }
+
+  public class TypefaceCompat {
+    method public static android.graphics.Typeface create(android.content.Context, android.graphics.Typeface?, int);
+    method public static android.graphics.Typeface create(android.content.Context, android.graphics.Typeface?, @IntRange(from=1, to=1000) int, boolean);
+  }
+
+}
+
+package androidx.core.graphics.drawable {
+
+  public final class DrawableCompat {
+    method public static void applyTheme(android.graphics.drawable.Drawable, android.content.res.Resources.Theme);
+    method public static boolean canApplyTheme(android.graphics.drawable.Drawable);
+    method public static void clearColorFilter(android.graphics.drawable.Drawable);
+    method public static int getAlpha(android.graphics.drawable.Drawable);
+    method public static android.graphics.ColorFilter? getColorFilter(android.graphics.drawable.Drawable);
+    method public static int getLayoutDirection(android.graphics.drawable.Drawable);
+    method public static void inflate(android.graphics.drawable.Drawable, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static boolean isAutoMirrored(android.graphics.drawable.Drawable);
+    method @Deprecated public static void jumpToCurrentState(android.graphics.drawable.Drawable);
+    method public static void setAutoMirrored(android.graphics.drawable.Drawable, boolean);
+    method public static void setHotspot(android.graphics.drawable.Drawable, float, float);
+    method public static void setHotspotBounds(android.graphics.drawable.Drawable, int, int, int, int);
+    method public static boolean setLayoutDirection(android.graphics.drawable.Drawable, int);
+    method public static void setTint(android.graphics.drawable.Drawable, @ColorInt int);
+    method public static void setTintList(android.graphics.drawable.Drawable, android.content.res.ColorStateList?);
+    method public static void setTintMode(android.graphics.drawable.Drawable, android.graphics.PorterDuff.Mode?);
+    method public static <T extends android.graphics.drawable.Drawable> T! unwrap(android.graphics.drawable.Drawable);
+    method public static android.graphics.drawable.Drawable wrap(android.graphics.drawable.Drawable);
+  }
+
+  public class IconCompat implements androidx.versionedparcelable.VersionedParcelable {
+    method public static androidx.core.graphics.drawable.IconCompat? createFromBundle(android.os.Bundle);
+    method @RequiresApi(23) public static androidx.core.graphics.drawable.IconCompat? createFromIcon(android.content.Context, android.graphics.drawable.Icon);
+    method public static androidx.core.graphics.drawable.IconCompat createWithAdaptiveBitmap(android.graphics.Bitmap);
+    method public static androidx.core.graphics.drawable.IconCompat createWithAdaptiveBitmapContentUri(android.net.Uri);
+    method public static androidx.core.graphics.drawable.IconCompat createWithAdaptiveBitmapContentUri(String);
+    method public static androidx.core.graphics.drawable.IconCompat createWithBitmap(android.graphics.Bitmap);
+    method public static androidx.core.graphics.drawable.IconCompat createWithContentUri(android.net.Uri);
+    method public static androidx.core.graphics.drawable.IconCompat createWithContentUri(String);
+    method public static androidx.core.graphics.drawable.IconCompat createWithData(byte[], int, int);
+    method public static androidx.core.graphics.drawable.IconCompat createWithResource(android.content.Context, @DrawableRes int);
+    method @DrawableRes public int getResId();
+    method public String getResPackage();
+    method public int getType();
+    method public android.net.Uri getUri();
+    method public android.graphics.drawable.Drawable? loadDrawable(android.content.Context);
+    method public void onPostParceling();
+    method public void onPreParceling(boolean);
+    method public androidx.core.graphics.drawable.IconCompat setTint(@ColorInt int);
+    method public androidx.core.graphics.drawable.IconCompat setTintList(android.content.res.ColorStateList?);
+    method public androidx.core.graphics.drawable.IconCompat setTintMode(android.graphics.PorterDuff.Mode?);
+    method public android.os.Bundle toBundle();
+    method @Deprecated @RequiresApi(23) public android.graphics.drawable.Icon toIcon();
+    method @RequiresApi(23) public android.graphics.drawable.Icon toIcon(android.content.Context?);
+    field public static final int TYPE_ADAPTIVE_BITMAP = 5; // 0x5
+    field public static final int TYPE_BITMAP = 1; // 0x1
+    field public static final int TYPE_DATA = 3; // 0x3
+    field public static final int TYPE_RESOURCE = 2; // 0x2
+    field public static final int TYPE_UNKNOWN = -1; // 0xffffffff
+    field public static final int TYPE_URI = 4; // 0x4
+    field public static final int TYPE_URI_ADAPTIVE_BITMAP = 6; // 0x6
+  }
+
+  public abstract class RoundedBitmapDrawable extends android.graphics.drawable.Drawable {
+    method public void draw(android.graphics.Canvas);
+    method public final android.graphics.Bitmap? getBitmap();
+    method public float getCornerRadius();
+    method public int getGravity();
+    method public int getOpacity();
+    method public final android.graphics.Paint getPaint();
+    method public boolean hasAntiAlias();
+    method public boolean hasMipMap();
+    method public boolean isCircular();
+    method public void setAlpha(int);
+    method public void setAntiAlias(boolean);
+    method public void setCircular(boolean);
+    method public void setColorFilter(android.graphics.ColorFilter!);
+    method public void setCornerRadius(float);
+    method public void setDither(boolean);
+    method public void setGravity(int);
+    method public void setMipMap(boolean);
+    method public void setTargetDensity(android.graphics.Canvas);
+    method public void setTargetDensity(android.util.DisplayMetrics);
+    method public void setTargetDensity(int);
+  }
+
+  public final class RoundedBitmapDrawableFactory {
+    method public static androidx.core.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, android.graphics.Bitmap?);
+    method public static androidx.core.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, java.io.InputStream);
+    method public static androidx.core.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, String);
+  }
+
+}
+
+package androidx.core.hardware.display {
+
+  public final class DisplayManagerCompat {
+    method public android.view.Display? getDisplay(int);
+    method public android.view.Display![] getDisplays();
+    method public android.view.Display![] getDisplays(String?);
+    method public static androidx.core.hardware.display.DisplayManagerCompat getInstance(android.content.Context);
+    field public static final String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
+  }
+
+}
+
+package androidx.core.hardware.fingerprint {
+
+  @Deprecated public class FingerprintManagerCompat {
+    method @Deprecated @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) public void authenticate(androidx.core.hardware.fingerprint.FingerprintManagerCompat.CryptoObject?, int, androidx.core.os.CancellationSignal?, androidx.core.hardware.fingerprint.FingerprintManagerCompat.AuthenticationCallback, android.os.Handler?);
+    method @Deprecated public static androidx.core.hardware.fingerprint.FingerprintManagerCompat from(android.content.Context);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) public boolean hasEnrolledFingerprints();
+    method @Deprecated @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) public boolean isHardwareDetected();
+  }
+
+  @Deprecated public abstract static class FingerprintManagerCompat.AuthenticationCallback {
+    ctor @Deprecated public FingerprintManagerCompat.AuthenticationCallback();
+    method @Deprecated public void onAuthenticationError(int, CharSequence!);
+    method @Deprecated public void onAuthenticationFailed();
+    method @Deprecated public void onAuthenticationHelp(int, CharSequence!);
+    method @Deprecated public void onAuthenticationSucceeded(androidx.core.hardware.fingerprint.FingerprintManagerCompat.AuthenticationResult!);
+  }
+
+  @Deprecated public static final class FingerprintManagerCompat.AuthenticationResult {
+    ctor @Deprecated public FingerprintManagerCompat.AuthenticationResult(androidx.core.hardware.fingerprint.FingerprintManagerCompat.CryptoObject!);
+    method @Deprecated public androidx.core.hardware.fingerprint.FingerprintManagerCompat.CryptoObject! getCryptoObject();
+  }
+
+  @Deprecated public static class FingerprintManagerCompat.CryptoObject {
+    ctor @Deprecated public FingerprintManagerCompat.CryptoObject(java.security.Signature);
+    ctor @Deprecated public FingerprintManagerCompat.CryptoObject(javax.crypto.Cipher);
+    ctor @Deprecated public FingerprintManagerCompat.CryptoObject(javax.crypto.Mac);
+    method @Deprecated public javax.crypto.Cipher? getCipher();
+    method @Deprecated public javax.crypto.Mac? getMac();
+    method @Deprecated public java.security.Signature? getSignature();
+  }
+
+}
+
+package androidx.core.location {
+
+  public abstract class GnssStatusCompat {
+    method @FloatRange(from=0, to=360) public abstract float getAzimuthDegrees(@IntRange(from=0) int);
+    method @FloatRange(from=0, to=63) public abstract float getBasebandCn0DbHz(@IntRange(from=0) int);
+    method @FloatRange(from=0) public abstract float getCarrierFrequencyHz(@IntRange(from=0) int);
+    method @FloatRange(from=0, to=63) public abstract float getCn0DbHz(@IntRange(from=0) int);
+    method public abstract int getConstellationType(@IntRange(from=0) int);
+    method @FloatRange(from=0xffffffa6, to=90) public abstract float getElevationDegrees(@IntRange(from=0) int);
+    method @IntRange(from=0) public abstract int getSatelliteCount();
+    method @IntRange(from=1, to=200) public abstract int getSvid(@IntRange(from=0) int);
+    method public abstract boolean hasAlmanacData(@IntRange(from=0) int);
+    method public abstract boolean hasBasebandCn0DbHz(@IntRange(from=0) int);
+    method public abstract boolean hasCarrierFrequencyHz(@IntRange(from=0) int);
+    method public abstract boolean hasEphemerisData(@IntRange(from=0) int);
+    method public abstract boolean usedInFix(@IntRange(from=0) int);
+    method @RequiresApi(android.os.Build.VERSION_CODES.N) public static androidx.core.location.GnssStatusCompat wrap(android.location.GnssStatus);
+    method public static androidx.core.location.GnssStatusCompat wrap(android.location.GpsStatus);
+    field public static final int CONSTELLATION_BEIDOU = 5; // 0x5
+    field public static final int CONSTELLATION_GALILEO = 6; // 0x6
+    field public static final int CONSTELLATION_GLONASS = 3; // 0x3
+    field public static final int CONSTELLATION_GPS = 1; // 0x1
+    field public static final int CONSTELLATION_IRNSS = 7; // 0x7
+    field public static final int CONSTELLATION_QZSS = 4; // 0x4
+    field public static final int CONSTELLATION_SBAS = 2; // 0x2
+    field public static final int CONSTELLATION_UNKNOWN = 0; // 0x0
+  }
+
+  public abstract static class GnssStatusCompat.Callback {
+    ctor public GnssStatusCompat.Callback();
+    method public void onFirstFix(@IntRange(from=0) int);
+    method public void onSatelliteStatusChanged(androidx.core.location.GnssStatusCompat);
+    method public void onStarted();
+    method public void onStopped();
+  }
+
+  public final class LocationCompat {
+    method public static float getBearingAccuracyDegrees(android.location.Location);
+    method public static long getElapsedRealtimeMillis(android.location.Location);
+    method public static long getElapsedRealtimeNanos(android.location.Location);
+    method @FloatRange(from=0.0) public static float getMslAltitudeAccuracyMeters(android.location.Location);
+    method public static double getMslAltitudeMeters(android.location.Location);
+    method public static float getSpeedAccuracyMetersPerSecond(android.location.Location);
+    method public static float getVerticalAccuracyMeters(android.location.Location);
+    method public static boolean hasBearingAccuracy(android.location.Location);
+    method public static boolean hasMslAltitude(android.location.Location);
+    method public static boolean hasMslAltitudeAccuracy(android.location.Location);
+    method public static boolean hasSpeedAccuracy(android.location.Location);
+    method public static boolean hasVerticalAccuracy(android.location.Location);
+    method public static boolean isMock(android.location.Location);
+    method public static void removeBearingAccuracy(android.location.Location);
+    method public static void removeMslAltitude(android.location.Location);
+    method public static void removeMslAltitudeAccuracy(android.location.Location);
+    method public static void removeSpeedAccuracy(android.location.Location);
+    method public static void removeVerticalAccuracy(android.location.Location);
+    method public static void setBearingAccuracyDegrees(android.location.Location, float);
+    method public static void setMock(android.location.Location, boolean);
+    method public static void setMslAltitudeAccuracyMeters(android.location.Location, @FloatRange(from=0.0) float);
+    method public static void setMslAltitudeMeters(android.location.Location, double);
+    method public static void setSpeedAccuracyMetersPerSecond(android.location.Location, float);
+    method public static void setVerticalAccuracyMeters(android.location.Location, float);
+    field public static final String EXTRA_BEARING_ACCURACY = "bearingAccuracy";
+    field public static final String EXTRA_IS_MOCK = "mockLocation";
+    field public static final String EXTRA_MSL_ALTITUDE = "androidx.core.location.extra.MSL_ALTITUDE";
+    field public static final String EXTRA_MSL_ALTITUDE_ACCURACY = "androidx.core.location.extra.MSL_ALTITUDE_ACCURACY";
+    field public static final String EXTRA_SPEED_ACCURACY = "speedAccuracy";
+    field public static final String EXTRA_VERTICAL_ACCURACY = "verticalAccuracy";
+  }
+
+  public interface LocationListenerCompat extends android.location.LocationListener {
+    method public default void onStatusChanged(String, int, android.os.Bundle?);
+  }
+
+  public final class LocationManagerCompat {
+    method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static void getCurrentLocation(android.location.LocationManager, String, androidx.core.os.CancellationSignal?, java.util.concurrent.Executor, androidx.core.util.Consumer<android.location.Location!>);
+    method public static String? getGnssHardwareModelName(android.location.LocationManager);
+    method public static int getGnssYearOfHardware(android.location.LocationManager);
+    method public static boolean hasProvider(android.location.LocationManager, String);
+    method public static boolean isLocationEnabled(android.location.LocationManager);
+    method @RequiresApi(android.os.Build.VERSION_CODES.N) @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static boolean registerGnssMeasurementsCallback(android.location.LocationManager, android.location.GnssMeasurementsEvent.Callback, android.os.Handler);
+    method @RequiresApi(android.os.Build.VERSION_CODES.N) @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static boolean registerGnssMeasurementsCallback(android.location.LocationManager, java.util.concurrent.Executor, android.location.GnssMeasurementsEvent.Callback);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static boolean registerGnssStatusCallback(android.location.LocationManager, androidx.core.location.GnssStatusCompat.Callback, android.os.Handler);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static boolean registerGnssStatusCallback(android.location.LocationManager, java.util.concurrent.Executor, androidx.core.location.GnssStatusCompat.Callback);
+    method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static void removeUpdates(android.location.LocationManager, androidx.core.location.LocationListenerCompat);
+    method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static void requestLocationUpdates(android.location.LocationManager, String, androidx.core.location.LocationRequestCompat, androidx.core.location.LocationListenerCompat, android.os.Looper);
+    method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static void requestLocationUpdates(android.location.LocationManager, String, androidx.core.location.LocationRequestCompat, java.util.concurrent.Executor, androidx.core.location.LocationListenerCompat);
+    method @RequiresApi(android.os.Build.VERSION_CODES.N) public static void unregisterGnssMeasurementsCallback(android.location.LocationManager, android.location.GnssMeasurementsEvent.Callback);
+    method public static void unregisterGnssStatusCallback(android.location.LocationManager, androidx.core.location.GnssStatusCompat.Callback);
+  }
+
+  public final class LocationRequestCompat {
+    method @IntRange(from=1) public long getDurationMillis();
+    method @IntRange(from=0) public long getIntervalMillis();
+    method @IntRange(from=0) public long getMaxUpdateDelayMillis();
+    method @IntRange(from=1, to=java.lang.Integer.MAX_VALUE) public int getMaxUpdates();
+    method @FloatRange(from=0, to=java.lang.Float.MAX_VALUE) public float getMinUpdateDistanceMeters();
+    method @IntRange(from=0) public long getMinUpdateIntervalMillis();
+    method public int getQuality();
+    method @RequiresApi(31) public android.location.LocationRequest toLocationRequest();
+    method @RequiresApi(19) public android.location.LocationRequest? toLocationRequest(String);
+    field public static final long PASSIVE_INTERVAL = 9223372036854775807L; // 0x7fffffffffffffffL
+    field public static final int QUALITY_BALANCED_POWER_ACCURACY = 102; // 0x66
+    field public static final int QUALITY_HIGH_ACCURACY = 100; // 0x64
+    field public static final int QUALITY_LOW_POWER = 104; // 0x68
+  }
+
+  public static final class LocationRequestCompat.Builder {
+    ctor public LocationRequestCompat.Builder(androidx.core.location.LocationRequestCompat);
+    ctor public LocationRequestCompat.Builder(long);
+    method public androidx.core.location.LocationRequestCompat build();
+    method public androidx.core.location.LocationRequestCompat.Builder clearMinUpdateIntervalMillis();
+    method public androidx.core.location.LocationRequestCompat.Builder setDurationMillis(@IntRange(from=1) long);
+    method public androidx.core.location.LocationRequestCompat.Builder setIntervalMillis(@IntRange(from=0) long);
+    method public androidx.core.location.LocationRequestCompat.Builder setMaxUpdateDelayMillis(@IntRange(from=0) long);
+    method public androidx.core.location.LocationRequestCompat.Builder setMaxUpdates(@IntRange(from=1, to=java.lang.Integer.MAX_VALUE) int);
+    method public androidx.core.location.LocationRequestCompat.Builder setMinUpdateDistanceMeters(@FloatRange(from=0, to=java.lang.Float.MAX_VALUE) float);
+    method public androidx.core.location.LocationRequestCompat.Builder setMinUpdateIntervalMillis(@IntRange(from=0) long);
+    method public androidx.core.location.LocationRequestCompat.Builder setQuality(int);
+  }
+
+}
+
+package androidx.core.math {
+
+  public class MathUtils {
+    method public static int addExact(int, int);
+    method public static long addExact(long, long);
+    method public static double clamp(double, double, double);
+    method public static float clamp(float, float, float);
+    method public static int clamp(int, int, int);
+    method public static long clamp(long, long, long);
+    method public static int decrementExact(int);
+    method public static long decrementExact(long);
+    method public static int incrementExact(int);
+    method public static long incrementExact(long);
+    method public static int multiplyExact(int, int);
+    method public static long multiplyExact(long, long);
+    method public static int negateExact(int);
+    method public static long negateExact(long);
+    method public static int subtractExact(int, int);
+    method public static long subtractExact(long, long);
+    method public static int toIntExact(long);
+  }
+
+}
+
+package androidx.core.net {
+
+  public final class ConnectivityManagerCompat {
+    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public static android.net.NetworkInfo? getNetworkInfoFromBroadcast(android.net.ConnectivityManager, android.content.Intent);
+    method public static int getRestrictBackgroundStatus(android.net.ConnectivityManager);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public static boolean isActiveNetworkMetered(android.net.ConnectivityManager);
+    field public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1; // 0x1
+    field public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3; // 0x3
+    field public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2; // 0x2
+  }
+
+  public final class MailTo {
+    method public String? getBcc();
+    method public String? getBody();
+    method public String? getCc();
+    method public java.util.Map<java.lang.String!,java.lang.String!>? getHeaders();
+    method public String? getSubject();
+    method public String? getTo();
+    method public static boolean isMailTo(android.net.Uri?);
+    method public static boolean isMailTo(String?);
+    method public static androidx.core.net.MailTo parse(android.net.Uri) throws androidx.core.net.ParseException;
+    method public static androidx.core.net.MailTo parse(String) throws androidx.core.net.ParseException;
+    field public static final String MAILTO_SCHEME = "mailto:";
+  }
+
+  public class ParseException extends java.lang.RuntimeException {
+    field public final String response;
+  }
+
+  public final class TrafficStatsCompat {
+    method @Deprecated public static void clearThreadStatsTag();
+    method @Deprecated public static int getThreadStatsTag();
+    method @Deprecated public static void incrementOperationCount(int);
+    method @Deprecated public static void incrementOperationCount(int, int);
+    method @Deprecated public static void setThreadStatsTag(int);
+    method public static void tagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
+    method @Deprecated public static void tagSocket(java.net.Socket!) throws java.net.SocketException;
+    method public static void untagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
+    method @Deprecated public static void untagSocket(java.net.Socket!) throws java.net.SocketException;
+  }
+
+  public final class UriCompat {
+    method public static String toSafeString(android.net.Uri);
+  }
+
+}
+
+package androidx.core.os {
+
+  public final class BuildCompat {
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.N) public static boolean isAtLeastN();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.N_MR1) public static boolean isAtLeastNMR1();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.O) public static boolean isAtLeastO();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.O_MR1) public static boolean isAtLeastOMR1();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.P) public static boolean isAtLeastP();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.Q) public static boolean isAtLeastQ();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.R) public static boolean isAtLeastR();
+    method @Deprecated @ChecksSdkIntAtLeast(api=31, codename="S") public static boolean isAtLeastS();
+    method @Deprecated @ChecksSdkIntAtLeast(api=32, codename="Sv2") public static boolean isAtLeastSv2();
+    method @Deprecated @ChecksSdkIntAtLeast(api=33, codename="Tiramisu") public static boolean isAtLeastT();
+    method @Deprecated @ChecksSdkIntAtLeast(api=34, codename="UpsideDownCake") public static boolean isAtLeastU();
+    method @SuppressCompatibility @ChecksSdkIntAtLeast(codename="VanillaIceCream") @androidx.core.os.BuildCompat.PrereleaseSdkCheck public static boolean isAtLeastV();
+    field @ChecksSdkIntAtLeast(extension=android.os.ext.SdkExtensions.AD_SERVICES) public static final int AD_SERVICES_EXTENSION_INT;
+    field public static final androidx.core.os.BuildCompat INSTANCE;
+    field @ChecksSdkIntAtLeast(extension=android.os.Build.VERSION_CODES.R) public static final int R_EXTENSION_INT;
+    field @ChecksSdkIntAtLeast(extension=android.os.Build.VERSION_CODES.S) public static final int S_EXTENSION_INT;
+    field @ChecksSdkIntAtLeast(extension=android.os.Build.VERSION_CODES.TIRAMISU) public static final int T_EXTENSION_INT;
+  }
+
+  @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public static @interface BuildCompat.PrereleaseSdkCheck {
+  }
+
+  public final class BundleCompat {
+    method public static android.os.IBinder? getBinder(android.os.Bundle, String?);
+    method public static <T> T? getParcelable(android.os.Bundle, String?, Class<T!>);
+    method public static android.os.Parcelable![]? getParcelableArray(android.os.Bundle, String?, Class<? extends android.os.Parcelable>);
+    method public static <T> java.util.ArrayList<T!>? getParcelableArrayList(android.os.Bundle, String?, Class<? extends T>);
+    method public static <T> android.util.SparseArray<T!>? getSparseParcelableArray(android.os.Bundle, String?, Class<? extends T>);
+    method public static void putBinder(android.os.Bundle, String?, android.os.IBinder?);
+  }
+
+  public final class CancellationSignal {
+    ctor public CancellationSignal();
+    method public void cancel();
+    method public Object? getCancellationSignalObject();
+    method public boolean isCanceled();
+    method public void setOnCancelListener(androidx.core.os.CancellationSignal.OnCancelListener?);
+    method public void throwIfCanceled();
+  }
+
+  public static interface CancellationSignal.OnCancelListener {
+    method public void onCancel();
+  }
+
+  public final class ConfigurationCompat {
+    method public static androidx.core.os.LocaleListCompat getLocales(android.content.res.Configuration);
+    method public static void setLocales(android.content.res.Configuration, androidx.core.os.LocaleListCompat);
+  }
+
+  public final class EnvironmentCompat {
+    method public static String getStorageState(java.io.File);
+    field public static final String MEDIA_UNKNOWN = "unknown";
+  }
+
+  public final class ExecutorCompat {
+    method public static java.util.concurrent.Executor create(android.os.Handler);
+  }
+
+  public final class HandlerCompat {
+    method public static android.os.Handler createAsync(android.os.Looper);
+    method public static android.os.Handler createAsync(android.os.Looper, android.os.Handler.Callback);
+    method @RequiresApi(16) public static boolean hasCallbacks(android.os.Handler, Runnable);
+    method public static boolean postDelayed(android.os.Handler, Runnable, Object?, long);
+  }
+
+  public final class LocaleListCompat {
+    method public static androidx.core.os.LocaleListCompat create(java.util.Locale!...);
+    method public static androidx.core.os.LocaleListCompat forLanguageTags(String?);
+    method public java.util.Locale? get(int);
+    method @Size(min=1) public static androidx.core.os.LocaleListCompat getAdjustedDefault();
+    method @Size(min=1) public static androidx.core.os.LocaleListCompat getDefault();
+    method public static androidx.core.os.LocaleListCompat getEmptyLocaleList();
+    method public java.util.Locale? getFirstMatch(String![]);
+    method @IntRange(from=0xffffffff) public int indexOf(java.util.Locale?);
+    method public boolean isEmpty();
+    method @RequiresApi(21) public static boolean matchesLanguageAndScript(java.util.Locale, java.util.Locale);
+    method @IntRange(from=0) public int size();
+    method public String toLanguageTags();
+    method public Object? unwrap();
+    method @RequiresApi(24) public static androidx.core.os.LocaleListCompat wrap(android.os.LocaleList);
+    method @Deprecated @RequiresApi(24) public static androidx.core.os.LocaleListCompat! wrap(Object!);
+  }
+
+  public final class MessageCompat {
+    method public static boolean isAsynchronous(android.os.Message);
+    method public static void setAsynchronous(android.os.Message, boolean);
+  }
+
+  public class OperationCanceledException extends java.lang.RuntimeException {
+    ctor public OperationCanceledException();
+    ctor public OperationCanceledException(String?);
+  }
+
+  public final class ParcelCompat {
+    method public static <T> Object![]? readArray(android.os.Parcel, ClassLoader?, Class<T!>);
+    method public static <T> java.util.ArrayList<T!>? readArrayList(android.os.Parcel, ClassLoader?, Class<? extends T>);
+    method public static boolean readBoolean(android.os.Parcel);
+    method public static <K, V> java.util.HashMap<K!,V!>? readHashMap(android.os.Parcel, ClassLoader?, Class<? extends K>, Class<? extends V>);
+    method public static <T> void readList(android.os.Parcel, java.util.List<? super T>, ClassLoader?, Class<T!>);
+    method public static <K, V> void readMap(android.os.Parcel, java.util.Map<? super K,? super V>, ClassLoader?, Class<K!>, Class<V!>);
+    method public static <T extends android.os.Parcelable> T? readParcelable(android.os.Parcel, ClassLoader?, Class<T!>);
+    method @Deprecated public static <T> T![]? readParcelableArray(android.os.Parcel, ClassLoader?, Class<T!>);
+    method public static <T> android.os.Parcelable![]? readParcelableArrayTyped(android.os.Parcel, ClassLoader?, Class<T!>);
+    method @RequiresApi(30) public static <T> android.os.Parcelable.Creator<T!>? readParcelableCreator(android.os.Parcel, ClassLoader?, Class<T!>);
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.Q) public static <T> java.util.List<T!> readParcelableList(android.os.Parcel, java.util.List<T!>, ClassLoader?, Class<T!>);
+    method public static <T extends java.io.Serializable> T? readSerializable(android.os.Parcel, ClassLoader?, Class<T!>);
+    method public static <T> android.util.SparseArray<T!>? readSparseArray(android.os.Parcel, ClassLoader?, Class<? extends T>);
+    method public static void writeBoolean(android.os.Parcel, boolean);
+  }
+
+  @Deprecated public final class ParcelableCompat {
+    method @Deprecated public static <T> android.os.Parcelable.Creator<T!>! newCreator(androidx.core.os.ParcelableCompatCreatorCallbacks<T!>!);
+  }
+
+  @Deprecated public interface ParcelableCompatCreatorCallbacks<T> {
+    method @Deprecated public T! createFromParcel(android.os.Parcel!, ClassLoader!);
+    method @Deprecated public T![]! newArray(int);
+  }
+
+  public final class ProcessCompat {
+    method public static boolean isApplicationUid(int);
+  }
+
+  @Deprecated public final class TraceCompat {
+    method @Deprecated public static void beginAsyncSection(String, int);
+    method @Deprecated public static void beginSection(String);
+    method @Deprecated public static void endAsyncSection(String, int);
+    method @Deprecated public static void endSection();
+    method @Deprecated public static boolean isEnabled();
+    method @Deprecated public static void setCounter(String, int);
+  }
+
+  @RequiresApi(17) public class UserHandleCompat {
+    method public static android.os.UserHandle getUserHandleForUid(int);
+  }
+
+  public class UserManagerCompat {
+    method public static boolean isUserUnlocked(android.content.Context);
+  }
+
+}
+
+package androidx.core.provider {
+
+  public final class DocumentsContractCompat {
+    method public static android.net.Uri? buildChildDocumentsUri(String, String?);
+    method public static android.net.Uri? buildChildDocumentsUriUsingTree(android.net.Uri, String);
+    method public static android.net.Uri? buildDocumentUri(String, String);
+    method public static android.net.Uri? buildDocumentUriUsingTree(android.net.Uri, String);
+    method public static android.net.Uri? buildTreeDocumentUri(String, String);
+    method public static android.net.Uri? createDocument(android.content.ContentResolver, android.net.Uri, String, String) throws java.io.FileNotFoundException;
+    method public static String? getDocumentId(android.net.Uri);
+    method public static String? getTreeDocumentId(android.net.Uri);
+    method public static boolean isDocumentUri(android.content.Context, android.net.Uri?);
+    method public static boolean isTreeUri(android.net.Uri);
+    method public static boolean removeDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+    method public static android.net.Uri? renameDocument(android.content.ContentResolver, android.net.Uri, String) throws java.io.FileNotFoundException;
+  }
+
+  public static final class DocumentsContractCompat.DocumentCompat {
+    field public static final int FLAG_VIRTUAL_DOCUMENT = 512; // 0x200
+  }
+
+  public final class FontRequest {
+    ctor public FontRequest(String, String, String, @ArrayRes int);
+    ctor public FontRequest(String, String, String, java.util.List<java.util.List<byte[]!>!>);
+    method public java.util.List<java.util.List<byte[]!>!>? getCertificates();
+    method @ArrayRes public int getCertificatesArrayResId();
+    method public String getProviderAuthority();
+    method public String getProviderPackage();
+    method public String getQuery();
+  }
+
+  public class FontsContractCompat {
+    method public static android.graphics.Typeface? buildTypeface(android.content.Context, android.os.CancellationSignal?, androidx.core.provider.FontsContractCompat.FontInfo![]);
+    method public static androidx.core.provider.FontsContractCompat.FontFamilyResult fetchFonts(android.content.Context, android.os.CancellationSignal?, androidx.core.provider.FontRequest) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static void requestFont(android.content.Context, androidx.core.provider.FontRequest, androidx.core.provider.FontsContractCompat.FontRequestCallback, android.os.Handler);
+  }
+
+  public static final class FontsContractCompat.Columns implements android.provider.BaseColumns {
+    ctor public FontsContractCompat.Columns();
+    field public static final String FILE_ID = "file_id";
+    field public static final String ITALIC = "font_italic";
+    field public static final String RESULT_CODE = "result_code";
+    field public static final int RESULT_CODE_FONT_NOT_FOUND = 1; // 0x1
+    field public static final int RESULT_CODE_FONT_UNAVAILABLE = 2; // 0x2
+    field public static final int RESULT_CODE_MALFORMED_QUERY = 3; // 0x3
+    field public static final int RESULT_CODE_OK = 0; // 0x0
+    field public static final String TTC_INDEX = "font_ttc_index";
+    field public static final String VARIATION_SETTINGS = "font_variation_settings";
+    field public static final String WEIGHT = "font_weight";
+  }
+
+  public static class FontsContractCompat.FontFamilyResult {
+    method public androidx.core.provider.FontsContractCompat.FontInfo![]! getFonts();
+    method public int getStatusCode();
+    field public static final int STATUS_OK = 0; // 0x0
+    field public static final int STATUS_UNEXPECTED_DATA_PROVIDED = 2; // 0x2
+    field public static final int STATUS_WRONG_CERTIFICATES = 1; // 0x1
+  }
+
+  public static class FontsContractCompat.FontInfo {
+    method public int getResultCode();
+    method @IntRange(from=0) public int getTtcIndex();
+    method public android.net.Uri getUri();
+    method @IntRange(from=1, to=1000) public int getWeight();
+    method public boolean isItalic();
+  }
+
+  public static class FontsContractCompat.FontRequestCallback {
+    ctor public FontsContractCompat.FontRequestCallback();
+    method public void onTypefaceRequestFailed(int);
+    method public void onTypefaceRetrieved(android.graphics.Typeface!);
+    field public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; // 0xfffffffd
+    field public static final int FAIL_REASON_FONT_NOT_FOUND = 1; // 0x1
+    field public static final int FAIL_REASON_FONT_UNAVAILABLE = 2; // 0x2
+    field public static final int FAIL_REASON_MALFORMED_QUERY = 3; // 0x3
+    field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = -1; // 0xffffffff
+    field public static final int FAIL_REASON_SECURITY_VIOLATION = -4; // 0xfffffffc
+    field public static final int FAIL_REASON_WRONG_CERTIFICATES = -2; // 0xfffffffe
+  }
+
+}
+
+package androidx.core.service.quicksettings {
+
+  public class PendingIntentActivityWrapper {
+    ctor public PendingIntentActivityWrapper(android.content.Context, int, android.content.Intent, int, android.os.Bundle?, boolean);
+    ctor public PendingIntentActivityWrapper(android.content.Context, int, android.content.Intent, int, boolean);
+    method public android.content.Context getContext();
+    method public int getFlags();
+    method public android.content.Intent getIntent();
+    method public android.os.Bundle getOptions();
+    method public android.app.PendingIntent? getPendingIntent();
+    method public int getRequestCode();
+    method public boolean isMutable();
+  }
+
+  public class TileServiceCompat {
+    method public static void startActivityAndCollapse(android.service.quicksettings.TileService, androidx.core.service.quicksettings.PendingIntentActivityWrapper);
+  }
+
+}
+
+package androidx.core.telephony {
+
+  @RequiresApi(22) public class SubscriptionManagerCompat {
+    method public static int getSlotIndex(int);
+  }
+
+  public class TelephonyManagerCompat {
+    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static String? getImei(android.telephony.TelephonyManager);
+    method public static int getSubscriptionId(android.telephony.TelephonyManager);
+  }
+
+}
+
+package androidx.core.telephony.mbms {
+
+  public final class MbmsHelper {
+    method public static CharSequence? getBestNameForService(android.content.Context, android.telephony.mbms.ServiceInfo);
+  }
+
+}
+
+package androidx.core.text {
+
+  public final class BidiFormatter {
+    method public static androidx.core.text.BidiFormatter! getInstance();
+    method public static androidx.core.text.BidiFormatter! getInstance(boolean);
+    method public static androidx.core.text.BidiFormatter! getInstance(java.util.Locale!);
+    method public boolean getStereoReset();
+    method public boolean isRtl(CharSequence!);
+    method public boolean isRtl(String!);
+    method public boolean isRtlContext();
+    method public CharSequence! unicodeWrap(CharSequence!);
+    method public CharSequence! unicodeWrap(CharSequence!, androidx.core.text.TextDirectionHeuristicCompat!);
+    method public CharSequence! unicodeWrap(CharSequence!, androidx.core.text.TextDirectionHeuristicCompat!, boolean);
+    method public CharSequence! unicodeWrap(CharSequence!, boolean);
+    method public String! unicodeWrap(String!);
+    method public String! unicodeWrap(String!, androidx.core.text.TextDirectionHeuristicCompat!);
+    method public String! unicodeWrap(String!, androidx.core.text.TextDirectionHeuristicCompat!, boolean);
+    method public String! unicodeWrap(String!, boolean);
+  }
+
+  public static final class BidiFormatter.Builder {
+    ctor public BidiFormatter.Builder();
+    ctor public BidiFormatter.Builder(boolean);
+    ctor public BidiFormatter.Builder(java.util.Locale!);
+    method public androidx.core.text.BidiFormatter! build();
+    method public androidx.core.text.BidiFormatter.Builder! setTextDirectionHeuristic(androidx.core.text.TextDirectionHeuristicCompat!);
+    method public androidx.core.text.BidiFormatter.Builder! stereoReset(boolean);
+  }
+
+  public final class HtmlCompat {
+    method public static android.text.Spanned fromHtml(String, int);
+    method public static android.text.Spanned fromHtml(String, int, android.text.Html.ImageGetter?, android.text.Html.TagHandler?);
+    method public static String toHtml(android.text.Spanned, int);
+    field public static final int FROM_HTML_MODE_COMPACT = 63; // 0x3f
+    field public static final int FROM_HTML_MODE_LEGACY = 0; // 0x0
+    field public static final int FROM_HTML_OPTION_USE_CSS_COLORS = 256; // 0x100
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_BLOCKQUOTE = 32; // 0x20
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_DIV = 16; // 0x10
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_HEADING = 2; // 0x2
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_LIST = 8; // 0x8
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM = 4; // 0x4
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH = 1; // 0x1
+    field public static final int TO_HTML_PARAGRAPH_LINES_CONSECUTIVE = 0; // 0x0
+    field public static final int TO_HTML_PARAGRAPH_LINES_INDIVIDUAL = 1; // 0x1
+  }
+
+  public final class ICUCompat {
+    method public static String? maximizeAndGetScript(java.util.Locale);
+  }
+
+  public class PrecomputedTextCompat implements android.text.Spannable {
+    method public char charAt(int);
+    method public static androidx.core.text.PrecomputedTextCompat! create(CharSequence, androidx.core.text.PrecomputedTextCompat.Params);
+    method @IntRange(from=0) public int getParagraphCount();
+    method @IntRange(from=0) public int getParagraphEnd(@IntRange(from=0) int);
+    method @IntRange(from=0) public int getParagraphStart(@IntRange(from=0) int);
+    method public androidx.core.text.PrecomputedTextCompat.Params getParams();
+    method public int getSpanEnd(Object!);
+    method public int getSpanFlags(Object!);
+    method public int getSpanStart(Object!);
+    method public <T> T![]! getSpans(int, int, Class<T!>!);
+    method @UiThread public static java.util.concurrent.Future<androidx.core.text.PrecomputedTextCompat!>! getTextFuture(CharSequence, androidx.core.text.PrecomputedTextCompat.Params, java.util.concurrent.Executor?);
+    method public int length();
+    method public int nextSpanTransition(int, int, Class!);
+    method public void removeSpan(Object!);
+    method public void setSpan(Object!, int, int, int);
+    method public CharSequence! subSequence(int, int);
+  }
+
+  public static final class PrecomputedTextCompat.Params {
+    ctor @RequiresApi(28) public PrecomputedTextCompat.Params(android.text.PrecomputedText.Params);
+    method @RequiresApi(23) public int getBreakStrategy();
+    method @RequiresApi(23) public int getHyphenationFrequency();
+    method @RequiresApi(18) public android.text.TextDirectionHeuristic? getTextDirection();
+    method public android.text.TextPaint getTextPaint();
+  }
+
+  public static class PrecomputedTextCompat.Params.Builder {
+    ctor public PrecomputedTextCompat.Params.Builder(android.text.TextPaint);
+    method public androidx.core.text.PrecomputedTextCompat.Params build();
+    method @RequiresApi(23) public androidx.core.text.PrecomputedTextCompat.Params.Builder! setBreakStrategy(int);
+    method @RequiresApi(23) public androidx.core.text.PrecomputedTextCompat.Params.Builder! setHyphenationFrequency(int);
+    method @RequiresApi(18) public androidx.core.text.PrecomputedTextCompat.Params.Builder! setTextDirection(android.text.TextDirectionHeuristic);
+  }
+
+  public interface TextDirectionHeuristicCompat {
+    method public boolean isRtl(char[]!, int, int);
+    method public boolean isRtl(CharSequence!, int, int);
+  }
+
+  public final class TextDirectionHeuristicsCompat {
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! ANYRTL_LTR;
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! FIRSTSTRONG_LTR;
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! FIRSTSTRONG_RTL;
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! LOCALE;
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! LTR;
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! RTL;
+  }
+
+  public final class TextUtilsCompat {
+    method public static int getLayoutDirectionFromLocale(java.util.Locale?);
+    method public static String htmlEncode(String);
+  }
+
+}
+
+package androidx.core.text.method {
+
+  public class LinkMovementMethodCompat extends android.text.method.LinkMovementMethod {
+    method public static androidx.core.text.method.LinkMovementMethodCompat getInstance();
+  }
+
+}
+
+package androidx.core.text.util {
+
+  public final class LinkifyCompat {
+    method public static boolean addLinks(android.text.Spannable, int);
+    method public static boolean addLinks(android.text.Spannable, java.util.regex.Pattern, String?);
+    method public static boolean addLinks(android.text.Spannable, java.util.regex.Pattern, String?, android.text.util.Linkify.MatchFilter?, android.text.util.Linkify.TransformFilter?);
+    method public static boolean addLinks(android.text.Spannable, java.util.regex.Pattern, String?, String![]?, android.text.util.Linkify.MatchFilter?, android.text.util.Linkify.TransformFilter?);
+    method public static boolean addLinks(android.widget.TextView, int);
+    method public static void addLinks(android.widget.TextView, java.util.regex.Pattern, String?);
+    method public static void addLinks(android.widget.TextView, java.util.regex.Pattern, String?, android.text.util.Linkify.MatchFilter?, android.text.util.Linkify.TransformFilter?);
+    method public static void addLinks(android.widget.TextView, java.util.regex.Pattern, String?, String![]?, android.text.util.Linkify.MatchFilter?, android.text.util.Linkify.TransformFilter?);
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.LOLLIPOP) public final class LocalePreferences {
+    method public static String getCalendarType();
+    method public static String getCalendarType(boolean);
+    method public static String getCalendarType(java.util.Locale);
+    method public static String getCalendarType(java.util.Locale, boolean);
+    method public static String getFirstDayOfWeek();
+    method public static String getFirstDayOfWeek(boolean);
+    method public static String getFirstDayOfWeek(java.util.Locale);
+    method public static String getFirstDayOfWeek(java.util.Locale, boolean);
+    method public static String getHourCycle();
+    method public static String getHourCycle(boolean);
+    method public static String getHourCycle(java.util.Locale);
+    method public static String getHourCycle(java.util.Locale, boolean);
+    method public static String getTemperatureUnit();
+    method public static String getTemperatureUnit(boolean);
+    method public static String getTemperatureUnit(java.util.Locale);
+    method public static String getTemperatureUnit(java.util.Locale, boolean);
+  }
+
+  public static class LocalePreferences.CalendarType {
+    field public static final String CHINESE = "chinese";
+    field public static final String DANGI = "dangi";
+    field public static final String DEFAULT = "";
+    field public static final String GREGORIAN = "gregorian";
+    field public static final String HEBREW = "hebrew";
+    field public static final String INDIAN = "indian";
+    field public static final String ISLAMIC = "islamic";
+    field public static final String ISLAMIC_CIVIL = "islamic-civil";
+    field public static final String ISLAMIC_RGSA = "islamic-rgsa";
+    field public static final String ISLAMIC_TBLA = "islamic-tbla";
+    field public static final String ISLAMIC_UMALQURA = "islamic-umalqura";
+    field public static final String PERSIAN = "persian";
+  }
+
+  public static class LocalePreferences.FirstDayOfWeek {
+    field public static final String DEFAULT = "";
+    field public static final String FRIDAY = "fri";
+    field public static final String MONDAY = "mon";
+    field public static final String SATURDAY = "sat";
+    field public static final String SUNDAY = "sun";
+    field public static final String THURSDAY = "thu";
+    field public static final String TUESDAY = "tue";
+    field public static final String WEDNESDAY = "wed";
+  }
+
+  public static class LocalePreferences.HourCycle {
+    field public static final String DEFAULT = "";
+    field public static final String H11 = "h11";
+    field public static final String H12 = "h12";
+    field public static final String H23 = "h23";
+    field public static final String H24 = "h24";
+  }
+
+  public static class LocalePreferences.TemperatureUnit {
+    field public static final String CELSIUS = "celsius";
+    field public static final String DEFAULT = "";
+    field public static final String FAHRENHEIT = "fahrenhe";
+    field public static final String KELVIN = "kelvin";
+  }
+
+}
+
+package androidx.core.util {
+
+  public class AtomicFile {
+    ctor public AtomicFile(java.io.File);
+    method public void delete();
+    method public void failWrite(java.io.FileOutputStream?);
+    method public void finishWrite(java.io.FileOutputStream?);
+    method public java.io.File getBaseFile();
+    method public java.io.FileInputStream openRead() throws java.io.FileNotFoundException;
+    method public byte[] readFully() throws java.io.IOException;
+    method public java.io.FileOutputStream startWrite() throws java.io.IOException;
+  }
+
+  public interface Consumer<T> {
+    method public void accept(T!);
+  }
+
+  @java.lang.FunctionalInterface public interface Function<T, R> {
+    method public R! apply(T!);
+  }
+
+  public class ObjectsCompat {
+    method public static boolean equals(Object?, Object?);
+    method public static int hash(java.lang.Object!...);
+    method public static int hashCode(Object?);
+    method public static <T> T requireNonNull(T?);
+    method public static <T> T requireNonNull(T?, String);
+    method public static String? toString(Object?, String?);
+  }
+
+  public class Pair<F, S> {
+    ctor public Pair(F!, S!);
+    method public static <A, B> androidx.core.util.Pair<A!,B!> create(A!, B!);
+    field public final F! first;
+    field public final S! second;
+  }
+
+  public final class PatternsCompat {
+    field public static final java.util.regex.Pattern DOMAIN_NAME;
+    field public static final java.util.regex.Pattern EMAIL_ADDRESS;
+    field public static final java.util.regex.Pattern IP_ADDRESS;
+    field public static final java.util.regex.Pattern WEB_URL;
+  }
+
+  public final class Pools {
+  }
+
+  public static interface Pools.Pool<T> {
+    method public T? acquire();
+    method public boolean release(T);
+  }
+
+  public static class Pools.SimplePool<T> implements androidx.core.util.Pools.Pool<T> {
+    ctor public Pools.SimplePool(int);
+    method public T! acquire();
+    method public boolean release(T);
+  }
+
+  public static class Pools.SynchronizedPool<T> extends androidx.core.util.Pools.SimplePool<T> {
+    ctor public Pools.SynchronizedPool(int);
+  }
+
+  public interface Predicate<T> {
+    method public default androidx.core.util.Predicate<T!>! and(androidx.core.util.Predicate<? super T>!);
+    method public static <T> androidx.core.util.Predicate<T!>! isEqual(Object!);
+    method public default androidx.core.util.Predicate<T!>! negate();
+    method public static <T> androidx.core.util.Predicate<T!>! not(androidx.core.util.Predicate<? super T>!);
+    method public default androidx.core.util.Predicate<T!>! or(androidx.core.util.Predicate<? super T>!);
+    method public boolean test(T!);
+  }
+
+  public final class SizeFCompat {
+    ctor public SizeFCompat(float, float);
+    method public float getHeight();
+    method public float getWidth();
+    method @RequiresApi(21) public android.util.SizeF toSizeF();
+    method @RequiresApi(21) public static androidx.core.util.SizeFCompat toSizeFCompat(android.util.SizeF);
+  }
+
+  public interface Supplier<T> {
+    method public T! get();
+  }
+
+  public class TypedValueCompat {
+    method public static float deriveDimension(int, float, android.util.DisplayMetrics);
+    method public static float dpToPx(float, android.util.DisplayMetrics);
+    method public static int getUnitFromComplexDimension(int);
+    method public static float pxToDp(float, android.util.DisplayMetrics);
+    method public static float pxToSp(float, android.util.DisplayMetrics);
+    method public static float spToPx(float, android.util.DisplayMetrics);
+  }
+
+}
+
+package androidx.core.view {
+
+  public class AccessibilityDelegateCompat {
+    ctor public AccessibilityDelegateCompat();
+    method public boolean dispatchPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public androidx.core.view.accessibility.AccessibilityNodeProviderCompat? getAccessibilityNodeProvider(android.view.View);
+    method public void onInitializeAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public void onInitializeAccessibilityNodeInfo(android.view.View, androidx.core.view.accessibility.AccessibilityNodeInfoCompat);
+    method public void onPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public boolean onRequestSendAccessibilityEvent(android.view.ViewGroup, android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public boolean performAccessibilityAction(android.view.View, int, android.os.Bundle?);
+    method public void sendAccessibilityEvent(android.view.View, int);
+    method public void sendAccessibilityEventUnchecked(android.view.View, android.view.accessibility.AccessibilityEvent);
+  }
+
+  public abstract class ActionProvider {
+    ctor public ActionProvider(android.content.Context);
+    method public android.content.Context getContext();
+    method public boolean hasSubMenu();
+    method public boolean isVisible();
+    method public abstract android.view.View onCreateActionView();
+    method public android.view.View onCreateActionView(android.view.MenuItem);
+    method public boolean onPerformDefaultAction();
+    method public void onPrepareSubMenu(android.view.SubMenu);
+    method public boolean overridesItemVisibility();
+    method public void refreshVisibility();
+    method public void setVisibilityListener(androidx.core.view.ActionProvider.VisibilityListener?);
+  }
+
+  public static interface ActionProvider.VisibilityListener {
+    method public void onActionProviderVisibilityChanged(boolean);
+  }
+
+  public final class ContentInfoCompat {
+    method public android.content.ClipData getClip();
+    method public android.os.Bundle? getExtras();
+    method public int getFlags();
+    method public android.net.Uri? getLinkUri();
+    method public int getSource();
+    method @RequiresApi(31) public static android.util.Pair<android.view.ContentInfo!,android.view.ContentInfo!> partition(android.view.ContentInfo, java.util.function.Predicate<android.content.ClipData.Item!>);
+    method public android.util.Pair<androidx.core.view.ContentInfoCompat!,androidx.core.view.ContentInfoCompat!> partition(androidx.core.util.Predicate<android.content.ClipData.Item!>);
+    method @RequiresApi(31) public android.view.ContentInfo toContentInfo();
+    method @RequiresApi(31) public static androidx.core.view.ContentInfoCompat toContentInfoCompat(android.view.ContentInfo);
+    field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
+    field public static final int SOURCE_APP = 0; // 0x0
+    field public static final int SOURCE_AUTOFILL = 4; // 0x4
+    field public static final int SOURCE_CLIPBOARD = 1; // 0x1
+    field public static final int SOURCE_DRAG_AND_DROP = 3; // 0x3
+    field public static final int SOURCE_INPUT_METHOD = 2; // 0x2
+    field public static final int SOURCE_PROCESS_TEXT = 5; // 0x5
+  }
+
+  public static final class ContentInfoCompat.Builder {
+    ctor public ContentInfoCompat.Builder(android.content.ClipData, int);
+    ctor public ContentInfoCompat.Builder(androidx.core.view.ContentInfoCompat);
+    method public androidx.core.view.ContentInfoCompat build();
+    method public androidx.core.view.ContentInfoCompat.Builder setClip(android.content.ClipData);
+    method public androidx.core.view.ContentInfoCompat.Builder setExtras(android.os.Bundle?);
+    method public androidx.core.view.ContentInfoCompat.Builder setFlags(int);
+    method public androidx.core.view.ContentInfoCompat.Builder setLinkUri(android.net.Uri?);
+    method public androidx.core.view.ContentInfoCompat.Builder setSource(int);
+  }
+
+  public final class DisplayCompat {
+    method public static androidx.core.view.DisplayCompat.ModeCompat getMode(android.content.Context, android.view.Display);
+    method public static androidx.core.view.DisplayCompat.ModeCompat![] getSupportedModes(android.content.Context, android.view.Display);
+  }
+
+  public static final class DisplayCompat.ModeCompat {
+    method public int getPhysicalHeight();
+    method public int getPhysicalWidth();
+    method @Deprecated public boolean isNative();
+    method @RequiresApi(android.os.Build.VERSION_CODES.M) public android.view.Display.Mode? toMode();
+  }
+
+  public final class DisplayCutoutCompat {
+    ctor public DisplayCutoutCompat(android.graphics.Rect?, java.util.List<android.graphics.Rect!>?);
+    ctor public DisplayCutoutCompat(androidx.core.graphics.Insets, android.graphics.Rect?, android.graphics.Rect?, android.graphics.Rect?, android.graphics.Rect?, androidx.core.graphics.Insets);
+    method public java.util.List<android.graphics.Rect!> getBoundingRects();
+    method public int getSafeInsetBottom();
+    method public int getSafeInsetLeft();
+    method public int getSafeInsetRight();
+    method public int getSafeInsetTop();
+    method public androidx.core.graphics.Insets getWaterfallInsets();
+  }
+
+  public final class DragAndDropPermissionsCompat {
+    method public void release();
+  }
+
+  public class DragStartHelper {
+    ctor public DragStartHelper(android.view.View, androidx.core.view.DragStartHelper.OnDragStartListener);
+    method public void attach();
+    method public void detach();
+    method public void getTouchPosition(android.graphics.Point);
+    method public boolean onLongClick(android.view.View);
+    method public boolean onTouch(android.view.View, android.view.MotionEvent);
+  }
+
+  public static interface DragStartHelper.OnDragStartListener {
+    method public boolean onDragStart(android.view.View, androidx.core.view.DragStartHelper);
+  }
+
+  public final class GestureDetectorCompat {
+    ctor public GestureDetectorCompat(android.content.Context, android.view.GestureDetector.OnGestureListener);
+    ctor public GestureDetectorCompat(android.content.Context, android.view.GestureDetector.OnGestureListener, android.os.Handler?);
+    method public boolean isLongpressEnabled();
+    method public boolean onTouchEvent(android.view.MotionEvent);
+    method public void setIsLongpressEnabled(boolean);
+    method public void setOnDoubleTapListener(android.view.GestureDetector.OnDoubleTapListener?);
+  }
+
+  public final class GravityCompat {
+    method public static void apply(int, int, int, android.graphics.Rect, android.graphics.Rect, int);
+    method public static void apply(int, int, int, android.graphics.Rect, int, int, android.graphics.Rect, int);
+    method public static void applyDisplay(int, android.graphics.Rect, android.graphics.Rect, int);
+    method public static int getAbsoluteGravity(int, int);
+    field public static final int END = 8388613; // 0x800005
+    field public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = 8388615; // 0x800007
+    field public static final int RELATIVE_LAYOUT_DIRECTION = 8388608; // 0x800000
+    field public static final int START = 8388611; // 0x800003
+  }
+
+  public final class HapticFeedbackConstantsCompat {
+    field public static final int CLOCK_TICK = 4; // 0x4
+    field public static final int CONFIRM = 16; // 0x10
+    field public static final int CONTEXT_CLICK = 6; // 0x6
+    field public static final int DRAG_START = 25; // 0x19
+    field public static final int FLAG_IGNORE_VIEW_SETTING = 1; // 0x1
+    field public static final int GESTURE_END = 13; // 0xd
+    field public static final int GESTURE_START = 12; // 0xc
+    field public static final int GESTURE_THRESHOLD_ACTIVATE = 23; // 0x17
+    field public static final int GESTURE_THRESHOLD_DEACTIVATE = 24; // 0x18
+    field public static final int KEYBOARD_PRESS = 3; // 0x3
+    field public static final int KEYBOARD_RELEASE = 7; // 0x7
+    field public static final int KEYBOARD_TAP = 3; // 0x3
+    field public static final int LONG_PRESS = 0; // 0x0
+    field public static final int NO_HAPTICS = -1; // 0xffffffff
+    field public static final int REJECT = 17; // 0x11
+    field public static final int SEGMENT_FREQUENT_TICK = 27; // 0x1b
+    field public static final int SEGMENT_TICK = 26; // 0x1a
+    field public static final int TEXT_HANDLE_MOVE = 9; // 0x9
+    field public static final int TOGGLE_OFF = 22; // 0x16
+    field public static final int TOGGLE_ON = 21; // 0x15
+    field public static final int VIRTUAL_KEY = 1; // 0x1
+    field public static final int VIRTUAL_KEY_RELEASE = 8; // 0x8
+  }
+
+  public final class InputDeviceCompat {
+    field public static final int SOURCE_ANY = -256; // 0xffffff00
+    field public static final int SOURCE_CLASS_BUTTON = 1; // 0x1
+    field public static final int SOURCE_CLASS_JOYSTICK = 16; // 0x10
+    field public static final int SOURCE_CLASS_MASK = 255; // 0xff
+    field public static final int SOURCE_CLASS_NONE = 0; // 0x0
+    field public static final int SOURCE_CLASS_POINTER = 2; // 0x2
+    field public static final int SOURCE_CLASS_POSITION = 8; // 0x8
+    field public static final int SOURCE_CLASS_TRACKBALL = 4; // 0x4
+    field public static final int SOURCE_DPAD = 513; // 0x201
+    field public static final int SOURCE_GAMEPAD = 1025; // 0x401
+    field public static final int SOURCE_HDMI = 33554433; // 0x2000001
+    field public static final int SOURCE_JOYSTICK = 16777232; // 0x1000010
+    field public static final int SOURCE_KEYBOARD = 257; // 0x101
+    field public static final int SOURCE_MOUSE = 8194; // 0x2002
+    field public static final int SOURCE_ROTARY_ENCODER = 4194304; // 0x400000
+    field public static final int SOURCE_STYLUS = 16386; // 0x4002
+    field public static final int SOURCE_TOUCHPAD = 1048584; // 0x100008
+    field public static final int SOURCE_TOUCHSCREEN = 4098; // 0x1002
+    field public static final int SOURCE_TOUCH_NAVIGATION = 2097152; // 0x200000
+    field public static final int SOURCE_TRACKBALL = 65540; // 0x10004
+    field public static final int SOURCE_UNKNOWN = 0; // 0x0
+  }
+
+  public final class LayoutInflaterCompat {
+    method @Deprecated public static androidx.core.view.LayoutInflaterFactory! getFactory(android.view.LayoutInflater!);
+    method @Deprecated public static void setFactory(android.view.LayoutInflater, androidx.core.view.LayoutInflaterFactory);
+    method public static void setFactory2(android.view.LayoutInflater, android.view.LayoutInflater.Factory2);
+  }
+
+  @Deprecated public interface LayoutInflaterFactory {
+    method @Deprecated public android.view.View! onCreateView(android.view.View!, String!, android.content.Context!, android.util.AttributeSet!);
+  }
+
+  public final class MarginLayoutParamsCompat {
+    method public static int getLayoutDirection(android.view.ViewGroup.MarginLayoutParams);
+    method public static int getMarginEnd(android.view.ViewGroup.MarginLayoutParams);
+    method public static int getMarginStart(android.view.ViewGroup.MarginLayoutParams);
+    method public static boolean isMarginRelative(android.view.ViewGroup.MarginLayoutParams);
+    method public static void resolveLayoutDirection(android.view.ViewGroup.MarginLayoutParams, int);
+    method public static void setLayoutDirection(android.view.ViewGroup.MarginLayoutParams, int);
+    method public static void setMarginEnd(android.view.ViewGroup.MarginLayoutParams, int);
+    method public static void setMarginStart(android.view.ViewGroup.MarginLayoutParams, int);
+  }
+
+  public final class MenuCompat {
+    method public static void setGroupDividerEnabled(android.view.Menu, boolean);
+    method @Deprecated public static void setShowAsAction(android.view.MenuItem!, int);
+  }
+
+  public interface MenuHost {
+    method public void addMenuProvider(androidx.core.view.MenuProvider);
+    method public void addMenuProvider(androidx.core.view.MenuProvider, androidx.lifecycle.LifecycleOwner);
+    method public void addMenuProvider(androidx.core.view.MenuProvider, androidx.lifecycle.LifecycleOwner, androidx.lifecycle.Lifecycle.State);
+    method public void invalidateMenu();
+    method public void removeMenuProvider(androidx.core.view.MenuProvider);
+  }
+
+  public class MenuHostHelper {
+    ctor public MenuHostHelper(Runnable);
+    method public void addMenuProvider(androidx.core.view.MenuProvider);
+    method public void addMenuProvider(androidx.core.view.MenuProvider, androidx.lifecycle.LifecycleOwner);
+    method public void addMenuProvider(androidx.core.view.MenuProvider, androidx.lifecycle.LifecycleOwner, androidx.lifecycle.Lifecycle.State);
+    method public void onCreateMenu(android.view.Menu, android.view.MenuInflater);
+    method public void onMenuClosed(android.view.Menu);
+    method public boolean onMenuItemSelected(android.view.MenuItem);
+    method public void onPrepareMenu(android.view.Menu);
+    method public void removeMenuProvider(androidx.core.view.MenuProvider);
+  }
+
+  public final class MenuItemCompat {
+    method @Deprecated public static boolean collapseActionView(android.view.MenuItem!);
+    method @Deprecated public static boolean expandActionView(android.view.MenuItem!);
+    method public static androidx.core.view.ActionProvider? getActionProvider(android.view.MenuItem);
+    method @Deprecated public static android.view.View! getActionView(android.view.MenuItem!);
+    method public static int getAlphabeticModifiers(android.view.MenuItem);
+    method public static CharSequence? getContentDescription(android.view.MenuItem);
+    method public static android.content.res.ColorStateList? getIconTintList(android.view.MenuItem);
+    method public static android.graphics.PorterDuff.Mode? getIconTintMode(android.view.MenuItem);
+    method public static int getNumericModifiers(android.view.MenuItem);
+    method public static CharSequence? getTooltipText(android.view.MenuItem);
+    method @Deprecated public static boolean isActionViewExpanded(android.view.MenuItem!);
+    method public static android.view.MenuItem? setActionProvider(android.view.MenuItem, androidx.core.view.ActionProvider?);
+    method @Deprecated public static android.view.MenuItem! setActionView(android.view.MenuItem!, android.view.View!);
+    method @Deprecated public static android.view.MenuItem! setActionView(android.view.MenuItem!, int);
+    method public static void setAlphabeticShortcut(android.view.MenuItem, char, int);
+    method public static void setContentDescription(android.view.MenuItem, CharSequence?);
+    method public static void setIconTintList(android.view.MenuItem, android.content.res.ColorStateList?);
+    method public static void setIconTintMode(android.view.MenuItem, android.graphics.PorterDuff.Mode?);
+    method public static void setNumericShortcut(android.view.MenuItem, char, int);
+    method @Deprecated public static android.view.MenuItem! setOnActionExpandListener(android.view.MenuItem!, androidx.core.view.MenuItemCompat.OnActionExpandListener!);
+    method public static void setShortcut(android.view.MenuItem, char, char, int, int);
+    method @Deprecated public static void setShowAsAction(android.view.MenuItem!, int);
+    method public static void setTooltipText(android.view.MenuItem, CharSequence?);
+    field @Deprecated public static final int SHOW_AS_ACTION_ALWAYS = 2; // 0x2
+    field @Deprecated public static final int SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = 8; // 0x8
+    field @Deprecated public static final int SHOW_AS_ACTION_IF_ROOM = 1; // 0x1
+    field @Deprecated public static final int SHOW_AS_ACTION_NEVER = 0; // 0x0
+    field @Deprecated public static final int SHOW_AS_ACTION_WITH_TEXT = 4; // 0x4
+  }
+
+  @Deprecated public static interface MenuItemCompat.OnActionExpandListener {
+    method @Deprecated public boolean onMenuItemActionCollapse(android.view.MenuItem!);
+    method @Deprecated public boolean onMenuItemActionExpand(android.view.MenuItem!);
+  }
+
+  public interface MenuProvider {
+    method public void onCreateMenu(android.view.Menu, android.view.MenuInflater);
+    method public default void onMenuClosed(android.view.Menu);
+    method public boolean onMenuItemSelected(android.view.MenuItem);
+    method public default void onPrepareMenu(android.view.Menu);
+  }
+
+  public final class MotionEventCompat {
+    method @Deprecated public static int findPointerIndex(android.view.MotionEvent!, int);
+    method @Deprecated public static int getActionIndex(android.view.MotionEvent!);
+    method @Deprecated public static int getActionMasked(android.view.MotionEvent!);
+    method @Deprecated public static float getAxisValue(android.view.MotionEvent!, int);
+    method @Deprecated public static float getAxisValue(android.view.MotionEvent!, int, int);
+    method @Deprecated public static int getButtonState(android.view.MotionEvent!);
+    method @Deprecated public static int getPointerCount(android.view.MotionEvent!);
+    method @Deprecated public static int getPointerId(android.view.MotionEvent!, int);
+    method @Deprecated public static int getSource(android.view.MotionEvent!);
+    method @Deprecated public static float getX(android.view.MotionEvent!, int);
+    method @Deprecated public static float getY(android.view.MotionEvent!, int);
+    method public static boolean isFromSource(android.view.MotionEvent, int);
+    field @Deprecated public static final int ACTION_HOVER_ENTER = 9; // 0x9
+    field @Deprecated public static final int ACTION_HOVER_EXIT = 10; // 0xa
+    field @Deprecated public static final int ACTION_HOVER_MOVE = 7; // 0x7
+    field @Deprecated public static final int ACTION_MASK = 255; // 0xff
+    field @Deprecated public static final int ACTION_POINTER_DOWN = 5; // 0x5
+    field @Deprecated public static final int ACTION_POINTER_INDEX_MASK = 65280; // 0xff00
+    field @Deprecated public static final int ACTION_POINTER_INDEX_SHIFT = 8; // 0x8
+    field @Deprecated public static final int ACTION_POINTER_UP = 6; // 0x6
+    field @Deprecated public static final int ACTION_SCROLL = 8; // 0x8
+    field @Deprecated public static final int AXIS_BRAKE = 23; // 0x17
+    field @Deprecated public static final int AXIS_DISTANCE = 24; // 0x18
+    field @Deprecated public static final int AXIS_GAS = 22; // 0x16
+    field @Deprecated public static final int AXIS_GENERIC_1 = 32; // 0x20
+    field @Deprecated public static final int AXIS_GENERIC_10 = 41; // 0x29
+    field @Deprecated public static final int AXIS_GENERIC_11 = 42; // 0x2a
+    field @Deprecated public static final int AXIS_GENERIC_12 = 43; // 0x2b
+    field @Deprecated public static final int AXIS_GENERIC_13 = 44; // 0x2c
+    field @Deprecated public static final int AXIS_GENERIC_14 = 45; // 0x2d
+    field @Deprecated public static final int AXIS_GENERIC_15 = 46; // 0x2e
+    field @Deprecated public static final int AXIS_GENERIC_16 = 47; // 0x2f
+    field @Deprecated public static final int AXIS_GENERIC_2 = 33; // 0x21
+    field @Deprecated public static final int AXIS_GENERIC_3 = 34; // 0x22
+    field @Deprecated public static final int AXIS_GENERIC_4 = 35; // 0x23
+    field @Deprecated public static final int AXIS_GENERIC_5 = 36; // 0x24
+    field @Deprecated public static final int AXIS_GENERIC_6 = 37; // 0x25
+    field @Deprecated public static final int AXIS_GENERIC_7 = 38; // 0x26
+    field @Deprecated public static final int AXIS_GENERIC_8 = 39; // 0x27
+    field @Deprecated public static final int AXIS_GENERIC_9 = 40; // 0x28
+    field @Deprecated public static final int AXIS_HAT_X = 15; // 0xf
+    field @Deprecated public static final int AXIS_HAT_Y = 16; // 0x10
+    field @Deprecated public static final int AXIS_HSCROLL = 10; // 0xa
+    field @Deprecated public static final int AXIS_LTRIGGER = 17; // 0x11
+    field @Deprecated public static final int AXIS_ORIENTATION = 8; // 0x8
+    field @Deprecated public static final int AXIS_PRESSURE = 2; // 0x2
+    field public static final int AXIS_RELATIVE_X = 27; // 0x1b
+    field public static final int AXIS_RELATIVE_Y = 28; // 0x1c
+    field @Deprecated public static final int AXIS_RTRIGGER = 18; // 0x12
+    field @Deprecated public static final int AXIS_RUDDER = 20; // 0x14
+    field @Deprecated public static final int AXIS_RX = 12; // 0xc
+    field @Deprecated public static final int AXIS_RY = 13; // 0xd
+    field @Deprecated public static final int AXIS_RZ = 14; // 0xe
+    field public static final int AXIS_SCROLL = 26; // 0x1a
+    field @Deprecated public static final int AXIS_SIZE = 3; // 0x3
+    field @Deprecated public static final int AXIS_THROTTLE = 19; // 0x13
+    field @Deprecated public static final int AXIS_TILT = 25; // 0x19
+    field @Deprecated public static final int AXIS_TOOL_MAJOR = 6; // 0x6
+    field @Deprecated public static final int AXIS_TOOL_MINOR = 7; // 0x7
+    field @Deprecated public static final int AXIS_TOUCH_MAJOR = 4; // 0x4
+    field @Deprecated public static final int AXIS_TOUCH_MINOR = 5; // 0x5
+    field @Deprecated public static final int AXIS_VSCROLL = 9; // 0x9
+    field @Deprecated public static final int AXIS_WHEEL = 21; // 0x15
+    field @Deprecated public static final int AXIS_X = 0; // 0x0
+    field @Deprecated public static final int AXIS_Y = 1; // 0x1
+    field @Deprecated public static final int AXIS_Z = 11; // 0xb
+    field @Deprecated public static final int BUTTON_PRIMARY = 1; // 0x1
+  }
+
+  public interface NestedScrollingChild {
+    method public boolean dispatchNestedFling(float, float, boolean);
+    method public boolean dispatchNestedPreFling(float, float);
+    method public boolean dispatchNestedPreScroll(int, int, int[]?, int[]?);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]?);
+    method public boolean hasNestedScrollingParent();
+    method public boolean isNestedScrollingEnabled();
+    method public void setNestedScrollingEnabled(boolean);
+    method public boolean startNestedScroll(int);
+    method public void stopNestedScroll();
+  }
+
+  public interface NestedScrollingChild2 extends androidx.core.view.NestedScrollingChild {
+    method public boolean dispatchNestedPreScroll(int, int, int[]?, int[]?, int);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]?, int);
+    method public boolean hasNestedScrollingParent(int);
+    method public boolean startNestedScroll(int, int);
+    method public void stopNestedScroll(int);
+  }
+
+  public interface NestedScrollingChild3 extends androidx.core.view.NestedScrollingChild2 {
+    method public void dispatchNestedScroll(int, int, int, int, int[]?, int, int[]);
+  }
+
+  public class NestedScrollingChildHelper {
+    ctor public NestedScrollingChildHelper(android.view.View);
+    method public boolean dispatchNestedFling(float, float, boolean);
+    method public boolean dispatchNestedPreFling(float, float);
+    method public boolean dispatchNestedPreScroll(int, int, int[]?, int[]?);
+    method public boolean dispatchNestedPreScroll(int, int, int[]?, int[]?, int);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]?);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]?, int);
+    method public void dispatchNestedScroll(int, int, int, int, int[]?, int, int[]?);
+    method public boolean hasNestedScrollingParent();
+    method public boolean hasNestedScrollingParent(int);
+    method public boolean isNestedScrollingEnabled();
+    method public void onDetachedFromWindow();
+    method public void onStopNestedScroll(android.view.View);
+    method public void setNestedScrollingEnabled(boolean);
+    method public boolean startNestedScroll(int);
+    method public boolean startNestedScroll(int, int);
+    method public void stopNestedScroll();
+    method public void stopNestedScroll(int);
+  }
+
+  public interface NestedScrollingParent {
+    method public int getNestedScrollAxes();
+    method public boolean onNestedFling(android.view.View, float, float, boolean);
+    method public boolean onNestedPreFling(android.view.View, float, float);
+    method public void onNestedPreScroll(android.view.View, int, int, int[]);
+    method public void onNestedScroll(android.view.View, int, int, int, int);
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, int);
+    method public boolean onStartNestedScroll(android.view.View, android.view.View, int);
+    method public void onStopNestedScroll(android.view.View);
+  }
+
+  public interface NestedScrollingParent2 extends androidx.core.view.NestedScrollingParent {
+    method public void onNestedPreScroll(android.view.View, int, int, int[], int);
+    method public void onNestedScroll(android.view.View, int, int, int, int, int);
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, int, int);
+    method public boolean onStartNestedScroll(android.view.View, android.view.View, int, int);
+    method public void onStopNestedScroll(android.view.View, int);
+  }
+
+  public interface NestedScrollingParent3 extends androidx.core.view.NestedScrollingParent2 {
+    method public void onNestedScroll(android.view.View, int, int, int, int, int, int[]);
+  }
+
+  public class NestedScrollingParentHelper {
+    ctor public NestedScrollingParentHelper(android.view.ViewGroup);
+    method public int getNestedScrollAxes();
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, int);
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, int, int);
+    method public void onStopNestedScroll(android.view.View);
+    method public void onStopNestedScroll(android.view.View, int);
+  }
+
+  public interface OnApplyWindowInsetsListener {
+    method public androidx.core.view.WindowInsetsCompat onApplyWindowInsets(android.view.View, androidx.core.view.WindowInsetsCompat);
+  }
+
+  public interface OnReceiveContentListener {
+    method public androidx.core.view.ContentInfoCompat? onReceiveContent(android.view.View, androidx.core.view.ContentInfoCompat);
+  }
+
+  public interface OnReceiveContentViewBehavior {
+    method public androidx.core.view.ContentInfoCompat? onReceiveContent(androidx.core.view.ContentInfoCompat);
+  }
+
+  public final class OneShotPreDrawListener implements android.view.View.OnAttachStateChangeListener android.view.ViewTreeObserver.OnPreDrawListener {
+    method public static androidx.core.view.OneShotPreDrawListener add(android.view.View, Runnable);
+    method public boolean onPreDraw();
+    method public void onViewAttachedToWindow(android.view.View);
+    method public void onViewDetachedFromWindow(android.view.View);
+    method public void removeListener();
+  }
+
+  public final class PointerIconCompat {
+    method public static androidx.core.view.PointerIconCompat create(android.graphics.Bitmap, float, float);
+    method public static androidx.core.view.PointerIconCompat getSystemIcon(android.content.Context, int);
+    method public static androidx.core.view.PointerIconCompat load(android.content.res.Resources, int);
+    field public static final int TYPE_ALIAS = 1010; // 0x3f2
+    field public static final int TYPE_ALL_SCROLL = 1013; // 0x3f5
+    field public static final int TYPE_ARROW = 1000; // 0x3e8
+    field public static final int TYPE_CELL = 1006; // 0x3ee
+    field public static final int TYPE_CONTEXT_MENU = 1001; // 0x3e9
+    field public static final int TYPE_COPY = 1011; // 0x3f3
+    field public static final int TYPE_CROSSHAIR = 1007; // 0x3ef
+    field public static final int TYPE_DEFAULT = 1000; // 0x3e8
+    field public static final int TYPE_GRAB = 1020; // 0x3fc
+    field public static final int TYPE_GRABBING = 1021; // 0x3fd
+    field public static final int TYPE_HAND = 1002; // 0x3ea
+    field public static final int TYPE_HELP = 1003; // 0x3eb
+    field public static final int TYPE_HORIZONTAL_DOUBLE_ARROW = 1014; // 0x3f6
+    field public static final int TYPE_NO_DROP = 1012; // 0x3f4
+    field public static final int TYPE_NULL = 0; // 0x0
+    field public static final int TYPE_TEXT = 1008; // 0x3f0
+    field public static final int TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW = 1017; // 0x3f9
+    field public static final int TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW = 1016; // 0x3f8
+    field public static final int TYPE_VERTICAL_DOUBLE_ARROW = 1015; // 0x3f7
+    field public static final int TYPE_VERTICAL_TEXT = 1009; // 0x3f1
+    field public static final int TYPE_WAIT = 1004; // 0x3ec
+    field public static final int TYPE_ZOOM_IN = 1018; // 0x3fa
+    field public static final int TYPE_ZOOM_OUT = 1019; // 0x3fb
+  }
+
+  public final class ScaleGestureDetectorCompat {
+    method public static boolean isQuickScaleEnabled(android.view.ScaleGestureDetector);
+    method @Deprecated public static boolean isQuickScaleEnabled(Object!);
+    method public static void setQuickScaleEnabled(android.view.ScaleGestureDetector, boolean);
+    method @Deprecated public static void setQuickScaleEnabled(Object!, boolean);
+  }
+
+  public interface ScrollingView {
+    method public int computeHorizontalScrollExtent();
+    method public int computeHorizontalScrollOffset();
+    method public int computeHorizontalScrollRange();
+    method public int computeVerticalScrollExtent();
+    method public int computeVerticalScrollOffset();
+    method public int computeVerticalScrollRange();
+  }
+
+  public final class SoftwareKeyboardControllerCompat {
+    ctor public SoftwareKeyboardControllerCompat(android.view.View);
+    method public void hide();
+    method public void show();
+  }
+
+  public interface TintableBackgroundView {
+    method public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  public final class VelocityTrackerCompat {
+    method public static float getAxisVelocity(android.view.VelocityTracker, int);
+    method public static float getAxisVelocity(android.view.VelocityTracker, int, int);
+    method @Deprecated public static float getXVelocity(android.view.VelocityTracker!, int);
+    method @Deprecated public static float getYVelocity(android.view.VelocityTracker!, int);
+    method public static boolean isAxisSupported(android.view.VelocityTracker, int);
+  }
+
+  public class ViewCompat {
+    ctor @Deprecated protected ViewCompat();
+    method public static int addAccessibilityAction(android.view.View, CharSequence, androidx.core.view.accessibility.AccessibilityViewCommand);
+    method public static void addKeyboardNavigationClusters(android.view.View, java.util.Collection<android.view.View!>, int);
+    method public static void addOnUnhandledKeyEventListener(android.view.View, androidx.core.view.ViewCompat.OnUnhandledKeyEventListenerCompat);
+    method public static androidx.core.view.ViewPropertyAnimatorCompat animate(android.view.View);
+    method @Deprecated public static boolean canScrollHorizontally(android.view.View!, int);
+    method @Deprecated public static boolean canScrollVertically(android.view.View!, int);
+    method public static void cancelDragAndDrop(android.view.View);
+    method @Deprecated public static int combineMeasuredStates(int, int);
+    method public static androidx.core.view.WindowInsetsCompat computeSystemWindowInsets(android.view.View, androidx.core.view.WindowInsetsCompat, android.graphics.Rect);
+    method public static androidx.core.view.WindowInsetsCompat dispatchApplyWindowInsets(android.view.View, androidx.core.view.WindowInsetsCompat);
+    method public static void dispatchFinishTemporaryDetach(android.view.View);
+    method public static boolean dispatchNestedFling(android.view.View, float, float, boolean);
+    method public static boolean dispatchNestedPreFling(android.view.View, float, float);
+    method public static boolean dispatchNestedPreScroll(android.view.View, int, int, int[]?, int[]?);
+    method public static boolean dispatchNestedPreScroll(android.view.View, int, int, int[]?, int[]?, int);
+    method public static boolean dispatchNestedScroll(android.view.View, int, int, int, int, int[]?);
+    method public static boolean dispatchNestedScroll(android.view.View, int, int, int, int, int[]?, int);
+    method public static void dispatchNestedScroll(android.view.View, int, int, int, int, int[]?, int, int[]);
+    method public static void dispatchStartTemporaryDetach(android.view.View);
+    method public static void enableAccessibleClickableSpanSupport(android.view.View);
+    method public static int generateViewId();
+    method public static androidx.core.view.AccessibilityDelegateCompat? getAccessibilityDelegate(android.view.View);
+    method public static int getAccessibilityLiveRegion(android.view.View);
+    method public static androidx.core.view.accessibility.AccessibilityNodeProviderCompat? getAccessibilityNodeProvider(android.view.View);
+    method @UiThread public static CharSequence? getAccessibilityPaneTitle(android.view.View);
+    method @Deprecated public static float getAlpha(android.view.View!);
+    method public static androidx.core.view.autofill.AutofillIdCompat? getAutofillId(android.view.View);
+    method public static android.content.res.ColorStateList? getBackgroundTintList(android.view.View);
+    method public static android.graphics.PorterDuff.Mode? getBackgroundTintMode(android.view.View);
+    method public static android.graphics.Rect? getClipBounds(android.view.View);
+    method public static androidx.core.view.contentcapture.ContentCaptureSessionCompat? getContentCaptureSession(android.view.View);
+    method public static android.view.Display? getDisplay(android.view.View);
+    method public static float getElevation(android.view.View);
+    method public static boolean getFitsSystemWindows(android.view.View);
+    method public static int getImportantForAccessibility(android.view.View);
+    method public static int getImportantForAutofill(android.view.View);
+    method public static int getImportantForContentCapture(android.view.View);
+    method public static int getLabelFor(android.view.View);
+    method @Deprecated public static int getLayerType(android.view.View!);
+    method public static int getLayoutDirection(android.view.View);
+    method @Deprecated public static android.graphics.Matrix? getMatrix(android.view.View!);
+    method @Deprecated public static int getMeasuredHeightAndState(android.view.View!);
+    method @Deprecated public static int getMeasuredState(android.view.View!);
+    method @Deprecated public static int getMeasuredWidthAndState(android.view.View!);
+    method public static int getMinimumHeight(android.view.View);
+    method public static int getMinimumWidth(android.view.View);
+    method public static int getNextClusterForwardId(android.view.View);
+    method public static String![]? getOnReceiveContentMimeTypes(android.view.View);
+    method @Deprecated public static int getOverScrollMode(android.view.View!);
+    method @Px public static int getPaddingEnd(android.view.View);
+    method @Px public static int getPaddingStart(android.view.View);
+    method public static android.view.ViewParent? getParentForAccessibility(android.view.View);
+    method @Deprecated public static float getPivotX(android.view.View!);
+    method @Deprecated public static float getPivotY(android.view.View!);
+    method public static androidx.core.view.WindowInsetsCompat? getRootWindowInsets(android.view.View);
+    method @Deprecated public static float getRotation(android.view.View!);
+    method @Deprecated public static float getRotationX(android.view.View!);
+    method @Deprecated public static float getRotationY(android.view.View!);
+    method @Deprecated public static float getScaleX(android.view.View!);
+    method @Deprecated public static float getScaleY(android.view.View!);
+    method public static int getScrollIndicators(android.view.View);
+    method @UiThread public static CharSequence? getStateDescription(android.view.View);
+    method public static java.util.List<android.graphics.Rect!> getSystemGestureExclusionRects(android.view.View);
+    method public static String? getTransitionName(android.view.View);
+    method @Deprecated public static float getTranslationX(android.view.View!);
+    method @Deprecated public static float getTranslationY(android.view.View!);
+    method public static float getTranslationZ(android.view.View);
+    method @Deprecated public static androidx.core.view.WindowInsetsControllerCompat? getWindowInsetsController(android.view.View);
+    method @Deprecated public static int getWindowSystemUiVisibility(android.view.View);
+    method @Deprecated public static float getX(android.view.View!);
+    method @Deprecated public static float getY(android.view.View!);
+    method public static float getZ(android.view.View);
+    method public static boolean hasAccessibilityDelegate(android.view.View);
+    method public static boolean hasExplicitFocusable(android.view.View);
+    method public static boolean hasNestedScrollingParent(android.view.View);
+    method public static boolean hasNestedScrollingParent(android.view.View, int);
+    method public static boolean hasOnClickListeners(android.view.View);
+    method public static boolean hasOverlappingRendering(android.view.View);
+    method public static boolean hasTransientState(android.view.View);
+    method @UiThread public static boolean isAccessibilityHeading(android.view.View);
+    method public static boolean isAttachedToWindow(android.view.View);
+    method public static boolean isFocusedByDefault(android.view.View);
+    method public static boolean isImportantForAccessibility(android.view.View);
+    method public static boolean isImportantForAutofill(android.view.View);
+    method public static boolean isImportantForContentCapture(android.view.View);
+    method public static boolean isInLayout(android.view.View);
+    method public static boolean isKeyboardNavigationCluster(android.view.View);
+    method public static boolean isLaidOut(android.view.View);
+    method public static boolean isLayoutDirectionResolved(android.view.View);
+    method public static boolean isNestedScrollingEnabled(android.view.View);
+    method @Deprecated public static boolean isOpaque(android.view.View!);
+    method public static boolean isPaddingRelative(android.view.View);
+    method @UiThread public static boolean isScreenReaderFocusable(android.view.View);
+    method @Deprecated public static void jumpDrawablesToCurrentState(android.view.View!);
+    method public static android.view.View? keyboardNavigationClusterSearch(android.view.View, android.view.View?, int);
+    method public static void offsetLeftAndRight(android.view.View, int);
+    method public static void offsetTopAndBottom(android.view.View, int);
+    method public static androidx.core.view.WindowInsetsCompat onApplyWindowInsets(android.view.View, androidx.core.view.WindowInsetsCompat);
+    method @Deprecated public static void onInitializeAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
+    method public static void onInitializeAccessibilityNodeInfo(android.view.View, androidx.core.view.accessibility.AccessibilityNodeInfoCompat);
+    method @Deprecated public static void onPopulateAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
+    method public static boolean performAccessibilityAction(android.view.View, int, android.os.Bundle?);
+    method public static boolean performHapticFeedback(android.view.View, int);
+    method public static boolean performHapticFeedback(android.view.View, int, int);
+    method public static androidx.core.view.ContentInfoCompat? performReceiveContent(android.view.View, androidx.core.view.ContentInfoCompat);
+    method public static void postInvalidateOnAnimation(android.view.View);
+    method public static void postInvalidateOnAnimation(android.view.View, int, int, int, int);
+    method public static void postOnAnimation(android.view.View, Runnable);
+    method public static void postOnAnimationDelayed(android.view.View, Runnable, long);
+    method public static void removeAccessibilityAction(android.view.View, int);
+    method public static void removeOnUnhandledKeyEventListener(android.view.View, androidx.core.view.ViewCompat.OnUnhandledKeyEventListenerCompat);
+    method public static void replaceAccessibilityAction(android.view.View, androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat, CharSequence?, androidx.core.view.accessibility.AccessibilityViewCommand?);
+    method public static void requestApplyInsets(android.view.View);
+    method public static <T extends android.view.View> T requireViewById(android.view.View, @IdRes int);
+    method @Deprecated public static int resolveSizeAndState(int, int, int);
+    method public static boolean restoreDefaultFocus(android.view.View);
+    method public static void saveAttributeDataForStyleable(android.view.View, android.content.Context, int[], android.util.AttributeSet?, android.content.res.TypedArray, int, int);
+    method public static void setAccessibilityDelegate(android.view.View, androidx.core.view.AccessibilityDelegateCompat?);
+    method @UiThread public static void setAccessibilityHeading(android.view.View, boolean);
+    method public static void setAccessibilityLiveRegion(android.view.View, int);
+    method @UiThread public static void setAccessibilityPaneTitle(android.view.View, CharSequence?);
+    method @Deprecated public static void setActivated(android.view.View!, boolean);
+    method @Deprecated public static void setAlpha(android.view.View!, @FloatRange(from=0.0, to=1.0) float);
+    method public static void setAutofillHints(android.view.View, java.lang.String!...);
+    method public static void setAutofillId(android.view.View, androidx.core.view.autofill.AutofillIdCompat?);
+    method public static void setBackground(android.view.View, android.graphics.drawable.Drawable?);
+    method public static void setBackgroundTintList(android.view.View, android.content.res.ColorStateList?);
+    method public static void setBackgroundTintMode(android.view.View, android.graphics.PorterDuff.Mode?);
+    method @Deprecated public static void setChildrenDrawingOrderEnabled(android.view.ViewGroup!, boolean);
+    method public static void setClipBounds(android.view.View, android.graphics.Rect?);
+    method public static void setContentCaptureSession(android.view.View, androidx.core.view.contentcapture.ContentCaptureSessionCompat?);
+    method public static void setElevation(android.view.View, float);
+    method @Deprecated public static void setFitsSystemWindows(android.view.View!, boolean);
+    method public static void setFocusedByDefault(android.view.View, boolean);
+    method public static void setHasTransientState(android.view.View, boolean);
+    method @UiThread public static void setImportantForAccessibility(android.view.View, int);
+    method public static void setImportantForAutofill(android.view.View, int);
+    method public static void setImportantForContentCapture(android.view.View, int);
+    method public static void setKeyboardNavigationCluster(android.view.View, boolean);
+    method public static void setLabelFor(android.view.View, @IdRes int);
+    method public static void setLayerPaint(android.view.View, android.graphics.Paint?);
+    method @Deprecated public static void setLayerType(android.view.View!, int, android.graphics.Paint!);
+    method public static void setLayoutDirection(android.view.View, int);
+    method public static void setNestedScrollingEnabled(android.view.View, boolean);
+    method public static void setNextClusterForwardId(android.view.View, int);
+    method public static void setOnApplyWindowInsetsListener(android.view.View, androidx.core.view.OnApplyWindowInsetsListener?);
+    method public static void setOnReceiveContentListener(android.view.View, String![]?, androidx.core.view.OnReceiveContentListener?);
+    method @Deprecated public static void setOverScrollMode(android.view.View!, int);
+    method public static void setPaddingRelative(android.view.View, @Px int, @Px int, @Px int, @Px int);
+    method @Deprecated public static void setPivotX(android.view.View!, float);
+    method @Deprecated public static void setPivotY(android.view.View!, float);
+    method public static void setPointerIcon(android.view.View, androidx.core.view.PointerIconCompat?);
+    method @Deprecated public static void setRotation(android.view.View!, float);
+    method @Deprecated public static void setRotationX(android.view.View!, float);
+    method @Deprecated public static void setRotationY(android.view.View!, float);
+    method @Deprecated public static void setSaveFromParentEnabled(android.view.View!, boolean);
+    method @Deprecated public static void setScaleX(android.view.View!, float);
+    method @Deprecated public static void setScaleY(android.view.View!, float);
+    method @UiThread public static void setScreenReaderFocusable(android.view.View, boolean);
+    method public static void setScrollIndicators(android.view.View, int);
+    method public static void setScrollIndicators(android.view.View, int, int);
+    method @UiThread public static void setStateDescription(android.view.View, CharSequence?);
+    method public static void setSystemGestureExclusionRects(android.view.View, java.util.List<android.graphics.Rect!>);
+    method public static void setTooltipText(android.view.View, CharSequence?);
+    method public static void setTransitionName(android.view.View, String?);
+    method @Deprecated public static void setTranslationX(android.view.View!, float);
+    method @Deprecated public static void setTranslationY(android.view.View!, float);
+    method public static void setTranslationZ(android.view.View, float);
+    method public static void setWindowInsetsAnimationCallback(android.view.View, androidx.core.view.WindowInsetsAnimationCompat.Callback?);
+    method @Deprecated public static void setX(android.view.View!, float);
+    method @Deprecated public static void setY(android.view.View!, float);
+    method public static void setZ(android.view.View, float);
+    method public static boolean startDragAndDrop(android.view.View, android.content.ClipData?, android.view.View.DragShadowBuilder, Object?, int);
+    method public static boolean startNestedScroll(android.view.View, int);
+    method public static boolean startNestedScroll(android.view.View, int, int);
+    method public static void stopNestedScroll(android.view.View);
+    method public static void stopNestedScroll(android.view.View, int);
+    method public static void updateDragShadow(android.view.View, android.view.View.DragShadowBuilder);
+    field public static final int ACCESSIBILITY_LIVE_REGION_ASSERTIVE = 2; // 0x2
+    field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
+    field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_AUTO = 0; // 0x0
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 2; // 0x2
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 4; // 0x4
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
+    field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_AUTO = 0; // 0x0
+    field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO = 2; // 0x2
+    field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS = 8; // 0x8
+    field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES = 1; // 0x1
+    field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS = 4; // 0x4
+    field @Deprecated public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
+    field @Deprecated public static final int LAYER_TYPE_NONE = 0; // 0x0
+    field @Deprecated public static final int LAYER_TYPE_SOFTWARE = 1; // 0x1
+    field public static final int LAYOUT_DIRECTION_INHERIT = 2; // 0x2
+    field public static final int LAYOUT_DIRECTION_LOCALE = 3; // 0x3
+    field public static final int LAYOUT_DIRECTION_LTR = 0; // 0x0
+    field public static final int LAYOUT_DIRECTION_RTL = 1; // 0x1
+    field @Deprecated public static final int MEASURED_HEIGHT_STATE_SHIFT = 16; // 0x10
+    field @Deprecated public static final int MEASURED_SIZE_MASK = 16777215; // 0xffffff
+    field @Deprecated public static final int MEASURED_STATE_MASK = -16777216; // 0xff000000
+    field @Deprecated public static final int MEASURED_STATE_TOO_SMALL = 16777216; // 0x1000000
+    field @Deprecated public static final int OVER_SCROLL_ALWAYS = 0; // 0x0
+    field @Deprecated public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1; // 0x1
+    field @Deprecated public static final int OVER_SCROLL_NEVER = 2; // 0x2
+    field public static final int SCROLL_AXIS_HORIZONTAL = 1; // 0x1
+    field public static final int SCROLL_AXIS_NONE = 0; // 0x0
+    field public static final int SCROLL_AXIS_VERTICAL = 2; // 0x2
+    field public static final int SCROLL_INDICATOR_BOTTOM = 2; // 0x2
+    field public static final int SCROLL_INDICATOR_END = 32; // 0x20
+    field public static final int SCROLL_INDICATOR_LEFT = 4; // 0x4
+    field public static final int SCROLL_INDICATOR_RIGHT = 8; // 0x8
+    field public static final int SCROLL_INDICATOR_START = 16; // 0x10
+    field public static final int SCROLL_INDICATOR_TOP = 1; // 0x1
+    field public static final int TYPE_NON_TOUCH = 1; // 0x1
+    field public static final int TYPE_TOUCH = 0; // 0x0
+  }
+
+  public static interface ViewCompat.OnUnhandledKeyEventListenerCompat {
+    method public boolean onUnhandledKeyEvent(android.view.View, android.view.KeyEvent);
+  }
+
+  public final class ViewConfigurationCompat {
+    method public static float getScaledHorizontalScrollFactor(android.view.ViewConfiguration, android.content.Context);
+    method public static int getScaledHoverSlop(android.view.ViewConfiguration);
+    method public static int getScaledMaximumFlingVelocity(android.content.Context, android.view.ViewConfiguration, int, int, int);
+    method public static int getScaledMinimumFlingVelocity(android.content.Context, android.view.ViewConfiguration, int, int, int);
+    method @Deprecated public static int getScaledPagingTouchSlop(android.view.ViewConfiguration!);
+    method public static float getScaledVerticalScrollFactor(android.view.ViewConfiguration, android.content.Context);
+    method @Deprecated public static boolean hasPermanentMenuKey(android.view.ViewConfiguration!);
+    method public static boolean shouldShowMenuShortcutsWhenKeyboardPresent(android.view.ViewConfiguration, android.content.Context);
+  }
+
+  public final class ViewGroupCompat {
+    method public static int getLayoutMode(android.view.ViewGroup);
+    method public static int getNestedScrollAxes(android.view.ViewGroup);
+    method public static boolean isTransitionGroup(android.view.ViewGroup);
+    method @Deprecated public static boolean onRequestSendAccessibilityEvent(android.view.ViewGroup!, android.view.View!, android.view.accessibility.AccessibilityEvent!);
+    method public static void setLayoutMode(android.view.ViewGroup, int);
+    method @Deprecated public static void setMotionEventSplittingEnabled(android.view.ViewGroup!, boolean);
+    method public static void setTransitionGroup(android.view.ViewGroup, boolean);
+    field public static final int LAYOUT_MODE_CLIP_BOUNDS = 0; // 0x0
+    field public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1; // 0x1
+  }
+
+  public final class ViewParentCompat {
+    method public static void notifySubtreeAccessibilityStateChanged(android.view.ViewParent, android.view.View, android.view.View, int);
+    method public static boolean onNestedFling(android.view.ViewParent, android.view.View, float, float, boolean);
+    method public static boolean onNestedPreFling(android.view.ViewParent, android.view.View, float, float);
+    method public static void onNestedPreScroll(android.view.ViewParent, android.view.View, int, int, int[]);
+    method public static void onNestedPreScroll(android.view.ViewParent, android.view.View, int, int, int[], int);
+    method public static void onNestedScroll(android.view.ViewParent, android.view.View, int, int, int, int);
+    method public static void onNestedScroll(android.view.ViewParent, android.view.View, int, int, int, int, int);
+    method public static void onNestedScroll(android.view.ViewParent, android.view.View, int, int, int, int, int, int[]);
+    method public static void onNestedScrollAccepted(android.view.ViewParent, android.view.View, android.view.View, int);
+    method public static void onNestedScrollAccepted(android.view.ViewParent, android.view.View, android.view.View, int, int);
+    method public static boolean onStartNestedScroll(android.view.ViewParent, android.view.View, android.view.View, int);
+    method public static boolean onStartNestedScroll(android.view.ViewParent, android.view.View, android.view.View, int, int);
+    method public static void onStopNestedScroll(android.view.ViewParent, android.view.View);
+    method public static void onStopNestedScroll(android.view.ViewParent, android.view.View, int);
+    method @Deprecated public static boolean requestSendAccessibilityEvent(android.view.ViewParent!, android.view.View!, android.view.accessibility.AccessibilityEvent!);
+  }
+
+  public final class ViewPropertyAnimatorCompat {
+    method public androidx.core.view.ViewPropertyAnimatorCompat alpha(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat alphaBy(float);
+    method public void cancel();
+    method public long getDuration();
+    method public android.view.animation.Interpolator? getInterpolator();
+    method public long getStartDelay();
+    method public androidx.core.view.ViewPropertyAnimatorCompat rotation(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat rotationBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat rotationX(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat rotationXBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat rotationY(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat rotationYBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat scaleX(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat scaleXBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat scaleY(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat scaleYBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat setDuration(long);
+    method public androidx.core.view.ViewPropertyAnimatorCompat setInterpolator(android.view.animation.Interpolator?);
+    method public androidx.core.view.ViewPropertyAnimatorCompat setListener(androidx.core.view.ViewPropertyAnimatorListener?);
+    method public androidx.core.view.ViewPropertyAnimatorCompat setStartDelay(long);
+    method public androidx.core.view.ViewPropertyAnimatorCompat setUpdateListener(androidx.core.view.ViewPropertyAnimatorUpdateListener?);
+    method public void start();
+    method public androidx.core.view.ViewPropertyAnimatorCompat translationX(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat translationXBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat translationY(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat translationYBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat translationZ(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat translationZBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat withEndAction(Runnable);
+    method public androidx.core.view.ViewPropertyAnimatorCompat withLayer();
+    method public androidx.core.view.ViewPropertyAnimatorCompat withStartAction(Runnable);
+    method public androidx.core.view.ViewPropertyAnimatorCompat x(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat xBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat y(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat yBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat z(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat zBy(float);
+  }
+
+  public interface ViewPropertyAnimatorListener {
+    method public void onAnimationCancel(android.view.View);
+    method public void onAnimationEnd(android.view.View);
+    method public void onAnimationStart(android.view.View);
+  }
+
+  public class ViewPropertyAnimatorListenerAdapter implements androidx.core.view.ViewPropertyAnimatorListener {
+    ctor public ViewPropertyAnimatorListenerAdapter();
+    method public void onAnimationCancel(android.view.View);
+    method public void onAnimationEnd(android.view.View);
+    method public void onAnimationStart(android.view.View);
+  }
+
+  public interface ViewPropertyAnimatorUpdateListener {
+    method public void onAnimationUpdate(android.view.View);
+  }
+
+  public class ViewStructureCompat {
+    method public void setClassName(String);
+    method public void setContentDescription(CharSequence);
+    method public void setDimens(int, int, int, int, int, int);
+    method public void setText(CharSequence);
+    method @RequiresApi(23) public android.view.ViewStructure toViewStructure();
+    method @RequiresApi(23) public static androidx.core.view.ViewStructureCompat toViewStructureCompat(android.view.ViewStructure);
+  }
+
+  public final class WindowCompat {
+    method public static androidx.core.view.WindowInsetsControllerCompat getInsetsController(android.view.Window, android.view.View);
+    method public static <T extends android.view.View> T requireViewById(android.view.Window, @IdRes int);
+    method public static void setDecorFitsSystemWindows(android.view.Window, boolean);
+    field public static final int FEATURE_ACTION_BAR = 8; // 0x8
+    field public static final int FEATURE_ACTION_BAR_OVERLAY = 9; // 0x9
+    field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
+  }
+
+  public final class WindowInsetsAnimationCompat {
+    ctor public WindowInsetsAnimationCompat(int, android.view.animation.Interpolator?, long);
+    method @FloatRange(from=0.0f, to=1.0f) public float getAlpha();
+    method public long getDurationMillis();
+    method @FloatRange(from=0.0f, to=1.0f) public float getFraction();
+    method public float getInterpolatedFraction();
+    method public android.view.animation.Interpolator? getInterpolator();
+    method public int getTypeMask();
+    method public void setAlpha(@FloatRange(from=0.0f, to=1.0f) float);
+    method public void setFraction(@FloatRange(from=0.0f, to=1.0f) float);
+  }
+
+  public static final class WindowInsetsAnimationCompat.BoundsCompat {
+    ctor public WindowInsetsAnimationCompat.BoundsCompat(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+    method public androidx.core.graphics.Insets getLowerBound();
+    method public androidx.core.graphics.Insets getUpperBound();
+    method public androidx.core.view.WindowInsetsAnimationCompat.BoundsCompat inset(androidx.core.graphics.Insets);
+    method @RequiresApi(30) public android.view.WindowInsetsAnimation.Bounds toBounds();
+    method @RequiresApi(30) public static androidx.core.view.WindowInsetsAnimationCompat.BoundsCompat toBoundsCompat(android.view.WindowInsetsAnimation.Bounds);
+  }
+
+  public abstract static class WindowInsetsAnimationCompat.Callback {
+    ctor public WindowInsetsAnimationCompat.Callback(int);
+    method public final int getDispatchMode();
+    method public void onEnd(androidx.core.view.WindowInsetsAnimationCompat);
+    method public void onPrepare(androidx.core.view.WindowInsetsAnimationCompat);
+    method public abstract androidx.core.view.WindowInsetsCompat onProgress(androidx.core.view.WindowInsetsCompat, java.util.List<androidx.core.view.WindowInsetsAnimationCompat!>);
+    method public androidx.core.view.WindowInsetsAnimationCompat.BoundsCompat onStart(androidx.core.view.WindowInsetsAnimationCompat, androidx.core.view.WindowInsetsAnimationCompat.BoundsCompat);
+    field public static final int DISPATCH_MODE_CONTINUE_ON_SUBTREE = 1; // 0x1
+    field public static final int DISPATCH_MODE_STOP = 0; // 0x0
+  }
+
+  public interface WindowInsetsAnimationControlListenerCompat {
+    method public void onCancelled(androidx.core.view.WindowInsetsAnimationControllerCompat?);
+    method public void onFinished(androidx.core.view.WindowInsetsAnimationControllerCompat);
+    method public void onReady(androidx.core.view.WindowInsetsAnimationControllerCompat, int);
+  }
+
+  public final class WindowInsetsAnimationControllerCompat {
+    method public void finish(boolean);
+    method public float getCurrentAlpha();
+    method @FloatRange(from=0.0f, to=1.0f) public float getCurrentFraction();
+    method public androidx.core.graphics.Insets getCurrentInsets();
+    method public androidx.core.graphics.Insets getHiddenStateInsets();
+    method public androidx.core.graphics.Insets getShownStateInsets();
+    method public int getTypes();
+    method public boolean isCancelled();
+    method public boolean isFinished();
+    method public boolean isReady();
+    method public void setInsetsAndAlpha(androidx.core.graphics.Insets?, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
+  }
+
+  public class WindowInsetsCompat {
+    ctor public WindowInsetsCompat(androidx.core.view.WindowInsetsCompat?);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat consumeDisplayCutout();
+    method @Deprecated public androidx.core.view.WindowInsetsCompat consumeStableInsets();
+    method @Deprecated public androidx.core.view.WindowInsetsCompat consumeSystemWindowInsets();
+    method public androidx.core.view.DisplayCutoutCompat? getDisplayCutout();
+    method public androidx.core.graphics.Insets getInsets(int);
+    method public androidx.core.graphics.Insets getInsetsIgnoringVisibility(int);
+    method @Deprecated public androidx.core.graphics.Insets getMandatorySystemGestureInsets();
+    method @Deprecated public int getStableInsetBottom();
+    method @Deprecated public int getStableInsetLeft();
+    method @Deprecated public int getStableInsetRight();
+    method @Deprecated public int getStableInsetTop();
+    method @Deprecated public androidx.core.graphics.Insets getStableInsets();
+    method @Deprecated public androidx.core.graphics.Insets getSystemGestureInsets();
+    method @Deprecated public int getSystemWindowInsetBottom();
+    method @Deprecated public int getSystemWindowInsetLeft();
+    method @Deprecated public int getSystemWindowInsetRight();
+    method @Deprecated public int getSystemWindowInsetTop();
+    method @Deprecated public androidx.core.graphics.Insets getSystemWindowInsets();
+    method @Deprecated public androidx.core.graphics.Insets getTappableElementInsets();
+    method public boolean hasInsets();
+    method @Deprecated public boolean hasStableInsets();
+    method @Deprecated public boolean hasSystemWindowInsets();
+    method public androidx.core.view.WindowInsetsCompat inset(androidx.core.graphics.Insets);
+    method public androidx.core.view.WindowInsetsCompat inset(@IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+    method public boolean isConsumed();
+    method public boolean isRound();
+    method public boolean isVisible(int);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat replaceSystemWindowInsets(android.graphics.Rect);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat replaceSystemWindowInsets(int, int, int, int);
+    method @RequiresApi(20) public android.view.WindowInsets? toWindowInsets();
+    method @RequiresApi(20) public static androidx.core.view.WindowInsetsCompat toWindowInsetsCompat(android.view.WindowInsets);
+    method @RequiresApi(20) public static androidx.core.view.WindowInsetsCompat toWindowInsetsCompat(android.view.WindowInsets, android.view.View?);
+    field public static final androidx.core.view.WindowInsetsCompat CONSUMED;
+  }
+
+  public static final class WindowInsetsCompat.Builder {
+    ctor public WindowInsetsCompat.Builder();
+    ctor public WindowInsetsCompat.Builder(androidx.core.view.WindowInsetsCompat);
+    method public androidx.core.view.WindowInsetsCompat build();
+    method public androidx.core.view.WindowInsetsCompat.Builder setDisplayCutout(androidx.core.view.DisplayCutoutCompat?);
+    method public androidx.core.view.WindowInsetsCompat.Builder setInsets(int, androidx.core.graphics.Insets);
+    method public androidx.core.view.WindowInsetsCompat.Builder setInsetsIgnoringVisibility(int, androidx.core.graphics.Insets);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setMandatorySystemGestureInsets(androidx.core.graphics.Insets);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setStableInsets(androidx.core.graphics.Insets);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setSystemGestureInsets(androidx.core.graphics.Insets);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setSystemWindowInsets(androidx.core.graphics.Insets);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setTappableElementInsets(androidx.core.graphics.Insets);
+    method public androidx.core.view.WindowInsetsCompat.Builder setVisible(int, boolean);
+  }
+
+  public static final class WindowInsetsCompat.Type {
+    method public static int captionBar();
+    method public static int displayCutout();
+    method public static int ime();
+    method public static int mandatorySystemGestures();
+    method public static int navigationBars();
+    method public static int statusBars();
+    method public static int systemBars();
+    method public static int systemGestures();
+    method public static int tappableElement();
+  }
+
+  public final class WindowInsetsControllerCompat {
+    ctor public WindowInsetsControllerCompat(android.view.Window, android.view.View);
+    method public void addOnControllableInsetsChangedListener(androidx.core.view.WindowInsetsControllerCompat.OnControllableInsetsChangedListener);
+    method public void controlWindowInsetsAnimation(int, long, android.view.animation.Interpolator?, android.os.CancellationSignal?, androidx.core.view.WindowInsetsAnimationControlListenerCompat);
+    method public int getSystemBarsBehavior();
+    method public void hide(int);
+    method public boolean isAppearanceLightNavigationBars();
+    method public boolean isAppearanceLightStatusBars();
+    method public void removeOnControllableInsetsChangedListener(androidx.core.view.WindowInsetsControllerCompat.OnControllableInsetsChangedListener);
+    method public void setAppearanceLightNavigationBars(boolean);
+    method public void setAppearanceLightStatusBars(boolean);
+    method public void setSystemBarsBehavior(int);
+    method public void show(int);
+    method @Deprecated @RequiresApi(30) public static androidx.core.view.WindowInsetsControllerCompat toWindowInsetsControllerCompat(android.view.WindowInsetsController);
+    field public static final int BEHAVIOR_DEFAULT = 1; // 0x1
+    field @Deprecated public static final int BEHAVIOR_SHOW_BARS_BY_SWIPE = 1; // 0x1
+    field @Deprecated public static final int BEHAVIOR_SHOW_BARS_BY_TOUCH = 0; // 0x0
+    field public static final int BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE = 2; // 0x2
+  }
+
+  public static interface WindowInsetsControllerCompat.OnControllableInsetsChangedListener {
+    method public void onControllableInsetsChanged(androidx.core.view.WindowInsetsControllerCompat, int);
+  }
+
+}
+
+package androidx.core.view.accessibility {
+
+  public final class AccessibilityClickableSpanCompat extends android.text.style.ClickableSpan {
+    method public void onClick(android.view.View);
+  }
+
+  public final class AccessibilityEventCompat {
+    method @Deprecated public static void appendRecord(android.view.accessibility.AccessibilityEvent!, androidx.core.view.accessibility.AccessibilityRecordCompat!);
+    method @Deprecated public static androidx.core.view.accessibility.AccessibilityRecordCompat! asRecord(android.view.accessibility.AccessibilityEvent!);
+    method public static int getAction(android.view.accessibility.AccessibilityEvent);
+    method public static int getContentChangeTypes(android.view.accessibility.AccessibilityEvent);
+    method public static int getMovementGranularity(android.view.accessibility.AccessibilityEvent);
+    method @Deprecated public static androidx.core.view.accessibility.AccessibilityRecordCompat! getRecord(android.view.accessibility.AccessibilityEvent!, int);
+    method @Deprecated public static int getRecordCount(android.view.accessibility.AccessibilityEvent!);
+    method public static boolean isAccessibilityDataSensitive(android.view.accessibility.AccessibilityEvent);
+    method public static void setAccessibilityDataSensitive(android.view.accessibility.AccessibilityEvent, boolean);
+    method public static void setAction(android.view.accessibility.AccessibilityEvent, int);
+    method public static void setContentChangeTypes(android.view.accessibility.AccessibilityEvent, int);
+    method public static void setMovementGranularity(android.view.accessibility.AccessibilityEvent, int);
+    field public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 4; // 0x4
+    field public static final int CONTENT_CHANGE_TYPE_CONTENT_INVALID = 1024; // 0x400
+    field public static final int CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 512; // 0x200
+    field public static final int CONTENT_CHANGE_TYPE_DRAG_DROPPED = 256; // 0x100
+    field public static final int CONTENT_CHANGE_TYPE_DRAG_STARTED = 128; // 0x80
+    field public static final int CONTENT_CHANGE_TYPE_ENABLED = 4096; // 0x1000
+    field public static final int CONTENT_CHANGE_TYPE_ERROR = 2048; // 0x800
+    field public static final int CONTENT_CHANGE_TYPE_PANE_APPEARED = 16; // 0x10
+    field public static final int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 32; // 0x20
+    field public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 8; // 0x8
+    field public static final int CONTENT_CHANGE_TYPE_STATE_DESCRIPTION = 64; // 0x40
+    field public static final int CONTENT_CHANGE_TYPE_SUBTREE = 1; // 0x1
+    field public static final int CONTENT_CHANGE_TYPE_TEXT = 2; // 0x2
+    field public static final int CONTENT_CHANGE_TYPE_UNDEFINED = 0; // 0x0
+    field public static final int TYPES_ALL_MASK = -1; // 0xffffffff
+    field public static final int TYPE_ANNOUNCEMENT = 16384; // 0x4000
+    field public static final int TYPE_ASSIST_READING_CONTEXT = 16777216; // 0x1000000
+    field public static final int TYPE_GESTURE_DETECTION_END = 524288; // 0x80000
+    field public static final int TYPE_GESTURE_DETECTION_START = 262144; // 0x40000
+    field @Deprecated public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END = 1024; // 0x400
+    field @Deprecated public static final int TYPE_TOUCH_EXPLORATION_GESTURE_START = 512; // 0x200
+    field public static final int TYPE_TOUCH_INTERACTION_END = 2097152; // 0x200000
+    field public static final int TYPE_TOUCH_INTERACTION_START = 1048576; // 0x100000
+    field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUSED = 32768; // 0x8000
+    field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 65536; // 0x10000
+    field public static final int TYPE_VIEW_CONTEXT_CLICKED = 8388608; // 0x800000
+    field @Deprecated public static final int TYPE_VIEW_HOVER_ENTER = 128; // 0x80
+    field @Deprecated public static final int TYPE_VIEW_HOVER_EXIT = 256; // 0x100
+    field @Deprecated public static final int TYPE_VIEW_SCROLLED = 4096; // 0x1000
+    field public static final int TYPE_VIEW_TARGETED_BY_SCROLL = 67108864; // 0x4000000
+    field @Deprecated public static final int TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192; // 0x2000
+    field public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 131072; // 0x20000
+    field public static final int TYPE_WINDOWS_CHANGED = 4194304; // 0x400000
+    field @Deprecated public static final int TYPE_WINDOW_CONTENT_CHANGED = 2048; // 0x800
+  }
+
+  public final class AccessibilityManagerCompat {
+    method @Deprecated public static boolean addAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager!, androidx.core.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener!);
+    method public static boolean addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager, androidx.core.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener);
+    method @Deprecated public static java.util.List<android.accessibilityservice.AccessibilityServiceInfo!>! getEnabledAccessibilityServiceList(android.view.accessibility.AccessibilityManager!, int);
+    method @Deprecated public static java.util.List<android.accessibilityservice.AccessibilityServiceInfo!>! getInstalledAccessibilityServiceList(android.view.accessibility.AccessibilityManager!);
+    method public static boolean isRequestFromAccessibilityTool(android.view.accessibility.AccessibilityManager);
+    method @Deprecated public static boolean isTouchExplorationEnabled(android.view.accessibility.AccessibilityManager!);
+    method @Deprecated public static boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager!, androidx.core.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener!);
+    method public static boolean removeTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager, androidx.core.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener);
+  }
+
+  @Deprecated public static interface AccessibilityManagerCompat.AccessibilityStateChangeListener {
+    method @Deprecated public void onAccessibilityStateChanged(boolean);
+  }
+
+  @Deprecated public abstract static class AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat implements androidx.core.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener {
+    ctor @Deprecated public AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat();
+  }
+
+  public static interface AccessibilityManagerCompat.TouchExplorationStateChangeListener {
+    method public void onTouchExplorationStateChanged(boolean);
+  }
+
+  public class AccessibilityNodeInfoCompat {
+    ctor @Deprecated public AccessibilityNodeInfoCompat(Object!);
+    method public void addAction(androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat!);
+    method public void addAction(int);
+    method public void addChild(android.view.View!);
+    method public void addChild(android.view.View!, int);
+    method public boolean canOpenPopup();
+    method public java.util.List<androidx.core.view.accessibility.AccessibilityNodeInfoCompat!>! findAccessibilityNodeInfosByText(String!);
+    method public java.util.List<androidx.core.view.accessibility.AccessibilityNodeInfoCompat!>! findAccessibilityNodeInfosByViewId(String!);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! findFocus(int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! focusSearch(int);
+    method public java.util.List<androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat!>! getActionList();
+    method @Deprecated public int getActions();
+    method public java.util.List<java.lang.String!> getAvailableExtraData();
+    method @Deprecated public void getBoundsInParent(android.graphics.Rect!);
+    method public void getBoundsInScreen(android.graphics.Rect!);
+    method public void getBoundsInWindow(android.graphics.Rect);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getChild(int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? getChild(int, int);
+    method public int getChildCount();
+    method public CharSequence! getClassName();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat! getCollectionInfo();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat! getCollectionItemInfo();
+    method public CharSequence? getContainerTitle();
+    method public CharSequence! getContentDescription();
+    method public int getDrawingOrder();
+    method public CharSequence! getError();
+    method public android.view.accessibility.AccessibilityNodeInfo.ExtraRenderingInfo? getExtraRenderingInfo();
+    method public android.os.Bundle! getExtras();
+    method public CharSequence? getHintText();
+    method @Deprecated public Object! getInfo();
+    method public int getInputType();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getLabelFor();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getLabeledBy();
+    method public int getLiveRegion();
+    method public int getMaxTextLength();
+    method public long getMinDurationBetweenContentChangesMillis();
+    method public int getMovementGranularities();
+    method public CharSequence! getPackageName();
+    method public CharSequence? getPaneTitle();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getParent();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? getParent(int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat! getRangeInfo();
+    method public CharSequence? getRoleDescription();
+    method public CharSequence? getStateDescription();
+    method public CharSequence! getText();
+    method public int getTextSelectionEnd();
+    method public int getTextSelectionStart();
+    method public CharSequence? getTooltipText();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.TouchDelegateInfoCompat? getTouchDelegateInfo();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getTraversalAfter();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getTraversalBefore();
+    method public String? getUniqueId();
+    method public String! getViewIdResourceName();
+    method public androidx.core.view.accessibility.AccessibilityWindowInfoCompat! getWindow();
+    method public int getWindowId();
+    method public boolean hasRequestInitialAccessibilityFocus();
+    method public boolean isAccessibilityDataSensitive();
+    method public boolean isAccessibilityFocused();
+    method public boolean isCheckable();
+    method public boolean isChecked();
+    method public boolean isClickable();
+    method public boolean isContentInvalid();
+    method public boolean isContextClickable();
+    method public boolean isDismissable();
+    method public boolean isEditable();
+    method public boolean isEnabled();
+    method public boolean isFocusable();
+    method public boolean isFocused();
+    method public boolean isGranularScrollingSupported();
+    method public boolean isHeading();
+    method public boolean isImportantForAccessibility();
+    method public boolean isLongClickable();
+    method public boolean isMultiLine();
+    method public boolean isPassword();
+    method public boolean isScreenReaderFocusable();
+    method public boolean isScrollable();
+    method public boolean isSelected();
+    method public boolean isShowingHintText();
+    method public boolean isTextEntryKey();
+    method public boolean isTextSelectable();
+    method public boolean isVisibleToUser();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! obtain();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! obtain(android.view.View!);
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! obtain(android.view.View!, int);
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! obtain(androidx.core.view.accessibility.AccessibilityNodeInfoCompat!);
+    method public boolean performAction(int);
+    method public boolean performAction(int, android.os.Bundle!);
+    method @Deprecated public void recycle();
+    method public boolean refresh();
+    method public boolean removeAction(androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat!);
+    method public boolean removeChild(android.view.View!);
+    method public boolean removeChild(android.view.View!, int);
+    method public void setAccessibilityDataSensitive(boolean);
+    method public void setAccessibilityFocused(boolean);
+    method public void setAvailableExtraData(java.util.List<java.lang.String!>);
+    method @Deprecated public void setBoundsInParent(android.graphics.Rect!);
+    method public void setBoundsInScreen(android.graphics.Rect!);
+    method public void setBoundsInWindow(android.graphics.Rect);
+    method public void setCanOpenPopup(boolean);
+    method public void setCheckable(boolean);
+    method public void setChecked(boolean);
+    method public void setClassName(CharSequence!);
+    method public void setClickable(boolean);
+    method public void setCollectionInfo(Object!);
+    method public void setCollectionItemInfo(Object!);
+    method public void setContainerTitle(CharSequence?);
+    method public void setContentDescription(CharSequence!);
+    method public void setContentInvalid(boolean);
+    method public void setContextClickable(boolean);
+    method public void setDismissable(boolean);
+    method public void setDrawingOrder(int);
+    method public void setEditable(boolean);
+    method public void setEnabled(boolean);
+    method public void setError(CharSequence!);
+    method public void setFocusable(boolean);
+    method public void setFocused(boolean);
+    method public void setGranularScrollingSupported(boolean);
+    method public void setHeading(boolean);
+    method public void setHintText(CharSequence?);
+    method public void setImportantForAccessibility(boolean);
+    method public void setInputType(int);
+    method public void setLabelFor(android.view.View!);
+    method public void setLabelFor(android.view.View!, int);
+    method public void setLabeledBy(android.view.View!);
+    method public void setLabeledBy(android.view.View!, int);
+    method public void setLiveRegion(int);
+    method public void setLongClickable(boolean);
+    method public void setMaxTextLength(int);
+    method public void setMinDurationBetweenContentChangesMillis(long);
+    method public void setMovementGranularities(int);
+    method public void setMultiLine(boolean);
+    method public void setPackageName(CharSequence!);
+    method public void setPaneTitle(CharSequence?);
+    method public void setParent(android.view.View!);
+    method public void setParent(android.view.View!, int);
+    method public void setPassword(boolean);
+    method public void setQueryFromAppProcessEnabled(android.view.View, boolean);
+    method public void setRangeInfo(androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat!);
+    method public void setRequestInitialAccessibilityFocus(boolean);
+    method public void setRoleDescription(CharSequence?);
+    method public void setScreenReaderFocusable(boolean);
+    method public void setScrollable(boolean);
+    method public void setSelected(boolean);
+    method public void setShowingHintText(boolean);
+    method public void setSource(android.view.View!);
+    method public void setSource(android.view.View!, int);
+    method public void setStateDescription(CharSequence?);
+    method public void setText(CharSequence!);
+    method public void setTextEntryKey(boolean);
+    method public void setTextSelectable(boolean);
+    method public void setTextSelection(int, int);
+    method public void setTooltipText(CharSequence?);
+    method public void setTouchDelegateInfo(androidx.core.view.accessibility.AccessibilityNodeInfoCompat.TouchDelegateInfoCompat);
+    method public void setTraversalAfter(android.view.View!);
+    method public void setTraversalAfter(android.view.View!, int);
+    method public void setTraversalBefore(android.view.View!);
+    method public void setTraversalBefore(android.view.View!, int);
+    method public void setUniqueId(String?);
+    method public void setViewIdResourceName(String!);
+    method public void setVisibleToUser(boolean);
+    method public android.view.accessibility.AccessibilityNodeInfo! unwrap();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! wrap(android.view.accessibility.AccessibilityNodeInfo);
+    field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
+    field public static final String ACTION_ARGUMENT_COLUMN_INT = "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
+    field public static final String ACTION_ARGUMENT_DIRECTION_INT = "androidx.core.view.accessibility.action.ARGUMENT_DIRECTION_INT";
+    field public static final String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
+    field public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
+    field public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
+    field public static final String ACTION_ARGUMENT_MOVE_WINDOW_X = "ACTION_ARGUMENT_MOVE_WINDOW_X";
+    field public static final String ACTION_ARGUMENT_MOVE_WINDOW_Y = "ACTION_ARGUMENT_MOVE_WINDOW_Y";
+    field public static final String ACTION_ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT = "android.view.accessibility.action.ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT";
+    field public static final String ACTION_ARGUMENT_PROGRESS_VALUE = "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";
+    field public static final String ACTION_ARGUMENT_ROW_INT = "android.view.accessibility.action.ARGUMENT_ROW_INT";
+    field public static final String ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT = "androidx.core.view.accessibility.action.ARGUMENT_SCROLL_AMOUNT_FLOAT";
+    field public static final String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT";
+    field public static final String ACTION_ARGUMENT_SELECTION_START_INT = "ACTION_ARGUMENT_SELECTION_START_INT";
+    field public static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
+    field public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 128; // 0x80
+    field public static final int ACTION_CLEAR_FOCUS = 2; // 0x2
+    field public static final int ACTION_CLEAR_SELECTION = 8; // 0x8
+    field public static final int ACTION_CLICK = 16; // 0x10
+    field public static final int ACTION_COLLAPSE = 524288; // 0x80000
+    field public static final int ACTION_COPY = 16384; // 0x4000
+    field public static final int ACTION_CUT = 65536; // 0x10000
+    field public static final int ACTION_DISMISS = 1048576; // 0x100000
+    field public static final int ACTION_EXPAND = 262144; // 0x40000
+    field public static final int ACTION_FOCUS = 1; // 0x1
+    field public static final int ACTION_LONG_CLICK = 32; // 0x20
+    field public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 256; // 0x100
+    field public static final int ACTION_NEXT_HTML_ELEMENT = 1024; // 0x400
+    field public static final int ACTION_PASTE = 32768; // 0x8000
+    field public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 512; // 0x200
+    field public static final int ACTION_PREVIOUS_HTML_ELEMENT = 2048; // 0x800
+    field public static final int ACTION_SCROLL_BACKWARD = 8192; // 0x2000
+    field public static final int ACTION_SCROLL_FORWARD = 4096; // 0x1000
+    field public static final int ACTION_SELECT = 4; // 0x4
+    field public static final int ACTION_SET_SELECTION = 131072; // 0x20000
+    field public static final int ACTION_SET_TEXT = 2097152; // 0x200000
+    field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH = "android.core.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
+    field public static final int EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH = 20000; // 0x4e20
+    field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX = "android.core.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX";
+    field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY = "android.core.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";
+    field public static final int FLAG_PREFETCH_ANCESTORS = 1; // 0x1
+    field public static final int FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST = 16; // 0x10
+    field public static final int FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST = 8; // 0x8
+    field public static final int FLAG_PREFETCH_DESCENDANTS_HYBRID = 4; // 0x4
+    field public static final int FLAG_PREFETCH_SIBLINGS = 2; // 0x2
+    field public static final int FLAG_PREFETCH_UNINTERRUPTIBLE = 32; // 0x20
+    field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2
+    field public static final int FOCUS_INPUT = 1; // 0x1
+    field public static final int MAX_NUMBER_OF_PREFETCHED_NODES = 50; // 0x32
+    field public static final int MOVEMENT_GRANULARITY_CHARACTER = 1; // 0x1
+    field public static final int MOVEMENT_GRANULARITY_LINE = 4; // 0x4
+    field public static final int MOVEMENT_GRANULARITY_PAGE = 16; // 0x10
+    field public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 8; // 0x8
+    field public static final int MOVEMENT_GRANULARITY_WORD = 2; // 0x2
+  }
+
+  public static class AccessibilityNodeInfoCompat.AccessibilityActionCompat {
+    ctor public AccessibilityNodeInfoCompat.AccessibilityActionCompat(int, CharSequence!);
+    method public int getId();
+    method public CharSequence! getLabel();
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_ACCESSIBILITY_FOCUS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CLEAR_ACCESSIBILITY_FOCUS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CLEAR_FOCUS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CLEAR_SELECTION;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CLICK;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_COLLAPSE;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CONTEXT_CLICK;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_COPY;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CUT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_DISMISS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_DRAG_CANCEL;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_DRAG_DROP;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_DRAG_START;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_EXPAND;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_FOCUS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_HIDE_TOOLTIP;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_IME_ENTER;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_LONG_CLICK;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_MOVE_WINDOW;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_NEXT_AT_MOVEMENT_GRANULARITY;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_NEXT_HTML_ELEMENT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PAGE_DOWN;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PAGE_LEFT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PAGE_RIGHT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PAGE_UP;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_PASTE;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PRESS_AND_HOLD;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_PREVIOUS_HTML_ELEMENT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_BACKWARD;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_DOWN;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_FORWARD;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_IN_DIRECTION;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_LEFT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_RIGHT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_TO_POSITION;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_UP;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SELECT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SET_PROGRESS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SET_SELECTION;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SET_TEXT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SHOW_ON_SCREEN;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SHOW_TEXT_SUGGESTIONS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SHOW_TOOLTIP;
+  }
+
+  public static class AccessibilityNodeInfoCompat.CollectionInfoCompat {
+    method public int getColumnCount();
+    method public int getRowCount();
+    method public int getSelectionMode();
+    method public boolean isHierarchical();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat! obtain(int, int, boolean);
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat! obtain(int, int, boolean, int);
+    field public static final int SELECTION_MODE_MULTIPLE = 2; // 0x2
+    field public static final int SELECTION_MODE_NONE = 0; // 0x0
+    field public static final int SELECTION_MODE_SINGLE = 1; // 0x1
+  }
+
+  public static class AccessibilityNodeInfoCompat.CollectionItemInfoCompat {
+    method public int getColumnIndex();
+    method public int getColumnSpan();
+    method public String? getColumnTitle();
+    method public int getRowIndex();
+    method public int getRowSpan();
+    method public String? getRowTitle();
+    method @Deprecated public boolean isHeading();
+    method public boolean isSelected();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat! obtain(int, int, int, int, boolean);
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat! obtain(int, int, int, int, boolean, boolean);
+  }
+
+  public static final class AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder {
+    ctor public AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat build();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setColumnIndex(int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setColumnSpan(int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setColumnTitle(String?);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setHeading(boolean);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setRowIndex(int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setRowSpan(int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setRowTitle(String?);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setSelected(boolean);
+  }
+
+  public static class AccessibilityNodeInfoCompat.RangeInfoCompat {
+    ctor public AccessibilityNodeInfoCompat.RangeInfoCompat(int, float, float, float);
+    method public float getCurrent();
+    method public float getMax();
+    method public float getMin();
+    method public int getType();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat! obtain(int, float, float, float);
+    field public static final int RANGE_TYPE_FLOAT = 1; // 0x1
+    field public static final int RANGE_TYPE_INT = 0; // 0x0
+    field public static final int RANGE_TYPE_PERCENT = 2; // 0x2
+  }
+
+  public static final class AccessibilityNodeInfoCompat.TouchDelegateInfoCompat {
+    ctor public AccessibilityNodeInfoCompat.TouchDelegateInfoCompat(java.util.Map<android.graphics.Region!,android.view.View!>);
+    method public android.graphics.Region? getRegionAt(@IntRange(from=0) int);
+    method @IntRange(from=0) public int getRegionCount();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? getTargetForRegion(android.graphics.Region);
+  }
+
+  public class AccessibilityNodeProviderCompat {
+    ctor public AccessibilityNodeProviderCompat();
+    ctor public AccessibilityNodeProviderCompat(Object?);
+    method public void addExtraDataToAccessibilityNodeInfo(int, androidx.core.view.accessibility.AccessibilityNodeInfoCompat, String, android.os.Bundle?);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? createAccessibilityNodeInfo(int);
+    method public java.util.List<androidx.core.view.accessibility.AccessibilityNodeInfoCompat!>? findAccessibilityNodeInfosByText(String, int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? findFocus(int);
+    method public Object? getProvider();
+    method public boolean performAction(int, int, android.os.Bundle?);
+    field public static final int HOST_VIEW_ID = -1; // 0xffffffff
+  }
+
+  public class AccessibilityRecordCompat {
+    ctor @Deprecated public AccessibilityRecordCompat(Object!);
+    method @Deprecated public boolean equals(Object?);
+    method @Deprecated public int getAddedCount();
+    method @Deprecated public CharSequence! getBeforeText();
+    method @Deprecated public CharSequence! getClassName();
+    method @Deprecated public CharSequence! getContentDescription();
+    method @Deprecated public int getCurrentItemIndex();
+    method @Deprecated public int getFromIndex();
+    method @Deprecated public Object! getImpl();
+    method @Deprecated public int getItemCount();
+    method @Deprecated public int getMaxScrollX();
+    method public static int getMaxScrollX(android.view.accessibility.AccessibilityRecord);
+    method @Deprecated public int getMaxScrollY();
+    method public static int getMaxScrollY(android.view.accessibility.AccessibilityRecord);
+    method @Deprecated public android.os.Parcelable! getParcelableData();
+    method @Deprecated public int getRemovedCount();
+    method @Deprecated public int getScrollX();
+    method @Deprecated public int getScrollY();
+    method @Deprecated public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getSource();
+    method @Deprecated public java.util.List<java.lang.CharSequence!>! getText();
+    method @Deprecated public int getToIndex();
+    method @Deprecated public int getWindowId();
+    method @Deprecated public int hashCode();
+    method @Deprecated public boolean isChecked();
+    method @Deprecated public boolean isEnabled();
+    method @Deprecated public boolean isFullScreen();
+    method @Deprecated public boolean isPassword();
+    method @Deprecated public boolean isScrollable();
+    method @Deprecated public static androidx.core.view.accessibility.AccessibilityRecordCompat! obtain();
+    method @Deprecated public static androidx.core.view.accessibility.AccessibilityRecordCompat! obtain(androidx.core.view.accessibility.AccessibilityRecordCompat!);
+    method @Deprecated public void recycle();
+    method @Deprecated public void setAddedCount(int);
+    method @Deprecated public void setBeforeText(CharSequence!);
+    method @Deprecated public void setChecked(boolean);
+    method @Deprecated public void setClassName(CharSequence!);
+    method @Deprecated public void setContentDescription(CharSequence!);
+    method @Deprecated public void setCurrentItemIndex(int);
+    method @Deprecated public void setEnabled(boolean);
+    method @Deprecated public void setFromIndex(int);
+    method @Deprecated public void setFullScreen(boolean);
+    method @Deprecated public void setItemCount(int);
+    method public static void setMaxScrollX(android.view.accessibility.AccessibilityRecord, int);
+    method @Deprecated public void setMaxScrollX(int);
+    method public static void setMaxScrollY(android.view.accessibility.AccessibilityRecord, int);
+    method @Deprecated public void setMaxScrollY(int);
+    method @Deprecated public void setParcelableData(android.os.Parcelable!);
+    method @Deprecated public void setPassword(boolean);
+    method @Deprecated public void setRemovedCount(int);
+    method @Deprecated public void setScrollX(int);
+    method @Deprecated public void setScrollY(int);
+    method @Deprecated public void setScrollable(boolean);
+    method public static void setSource(android.view.accessibility.AccessibilityRecord, android.view.View?, int);
+    method @Deprecated public void setSource(android.view.View!);
+    method @Deprecated public void setSource(android.view.View!, int);
+    method @Deprecated public void setToIndex(int);
+  }
+
+  public interface AccessibilityViewCommand {
+    method public boolean perform(android.view.View, androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments?);
+  }
+
+  public abstract static class AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.CommandArguments();
+  }
+
+  public static final class AccessibilityViewCommand.MoveAtGranularityArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.MoveAtGranularityArguments();
+    method public boolean getExtendSelection();
+    method public int getGranularity();
+  }
+
+  public static final class AccessibilityViewCommand.MoveHtmlArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.MoveHtmlArguments();
+    method public String? getHTMLElement();
+  }
+
+  public static final class AccessibilityViewCommand.MoveWindowArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.MoveWindowArguments();
+    method public int getX();
+    method public int getY();
+  }
+
+  public static final class AccessibilityViewCommand.ScrollToPositionArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.ScrollToPositionArguments();
+    method public int getColumn();
+    method public int getRow();
+  }
+
+  public static final class AccessibilityViewCommand.SetProgressArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.SetProgressArguments();
+    method public float getProgress();
+  }
+
+  public static final class AccessibilityViewCommand.SetSelectionArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.SetSelectionArguments();
+    method public int getEnd();
+    method public int getStart();
+  }
+
+  public static final class AccessibilityViewCommand.SetTextArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.SetTextArguments();
+    method public CharSequence? getText();
+  }
+
+  public class AccessibilityWindowInfoCompat {
+    ctor public AccessibilityWindowInfoCompat();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? getAnchor();
+    method public void getBoundsInScreen(android.graphics.Rect);
+    method public androidx.core.view.accessibility.AccessibilityWindowInfoCompat? getChild(int);
+    method public int getChildCount();
+    method public int getDisplayId();
+    method public int getId();
+    method public int getLayer();
+    method public androidx.core.os.LocaleListCompat getLocales();
+    method public androidx.core.view.accessibility.AccessibilityWindowInfoCompat? getParent();
+    method public void getRegionInScreen(android.graphics.Region);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? getRoot();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? getRoot(int);
+    method public CharSequence? getTitle();
+    method public long getTransitionTimeMillis();
+    method public int getType();
+    method public boolean isAccessibilityFocused();
+    method public boolean isActive();
+    method public boolean isFocused();
+    method public boolean isInPictureInPictureMode();
+    method public static androidx.core.view.accessibility.AccessibilityWindowInfoCompat? obtain();
+    method public static androidx.core.view.accessibility.AccessibilityWindowInfoCompat? obtain(androidx.core.view.accessibility.AccessibilityWindowInfoCompat?);
+    method @Deprecated public void recycle();
+    method public android.view.accessibility.AccessibilityWindowInfo? unwrap();
+    field public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; // 0x4
+    field public static final int TYPE_APPLICATION = 1; // 0x1
+    field public static final int TYPE_INPUT_METHOD = 2; // 0x2
+    field public static final int TYPE_MAGNIFICATION_OVERLAY = 6; // 0x6
+    field public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5; // 0x5
+    field public static final int TYPE_SYSTEM = 3; // 0x3
+  }
+
+}
+
+package androidx.core.view.animation {
+
+  public final class PathInterpolatorCompat {
+    method public static android.view.animation.Interpolator create(android.graphics.Path);
+    method public static android.view.animation.Interpolator create(float, float);
+    method public static android.view.animation.Interpolator create(float, float, float, float);
+  }
+
+}
+
+package androidx.core.view.autofill {
+
+  public class AutofillIdCompat {
+    method @RequiresApi(26) public android.view.autofill.AutofillId toAutofillId();
+    method @RequiresApi(26) public static androidx.core.view.autofill.AutofillIdCompat toAutofillIdCompat(android.view.autofill.AutofillId);
+  }
+
+}
+
+package androidx.core.view.contentcapture {
+
+  public class ContentCaptureSessionCompat {
+    method public android.view.autofill.AutofillId? newAutofillId(long);
+    method public androidx.core.view.ViewStructureCompat? newVirtualViewStructure(android.view.autofill.AutofillId, long);
+    method public void notifyViewTextChanged(android.view.autofill.AutofillId, CharSequence?);
+    method public void notifyViewsAppeared(java.util.List<android.view.ViewStructure!>);
+    method public void notifyViewsDisappeared(long[]);
+    method @RequiresApi(29) public android.view.contentcapture.ContentCaptureSession toContentCaptureSession();
+    method @RequiresApi(29) public static androidx.core.view.contentcapture.ContentCaptureSessionCompat toContentCaptureSessionCompat(android.view.contentcapture.ContentCaptureSession, android.view.View);
+  }
+
+}
+
+package androidx.core.view.inputmethod {
+
+  public final class EditorInfoCompat {
+    ctor @Deprecated public EditorInfoCompat();
+    method public static String![] getContentMimeTypes(android.view.inputmethod.EditorInfo);
+    method public static CharSequence? getInitialSelectedText(android.view.inputmethod.EditorInfo, int);
+    method public static CharSequence? getInitialTextAfterCursor(android.view.inputmethod.EditorInfo, int, int);
+    method public static CharSequence? getInitialTextBeforeCursor(android.view.inputmethod.EditorInfo, int, int);
+    method public static void setContentMimeTypes(android.view.inputmethod.EditorInfo, String![]?);
+    method public static void setInitialSurroundingSubText(android.view.inputmethod.EditorInfo, CharSequence, int);
+    method public static void setInitialSurroundingText(android.view.inputmethod.EditorInfo, CharSequence);
+    field public static final int IME_FLAG_FORCE_ASCII = -2147483648; // 0x80000000
+    field public static final int IME_FLAG_NO_PERSONALIZED_LEARNING = 16777216; // 0x1000000
+  }
+
+  public final class InputConnectionCompat {
+    ctor @Deprecated public InputConnectionCompat();
+    method public static boolean commitContent(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo, androidx.core.view.inputmethod.InputContentInfoCompat, int, android.os.Bundle?);
+    method @Deprecated public static android.view.inputmethod.InputConnection createWrapper(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo, androidx.core.view.inputmethod.InputConnectionCompat.OnCommitContentListener);
+    method public static android.view.inputmethod.InputConnection createWrapper(android.view.View, android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo);
+    field public static final int INPUT_CONTENT_GRANT_READ_URI_PERMISSION = 1; // 0x1
+  }
+
+  public static interface InputConnectionCompat.OnCommitContentListener {
+    method public boolean onCommitContent(androidx.core.view.inputmethod.InputContentInfoCompat, int, android.os.Bundle?);
+  }
+
+  public final class InputContentInfoCompat {
+    ctor public InputContentInfoCompat(android.net.Uri, android.content.ClipDescription, android.net.Uri?);
+    method public android.net.Uri getContentUri();
+    method public android.content.ClipDescription getDescription();
+    method public android.net.Uri? getLinkUri();
+    method public void releasePermission();
+    method public void requestPermission();
+    method public Object? unwrap();
+    method public static androidx.core.view.inputmethod.InputContentInfoCompat? wrap(Object?);
+  }
+
+}
+
+package androidx.core.widget {
+
+  public abstract class AutoScrollHelper implements android.view.View.OnTouchListener {
+    ctor public AutoScrollHelper(android.view.View);
+    method public abstract boolean canTargetScrollHorizontally(int);
+    method public abstract boolean canTargetScrollVertically(int);
+    method public boolean isEnabled();
+    method public boolean isExclusive();
+    method public boolean onTouch(android.view.View!, android.view.MotionEvent!);
+    method public abstract void scrollTargetBy(int, int);
+    method public androidx.core.widget.AutoScrollHelper setActivationDelay(int);
+    method public androidx.core.widget.AutoScrollHelper setEdgeType(int);
+    method public androidx.core.widget.AutoScrollHelper! setEnabled(boolean);
+    method public androidx.core.widget.AutoScrollHelper! setExclusive(boolean);
+    method public androidx.core.widget.AutoScrollHelper setMaximumEdges(float, float);
+    method public androidx.core.widget.AutoScrollHelper setMaximumVelocity(float, float);
+    method public androidx.core.widget.AutoScrollHelper setMinimumVelocity(float, float);
+    method public androidx.core.widget.AutoScrollHelper setRampDownDuration(int);
+    method public androidx.core.widget.AutoScrollHelper setRampUpDuration(int);
+    method public androidx.core.widget.AutoScrollHelper setRelativeEdges(float, float);
+    method public androidx.core.widget.AutoScrollHelper setRelativeVelocity(float, float);
+    field public static final int EDGE_TYPE_INSIDE = 0; // 0x0
+    field public static final int EDGE_TYPE_INSIDE_EXTEND = 1; // 0x1
+    field public static final int EDGE_TYPE_OUTSIDE = 2; // 0x2
+    field public static final float NO_MAX = 3.4028235E38f;
+    field public static final float NO_MIN = 0.0f;
+    field public static final float RELATIVE_UNSPECIFIED = 0.0f;
+  }
+
+  public final class CheckedTextViewCompat {
+    method public static android.graphics.drawable.Drawable? getCheckMarkDrawable(android.widget.CheckedTextView);
+    method public static android.content.res.ColorStateList? getCheckMarkTintList(android.widget.CheckedTextView);
+    method public static android.graphics.PorterDuff.Mode? getCheckMarkTintMode(android.widget.CheckedTextView);
+    method public static void setCheckMarkTintList(android.widget.CheckedTextView, android.content.res.ColorStateList?);
+    method public static void setCheckMarkTintMode(android.widget.CheckedTextView, android.graphics.PorterDuff.Mode?);
+  }
+
+  public final class CompoundButtonCompat {
+    method public static android.graphics.drawable.Drawable? getButtonDrawable(android.widget.CompoundButton);
+    method public static android.content.res.ColorStateList? getButtonTintList(android.widget.CompoundButton);
+    method public static android.graphics.PorterDuff.Mode? getButtonTintMode(android.widget.CompoundButton);
+    method public static void setButtonTintList(android.widget.CompoundButton, android.content.res.ColorStateList?);
+    method public static void setButtonTintMode(android.widget.CompoundButton, android.graphics.PorterDuff.Mode?);
+  }
+
+  public class ContentLoadingProgressBar extends android.widget.ProgressBar {
+    ctor public ContentLoadingProgressBar(android.content.Context);
+    ctor public ContentLoadingProgressBar(android.content.Context, android.util.AttributeSet?);
+    method public void hide();
+    method public void onAttachedToWindow();
+    method public void onDetachedFromWindow();
+    method public void show();
+  }
+
+  public final class EdgeEffectCompat {
+    ctor @Deprecated public EdgeEffectCompat(android.content.Context!);
+    method public static android.widget.EdgeEffect create(android.content.Context, android.util.AttributeSet?);
+    method @Deprecated public boolean draw(android.graphics.Canvas!);
+    method @Deprecated public void finish();
+    method public static float getDistance(android.widget.EdgeEffect);
+    method @Deprecated public boolean isFinished();
+    method @Deprecated public boolean onAbsorb(int);
+    method public static void onPull(android.widget.EdgeEffect, float, float);
+    method @Deprecated public boolean onPull(float);
+    method @Deprecated public boolean onPull(float, float);
+    method public static float onPullDistance(android.widget.EdgeEffect, float, float);
+    method @Deprecated public boolean onRelease();
+    method @Deprecated public void setSize(int, int);
+  }
+
+  public class ImageViewCompat {
+    method public static android.content.res.ColorStateList? getImageTintList(android.widget.ImageView);
+    method public static android.graphics.PorterDuff.Mode? getImageTintMode(android.widget.ImageView);
+    method public static void setImageTintList(android.widget.ImageView, android.content.res.ColorStateList?);
+    method public static void setImageTintMode(android.widget.ImageView, android.graphics.PorterDuff.Mode?);
+  }
+
+  public final class ListPopupWindowCompat {
+    method public static android.view.View.OnTouchListener? createDragToOpenListener(android.widget.ListPopupWindow, android.view.View);
+    method @Deprecated public static android.view.View.OnTouchListener! createDragToOpenListener(Object!, android.view.View!);
+  }
+
+  public class ListViewAutoScrollHelper extends androidx.core.widget.AutoScrollHelper {
+    ctor public ListViewAutoScrollHelper(android.widget.ListView);
+    method public boolean canTargetScrollHorizontally(int);
+    method public boolean canTargetScrollVertically(int);
+    method public void scrollTargetBy(int, int);
+  }
+
+  public final class ListViewCompat {
+    method public static boolean canScrollList(android.widget.ListView, int);
+    method public static void scrollListBy(android.widget.ListView, int);
+  }
+
+  public class NestedScrollView extends android.widget.FrameLayout implements androidx.core.view.NestedScrollingChild3 androidx.core.view.NestedScrollingParent3 androidx.core.view.ScrollingView {
+    ctor public NestedScrollView(android.content.Context);
+    ctor public NestedScrollView(android.content.Context, android.util.AttributeSet?);
+    ctor public NestedScrollView(android.content.Context, android.util.AttributeSet?, int);
+    method public boolean arrowScroll(int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeHorizontalScrollExtent();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeHorizontalScrollOffset();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeHorizontalScrollRange();
+    method protected int computeScrollDeltaToGetChildRectOnScreen(android.graphics.Rect!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeVerticalScrollExtent();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeVerticalScrollOffset();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeVerticalScrollRange();
+    method public boolean dispatchNestedPreScroll(int, int, int[]?, int[]?, int);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]?, int);
+    method public void dispatchNestedScroll(int, int, int, int, int[]?, int, int[]);
+    method public boolean executeKeyEvent(android.view.KeyEvent);
+    method public void fling(int);
+    method public boolean fullScroll(int);
+    method public int getMaxScrollAmount();
+    method public boolean hasNestedScrollingParent(int);
+    method public boolean isFillViewport();
+    method public boolean isSmoothScrollingEnabled();
+    method public void onAttachedToWindow();
+    method public void onNestedPreScroll(android.view.View, int, int, int[], int);
+    method public void onNestedScroll(android.view.View, int, int, int, int, int);
+    method public void onNestedScroll(android.view.View, int, int, int, int, int, int[]);
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, int, int);
+    method public boolean onStartNestedScroll(android.view.View, android.view.View, int, int);
+    method public void onStopNestedScroll(android.view.View, int);
+    method public boolean pageScroll(int);
+    method public void setFillViewport(boolean);
+    method public void setOnScrollChangeListener(androidx.core.widget.NestedScrollView.OnScrollChangeListener?);
+    method public void setSmoothScrollingEnabled(boolean);
+    method public final void smoothScrollBy(int, int);
+    method public final void smoothScrollBy(int, int, int);
+    method public final void smoothScrollTo(int, int);
+    method public final void smoothScrollTo(int, int, int);
+    method public boolean startNestedScroll(int, int);
+    method public void stopNestedScroll(int);
+  }
+
+  public static interface NestedScrollView.OnScrollChangeListener {
+    method public void onScrollChange(androidx.core.widget.NestedScrollView, int, int, int, int);
+  }
+
+  public final class PopupMenuCompat {
+    method public static android.view.View.OnTouchListener? getDragToOpenListener(Object);
+  }
+
+  public final class PopupWindowCompat {
+    method public static boolean getOverlapAnchor(android.widget.PopupWindow);
+    method public static int getWindowLayoutType(android.widget.PopupWindow);
+    method public static void setOverlapAnchor(android.widget.PopupWindow, boolean);
+    method public static void setWindowLayoutType(android.widget.PopupWindow, int);
+    method public static void showAsDropDown(android.widget.PopupWindow, android.view.View, int, int, int);
+  }
+
+  @Deprecated public final class ScrollerCompat {
+    method @Deprecated public void abortAnimation();
+    method @Deprecated public boolean computeScrollOffset();
+    method @Deprecated public static androidx.core.widget.ScrollerCompat! create(android.content.Context!);
+    method @Deprecated public static androidx.core.widget.ScrollerCompat! create(android.content.Context!, android.view.animation.Interpolator!);
+    method @Deprecated public void fling(int, int, int, int, int, int, int, int);
+    method @Deprecated public void fling(int, int, int, int, int, int, int, int, int, int);
+    method @Deprecated public float getCurrVelocity();
+    method @Deprecated public int getCurrX();
+    method @Deprecated public int getCurrY();
+    method @Deprecated public int getFinalX();
+    method @Deprecated public int getFinalY();
+    method @Deprecated public boolean isFinished();
+    method @Deprecated public boolean isOverScrolled();
+    method @Deprecated public void notifyHorizontalEdgeReached(int, int, int);
+    method @Deprecated public void notifyVerticalEdgeReached(int, int, int);
+    method @Deprecated public boolean springBack(int, int, int, int, int, int);
+    method @Deprecated public void startScroll(int, int, int, int);
+    method @Deprecated public void startScroll(int, int, int, int, int);
+  }
+
+  public final class TextViewCompat {
+    method public static int getAutoSizeMaxTextSize(android.widget.TextView);
+    method public static int getAutoSizeMinTextSize(android.widget.TextView);
+    method public static int getAutoSizeStepGranularity(android.widget.TextView);
+    method public static int[] getAutoSizeTextAvailableSizes(android.widget.TextView);
+    method public static int getAutoSizeTextType(android.widget.TextView);
+    method public static android.content.res.ColorStateList? getCompoundDrawableTintList(android.widget.TextView);
+    method public static android.graphics.PorterDuff.Mode? getCompoundDrawableTintMode(android.widget.TextView);
+    method public static android.graphics.drawable.Drawable![] getCompoundDrawablesRelative(android.widget.TextView);
+    method public static int getFirstBaselineToTopHeight(android.widget.TextView);
+    method public static int getLastBaselineToBottomHeight(android.widget.TextView);
+    method public static int getMaxLines(android.widget.TextView);
+    method public static int getMinLines(android.widget.TextView);
+    method public static androidx.core.text.PrecomputedTextCompat.Params getTextMetricsParams(android.widget.TextView);
+    method public static void setAutoSizeTextTypeUniformWithConfiguration(android.widget.TextView, int, int, int, int) throws java.lang.IllegalArgumentException;
+    method public static void setAutoSizeTextTypeUniformWithPresetSizes(android.widget.TextView, int[], int) throws java.lang.IllegalArgumentException;
+    method public static void setAutoSizeTextTypeWithDefaults(android.widget.TextView, int);
+    method public static void setCompoundDrawableTintList(android.widget.TextView, android.content.res.ColorStateList?);
+    method public static void setCompoundDrawableTintMode(android.widget.TextView, android.graphics.PorterDuff.Mode?);
+    method public static void setCompoundDrawablesRelative(android.widget.TextView, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?);
+    method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?);
+    method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, @DrawableRes int, @DrawableRes int, @DrawableRes int, @DrawableRes int);
+    method public static void setCustomSelectionActionModeCallback(android.widget.TextView, android.view.ActionMode.Callback);
+    method public static void setFirstBaselineToTopHeight(android.widget.TextView, @IntRange(from=0) @Px int);
+    method public static void setLastBaselineToBottomHeight(android.widget.TextView, @IntRange(from=0) @Px int);
+    method public static void setLineHeight(android.widget.TextView, @IntRange(from=0) @Px int);
+    method public static void setLineHeight(android.widget.TextView, int, @FloatRange(from=0) float);
+    method public static void setPrecomputedText(android.widget.TextView, androidx.core.text.PrecomputedTextCompat);
+    method public static void setTextAppearance(android.widget.TextView, @StyleRes int);
+    method public static void setTextMetricsParams(android.widget.TextView, androidx.core.text.PrecomputedTextCompat.Params);
+    field public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; // 0x0
+    field public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1; // 0x1
+  }
+
+  public interface TintableCompoundButton {
+    method public android.content.res.ColorStateList? getSupportButtonTintList();
+    method public android.graphics.PorterDuff.Mode? getSupportButtonTintMode();
+    method public void setSupportButtonTintList(android.content.res.ColorStateList?);
+    method public void setSupportButtonTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  public interface TintableCompoundDrawablesView {
+    method public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+    method public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+    method public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+    method public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+}
+
diff --git a/core/core/api/current.ignore b/core/core/api/current.ignore
index d11fec9..a3517f0 100644
--- a/core/core/api/current.ignore
+++ b/core/core/api/current.ignore
@@ -1,7 +1,5 @@
 // Baseline format: 1.0
-InvalidNullConversion: androidx.core.app.PendingIntentCompat#getActivity(android.content.Context, int, android.content.Intent, int, android.os.Bundle, boolean):
-    Attempted to change method return from @NonNull to @Nullable: incompatible change for method androidx.core.app.PendingIntentCompat.getActivity(android.content.Context,int,android.content.Intent,int,android.os.Bundle,boolean)
-InvalidNullConversion: androidx.core.app.PendingIntentCompat#getActivity(android.content.Context, int, android.content.Intent, int, boolean):
-    Attempted to change method return from @NonNull to @Nullable: incompatible change for method androidx.core.app.PendingIntentCompat.getActivity(android.content.Context,int,android.content.Intent,int,boolean)
-InvalidNullConversion: androidx.core.app.PendingIntentCompat#getService(android.content.Context, int, android.content.Intent, int, boolean):
-    Attempted to change method return from @NonNull to @Nullable: incompatible change for method androidx.core.app.PendingIntentCompat.getService(android.content.Context,int,android.content.Intent,int,boolean)
+AddedMethod: androidx.core.util.TypedValueCompat#getUnitFromComplexDimension(int):
+    Added method androidx.core.util.TypedValueCompat.getUnitFromComplexDimension(int)
+AddedMethod: androidx.core.widget.TextViewCompat#setLineHeight(android.widget.TextView, int, float):
+    Added method androidx.core.widget.TextViewCompat.setLineHeight(android.widget.TextView,int,float)
diff --git a/core/core/api/current.txt b/core/core/api/current.txt
index d2d9ad3..7ff5c2a 100644
--- a/core/core/api/current.txt
+++ b/core/core/api/current.txt
@@ -1858,9 +1858,9 @@
     method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.Q) public static boolean isAtLeastQ();
     method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.R) public static boolean isAtLeastR();
     method @Deprecated @ChecksSdkIntAtLeast(api=31, codename="S") public static boolean isAtLeastS();
-    method @Deprecated @SuppressCompatibility @ChecksSdkIntAtLeast(api=32, codename="Sv2") @androidx.core.os.BuildCompat.PrereleaseSdkCheck public static boolean isAtLeastSv2();
-    method @Deprecated @SuppressCompatibility @ChecksSdkIntAtLeast(api=33, codename="Tiramisu") @androidx.core.os.BuildCompat.PrereleaseSdkCheck public static boolean isAtLeastT();
-    method @Deprecated @SuppressCompatibility @ChecksSdkIntAtLeast(api=34, codename="UpsideDownCake") @androidx.core.os.BuildCompat.PrereleaseSdkCheck public static boolean isAtLeastU();
+    method @Deprecated @ChecksSdkIntAtLeast(api=32, codename="Sv2") public static boolean isAtLeastSv2();
+    method @Deprecated @ChecksSdkIntAtLeast(api=33, codename="Tiramisu") public static boolean isAtLeastT();
+    method @Deprecated @ChecksSdkIntAtLeast(api=34, codename="UpsideDownCake") public static boolean isAtLeastU();
     method @SuppressCompatibility @ChecksSdkIntAtLeast(codename="VanillaIceCream") @androidx.core.os.BuildCompat.PrereleaseSdkCheck public static boolean isAtLeastV();
     field @ChecksSdkIntAtLeast(extension=android.os.ext.SdkExtensions.AD_SERVICES) public static final int AD_SERVICES_EXTENSION_INT;
     field public static final androidx.core.os.BuildCompat INSTANCE;
@@ -3387,13 +3387,18 @@
     method public static int getMovementGranularity(android.view.accessibility.AccessibilityEvent);
     method @Deprecated public static androidx.core.view.accessibility.AccessibilityRecordCompat! getRecord(android.view.accessibility.AccessibilityEvent!, int);
     method @Deprecated public static int getRecordCount(android.view.accessibility.AccessibilityEvent!);
+    method public static boolean isAccessibilityDataSensitive(android.view.accessibility.AccessibilityEvent);
+    method public static void setAccessibilityDataSensitive(android.view.accessibility.AccessibilityEvent, boolean);
     method public static void setAction(android.view.accessibility.AccessibilityEvent, int);
     method public static void setContentChangeTypes(android.view.accessibility.AccessibilityEvent, int);
     method public static void setMovementGranularity(android.view.accessibility.AccessibilityEvent, int);
     field public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 4; // 0x4
+    field public static final int CONTENT_CHANGE_TYPE_CONTENT_INVALID = 1024; // 0x400
     field public static final int CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 512; // 0x200
     field public static final int CONTENT_CHANGE_TYPE_DRAG_DROPPED = 256; // 0x100
     field public static final int CONTENT_CHANGE_TYPE_DRAG_STARTED = 128; // 0x80
+    field public static final int CONTENT_CHANGE_TYPE_ENABLED = 4096; // 0x1000
+    field public static final int CONTENT_CHANGE_TYPE_ERROR = 2048; // 0x800
     field public static final int CONTENT_CHANGE_TYPE_PANE_APPEARED = 16; // 0x10
     field public static final int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 32; // 0x20
     field public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 8; // 0x8
@@ -3428,6 +3433,7 @@
     method public static boolean addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager, androidx.core.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener);
     method @Deprecated public static java.util.List<android.accessibilityservice.AccessibilityServiceInfo!>! getEnabledAccessibilityServiceList(android.view.accessibility.AccessibilityManager!, int);
     method @Deprecated public static java.util.List<android.accessibilityservice.AccessibilityServiceInfo!>! getInstalledAccessibilityServiceList(android.view.accessibility.AccessibilityManager!);
+    method public static boolean isRequestFromAccessibilityTool(android.view.accessibility.AccessibilityManager);
     method @Deprecated public static boolean isTouchExplorationEnabled(android.view.accessibility.AccessibilityManager!);
     method @Deprecated public static boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager!, androidx.core.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener!);
     method public static boolean removeTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager, androidx.core.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener);
@@ -3461,11 +3467,14 @@
     method public java.util.List<java.lang.String!> getAvailableExtraData();
     method @Deprecated public void getBoundsInParent(android.graphics.Rect!);
     method public void getBoundsInScreen(android.graphics.Rect!);
+    method public void getBoundsInWindow(android.graphics.Rect);
     method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getChild(int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? getChild(int, int);
     method public int getChildCount();
     method public CharSequence! getClassName();
     method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat! getCollectionInfo();
     method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat! getCollectionItemInfo();
+    method public CharSequence? getContainerTitle();
     method public CharSequence! getContentDescription();
     method public int getDrawingOrder();
     method public CharSequence! getError();
@@ -3483,6 +3492,7 @@
     method public CharSequence! getPackageName();
     method public CharSequence? getPaneTitle();
     method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getParent();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? getParent(int);
     method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat! getRangeInfo();
     method public CharSequence? getRoleDescription();
     method public CharSequence? getStateDescription();
@@ -3498,6 +3508,7 @@
     method public androidx.core.view.accessibility.AccessibilityWindowInfoCompat! getWindow();
     method public int getWindowId();
     method public boolean hasRequestInitialAccessibilityFocus();
+    method public boolean isAccessibilityDataSensitive();
     method public boolean isAccessibilityFocused();
     method public boolean isCheckable();
     method public boolean isChecked();
@@ -3533,10 +3544,12 @@
     method public boolean removeAction(androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat!);
     method public boolean removeChild(android.view.View!);
     method public boolean removeChild(android.view.View!, int);
+    method public void setAccessibilityDataSensitive(boolean);
     method public void setAccessibilityFocused(boolean);
     method public void setAvailableExtraData(java.util.List<java.lang.String!>);
     method @Deprecated public void setBoundsInParent(android.graphics.Rect!);
     method public void setBoundsInScreen(android.graphics.Rect!);
+    method public void setBoundsInWindow(android.graphics.Rect);
     method public void setCanOpenPopup(boolean);
     method public void setCheckable(boolean);
     method public void setChecked(boolean);
@@ -3544,6 +3557,7 @@
     method public void setClickable(boolean);
     method public void setCollectionInfo(Object!);
     method public void setCollectionItemInfo(Object!);
+    method public void setContainerTitle(CharSequence?);
     method public void setContentDescription(CharSequence!);
     method public void setContentInvalid(boolean);
     method public void setContextClickable(boolean);
@@ -3574,6 +3588,7 @@
     method public void setParent(android.view.View!);
     method public void setParent(android.view.View!, int);
     method public void setPassword(boolean);
+    method public void setQueryFromAppProcessEnabled(android.view.View, boolean);
     method public void setRangeInfo(androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat!);
     method public void setRequestInitialAccessibilityFocus(boolean);
     method public void setRoleDescription(CharSequence?);
@@ -3639,8 +3654,15 @@
     field public static final int EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH = 20000; // 0x4e20
     field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX = "android.core.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX";
     field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY = "android.core.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";
+    field public static final int FLAG_PREFETCH_ANCESTORS = 1; // 0x1
+    field public static final int FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST = 16; // 0x10
+    field public static final int FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST = 8; // 0x8
+    field public static final int FLAG_PREFETCH_DESCENDANTS_HYBRID = 4; // 0x4
+    field public static final int FLAG_PREFETCH_SIBLINGS = 2; // 0x2
+    field public static final int FLAG_PREFETCH_UNINTERRUPTIBLE = 32; // 0x20
     field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2
     field public static final int FOCUS_INPUT = 1; // 0x1
+    field public static final int MAX_NUMBER_OF_PREFETCHED_NODES = 50; // 0x32
     field public static final int MOVEMENT_GRANULARITY_CHARACTER = 1; // 0x1
     field public static final int MOVEMENT_GRANULARITY_LINE = 4; // 0x4
     field public static final int MOVEMENT_GRANULARITY_PAGE = 16; // 0x10
@@ -3713,15 +3735,31 @@
   public static class AccessibilityNodeInfoCompat.CollectionItemInfoCompat {
     method public int getColumnIndex();
     method public int getColumnSpan();
+    method public String? getColumnTitle();
     method public int getRowIndex();
     method public int getRowSpan();
+    method public String? getRowTitle();
     method @Deprecated public boolean isHeading();
     method public boolean isSelected();
     method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat! obtain(int, int, int, int, boolean);
     method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat! obtain(int, int, int, int, boolean, boolean);
   }
 
+  public static final class AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder {
+    ctor public AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat build();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setColumnIndex(int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setColumnSpan(int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setColumnTitle(String?);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setHeading(boolean);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setRowIndex(int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setRowSpan(int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setRowTitle(String?);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setSelected(boolean);
+  }
+
   public static class AccessibilityNodeInfoCompat.RangeInfoCompat {
+    ctor public AccessibilityNodeInfoCompat.RangeInfoCompat(int, float, float, float);
     method public float getCurrent();
     method public float getMax();
     method public float getMin();
@@ -3865,10 +3903,13 @@
     method public int getDisplayId();
     method public int getId();
     method public int getLayer();
+    method public androidx.core.os.LocaleListCompat getLocales();
     method public androidx.core.view.accessibility.AccessibilityWindowInfoCompat? getParent();
     method public void getRegionInScreen(android.graphics.Region);
     method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? getRoot();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? getRoot(int);
     method public CharSequence? getTitle();
+    method public long getTransitionTimeMillis();
     method public int getType();
     method public boolean isAccessibilityFocused();
     method public boolean isActive();
@@ -3881,6 +3922,7 @@
     field public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; // 0x4
     field public static final int TYPE_APPLICATION = 1; // 0x1
     field public static final int TYPE_INPUT_METHOD = 2; // 0x2
+    field public static final int TYPE_MAGNIFICATION_OVERLAY = 6; // 0x6
     field public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5; // 0x5
     field public static final int TYPE_SYSTEM = 3; // 0x3
   }
diff --git a/core/core/api/res-1.12.0-beta01.txt b/core/core/api/res-1.12.0-beta01.txt
new file mode 100644
index 0000000..dd913d3
--- /dev/null
+++ b/core/core/api/res-1.12.0-beta01.txt
@@ -0,0 +1,21 @@
+attr alpha
+attr font
+attr fontProviderAuthority
+attr fontProviderCerts
+attr fontProviderFetchStrategy
+attr fontProviderFetchTimeout
+attr fontProviderPackage
+attr fontProviderQuery
+attr fontProviderSystemFontFamily
+attr fontStyle
+attr fontVariationSettings
+attr fontWeight
+attr lStar
+attr queryPatterns
+attr shortcutMatchRequired
+attr ttcIndex
+style TextAppearance_Compat_Notification
+style TextAppearance_Compat_Notification_Info
+style TextAppearance_Compat_Notification_Line2
+style TextAppearance_Compat_Notification_Time
+style TextAppearance_Compat_Notification_Title
diff --git a/core/core/api/restricted_1.12.0-beta01.txt b/core/core/api/restricted_1.12.0-beta01.txt
new file mode 100644
index 0000000..7be35d5
--- /dev/null
+++ b/core/core/api/restricted_1.12.0-beta01.txt
@@ -0,0 +1,4751 @@
+// Signature format: 4.0
+package android.support.v4.os {
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ResultReceiver implements android.os.Parcelable {
+    ctor public ResultReceiver(android.os.Handler!);
+    method public int describeContents();
+    method protected void onReceiveResult(int, android.os.Bundle!);
+    method public void send(int, android.os.Bundle!);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.support.v4.os.ResultReceiver!>! CREATOR;
+  }
+
+}
+
+package androidx.core.accessibilityservice {
+
+  public final class AccessibilityServiceInfoCompat {
+    method public static String capabilityToString(int);
+    method public static String feedbackTypeToString(int);
+    method public static String? flagToString(int);
+    method public static int getCapabilities(android.accessibilityservice.AccessibilityServiceInfo);
+    method public static String? loadDescription(android.accessibilityservice.AccessibilityServiceInfo, android.content.pm.PackageManager);
+    field public static final int CAPABILITY_CAN_FILTER_KEY_EVENTS = 8; // 0x8
+    field public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 4; // 0x4
+    field public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 2; // 0x2
+    field public static final int CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 1; // 0x1
+    field public static final int FEEDBACK_ALL_MASK = -1; // 0xffffffff
+    field public static final int FEEDBACK_BRAILLE = 32; // 0x20
+    field public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 2; // 0x2
+    field public static final int FLAG_REPORT_VIEW_IDS = 16; // 0x10
+    field public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 8; // 0x8
+    field public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 32; // 0x20
+    field public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 4; // 0x4
+  }
+
+}
+
+package androidx.core.app {
+
+  public class ActivityCompat extends androidx.core.content.ContextCompat {
+    ctor protected ActivityCompat();
+    method public static void finishAffinity(android.app.Activity);
+    method public static void finishAfterTransition(android.app.Activity);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static androidx.core.app.ActivityCompat.PermissionCompatDelegate? getPermissionCompatDelegate();
+    method public static android.net.Uri? getReferrer(android.app.Activity);
+    method @Deprecated public static boolean invalidateOptionsMenu(android.app.Activity!);
+    method public static boolean isLaunchedFromBubble(android.app.Activity);
+    method public static void postponeEnterTransition(android.app.Activity);
+    method public static void recreate(android.app.Activity);
+    method public static androidx.core.view.DragAndDropPermissionsCompat? requestDragAndDropPermissions(android.app.Activity, android.view.DragEvent);
+    method public static void requestPermissions(android.app.Activity, String![], @IntRange(from=0) int);
+    method public static <T extends android.view.View> T requireViewById(android.app.Activity, @IdRes int);
+    method public static void setEnterSharedElementCallback(android.app.Activity, androidx.core.app.SharedElementCallback?);
+    method public static void setExitSharedElementCallback(android.app.Activity, androidx.core.app.SharedElementCallback?);
+    method public static void setLocusContext(android.app.Activity, androidx.core.content.LocusIdCompat?, android.os.Bundle?);
+    method public static void setPermissionCompatDelegate(androidx.core.app.ActivityCompat.PermissionCompatDelegate?);
+    method public static boolean shouldShowRequestPermissionRationale(android.app.Activity, String);
+    method public static void startActivityForResult(android.app.Activity, android.content.Intent, int, android.os.Bundle?);
+    method public static void startIntentSenderForResult(android.app.Activity, android.content.IntentSender, int, android.content.Intent?, int, int, int, android.os.Bundle?) throws android.content.IntentSender.SendIntentException;
+    method public static void startPostponedEnterTransition(android.app.Activity);
+  }
+
+  public static interface ActivityCompat.OnRequestPermissionsResultCallback {
+    method public void onRequestPermissionsResult(int, String![], int[]);
+  }
+
+  public static interface ActivityCompat.PermissionCompatDelegate {
+    method public boolean onActivityResult(android.app.Activity, @IntRange(from=0) int, int, android.content.Intent?);
+    method public boolean requestPermissions(android.app.Activity, String![], @IntRange(from=0) int);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static interface ActivityCompat.RequestPermissionsRequestCodeValidator {
+    method public void validateRequestPermissionsRequestCode(int);
+  }
+
+  public final class ActivityManagerCompat {
+    method public static boolean isLowRamDevice(android.app.ActivityManager);
+  }
+
+  public class ActivityOptionsCompat {
+    ctor protected ActivityOptionsCompat();
+    method public android.graphics.Rect? getLaunchBounds();
+    method public static androidx.core.app.ActivityOptionsCompat makeBasic();
+    method public static androidx.core.app.ActivityOptionsCompat makeClipRevealAnimation(android.view.View, int, int, int, int);
+    method public static androidx.core.app.ActivityOptionsCompat makeCustomAnimation(android.content.Context, int, int);
+    method public static androidx.core.app.ActivityOptionsCompat makeScaleUpAnimation(android.view.View, int, int, int, int);
+    method public static androidx.core.app.ActivityOptionsCompat makeSceneTransitionAnimation(android.app.Activity, android.view.View, String);
+    method public static androidx.core.app.ActivityOptionsCompat makeSceneTransitionAnimation(android.app.Activity, androidx.core.util.Pair<android.view.View!,java.lang.String!>!...);
+    method public static androidx.core.app.ActivityOptionsCompat makeTaskLaunchBehind();
+    method public static androidx.core.app.ActivityOptionsCompat makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int);
+    method public void requestUsageTimeReport(android.app.PendingIntent);
+    method public androidx.core.app.ActivityOptionsCompat setLaunchBounds(android.graphics.Rect?);
+    method public android.os.Bundle? toBundle();
+    method public void update(androidx.core.app.ActivityOptionsCompat);
+    field public static final String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
+    field public static final String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages";
+  }
+
+  public final class AlarmManagerCompat {
+    method public static void setAlarmClock(android.app.AlarmManager, long, android.app.PendingIntent, android.app.PendingIntent);
+    method public static void setAndAllowWhileIdle(android.app.AlarmManager, int, long, android.app.PendingIntent);
+    method public static void setExact(android.app.AlarmManager, int, long, android.app.PendingIntent);
+    method public static void setExactAndAllowWhileIdle(android.app.AlarmManager, int, long, android.app.PendingIntent);
+  }
+
+  @RequiresApi(28) public class AppComponentFactory extends android.app.AppComponentFactory {
+    ctor public AppComponentFactory();
+    method public final android.app.Activity instantiateActivity(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public android.app.Activity instantiateActivityCompat(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public final android.app.Application instantiateApplication(ClassLoader, String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public android.app.Application instantiateApplicationCompat(ClassLoader, String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public final android.content.ContentProvider instantiateProvider(ClassLoader, String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public android.content.ContentProvider instantiateProviderCompat(ClassLoader, String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public final android.content.BroadcastReceiver instantiateReceiver(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public android.content.BroadcastReceiver instantiateReceiverCompat(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public final android.app.Service instantiateService(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+    method public android.app.Service instantiateServiceCompat(ClassLoader, String, android.content.Intent?) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+  }
+
+  public class AppLaunchChecker {
+    ctor @Deprecated public AppLaunchChecker();
+    method public static boolean hasStartedFromLauncher(android.content.Context);
+    method public static void onActivityCreate(android.app.Activity);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class AppLocalesStorageHelper {
+    method public static void persistLocales(android.content.Context, String);
+    method public static String readLocales(android.content.Context);
+  }
+
+  public final class AppOpsManagerCompat {
+    method public static int checkOrNoteProxyOp(android.content.Context, int, String, String);
+    method public static int noteOp(android.content.Context, String, int, String);
+    method public static int noteOpNoThrow(android.content.Context, String, int, String);
+    method public static int noteProxyOp(android.content.Context, String, String);
+    method public static int noteProxyOpNoThrow(android.content.Context, String, String);
+    method public static String? permissionToOp(String);
+    field public static final int MODE_ALLOWED = 0; // 0x0
+    field public static final int MODE_DEFAULT = 3; // 0x3
+    field public static final int MODE_ERRORED = 2; // 0x2
+    field public static final int MODE_IGNORED = 1; // 0x1
+  }
+
+  @Deprecated public final class BundleCompat {
+    method @Deprecated public static android.os.IBinder? getBinder(android.os.Bundle, String?);
+    method @Deprecated public static void putBinder(android.os.Bundle, String?, android.os.IBinder?);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class ComponentActivity extends android.app.Activity implements androidx.core.view.KeyEventDispatcher.Component androidx.lifecycle.LifecycleOwner {
+    ctor public ComponentActivity();
+    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public <T extends androidx.core.app.ComponentActivity.ExtraData> T! getExtraData(Class<T!>!);
+    method public androidx.lifecycle.Lifecycle getLifecycle();
+    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void putExtraData(androidx.core.app.ComponentActivity.ExtraData!);
+    method protected final boolean shouldDumpInternalState(String![]?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean superDispatchKeyEvent(android.view.KeyEvent);
+  }
+
+  @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static class ComponentActivity.ExtraData {
+    ctor @Deprecated public ComponentActivity.ExtraData();
+  }
+
+  @RequiresApi(api=28) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class CoreComponentFactory extends android.app.AppComponentFactory {
+    ctor public CoreComponentFactory();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static interface CoreComponentFactory.CompatWrapped {
+    method public Object! getWrapper();
+  }
+
+  public class DialogCompat {
+    method public static android.view.View requireViewById(android.app.Dialog, int);
+  }
+
+  public class FrameMetricsAggregator {
+    ctor public FrameMetricsAggregator();
+    ctor public FrameMetricsAggregator(@androidx.core.app.FrameMetricsAggregator.MetricType int);
+    method public void add(android.app.Activity);
+    method public android.util.SparseIntArray![]? getMetrics();
+    method public android.util.SparseIntArray![]? remove(android.app.Activity);
+    method public android.util.SparseIntArray![]? reset();
+    method public android.util.SparseIntArray![]? stop();
+    field public static final int ANIMATION_DURATION = 256; // 0x100
+    field public static final int ANIMATION_INDEX = 8; // 0x8
+    field public static final int COMMAND_DURATION = 32; // 0x20
+    field public static final int COMMAND_INDEX = 5; // 0x5
+    field public static final int DELAY_DURATION = 128; // 0x80
+    field public static final int DELAY_INDEX = 7; // 0x7
+    field public static final int DRAW_DURATION = 8; // 0x8
+    field public static final int DRAW_INDEX = 3; // 0x3
+    field public static final int EVERY_DURATION = 511; // 0x1ff
+    field public static final int INPUT_DURATION = 2; // 0x2
+    field public static final int INPUT_INDEX = 1; // 0x1
+    field public static final int LAYOUT_MEASURE_DURATION = 4; // 0x4
+    field public static final int LAYOUT_MEASURE_INDEX = 2; // 0x2
+    field public static final int SWAP_DURATION = 64; // 0x40
+    field public static final int SWAP_INDEX = 6; // 0x6
+    field public static final int SYNC_DURATION = 16; // 0x10
+    field public static final int SYNC_INDEX = 4; // 0x4
+    field public static final int TOTAL_DURATION = 1; // 0x1
+    field public static final int TOTAL_INDEX = 0; // 0x0
+  }
+
+  @IntDef(flag=true, value={androidx.core.app.FrameMetricsAggregator.TOTAL_DURATION, androidx.core.app.FrameMetricsAggregator.INPUT_DURATION, androidx.core.app.FrameMetricsAggregator.LAYOUT_MEASURE_DURATION, androidx.core.app.FrameMetricsAggregator.DRAW_DURATION, androidx.core.app.FrameMetricsAggregator.SYNC_DURATION, androidx.core.app.FrameMetricsAggregator.COMMAND_DURATION, androidx.core.app.FrameMetricsAggregator.SWAP_DURATION, androidx.core.app.FrameMetricsAggregator.DELAY_DURATION, androidx.core.app.FrameMetricsAggregator.ANIMATION_DURATION, androidx.core.app.FrameMetricsAggregator.EVERY_DURATION}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface FrameMetricsAggregator.MetricType {
+  }
+
+  public final class GrammaticalInflectionManagerCompat {
+    method @AnyThread public static int getApplicationGrammaticalGender(android.content.Context);
+    method @AnyThread public static void setRequestedApplicationGrammaticalGender(android.content.Context, int);
+    field public static final int GRAMMATICAL_GENDER_FEMININE = 2; // 0x2
+    field public static final int GRAMMATICAL_GENDER_MASCULINE = 3; // 0x3
+    field public static final int GRAMMATICAL_GENDER_NEUTRAL = 1; // 0x1
+    field public static final int GRAMMATICAL_GENDER_NOT_SPECIFIED = 0; // 0x0
+  }
+
+  @Deprecated public abstract class JobIntentService extends android.app.Service {
+    ctor @Deprecated public JobIntentService();
+    method @Deprecated public static void enqueueWork(android.content.Context, android.content.ComponentName, int, android.content.Intent);
+    method @Deprecated public static void enqueueWork(android.content.Context, Class<?>, int, android.content.Intent);
+    method @Deprecated public boolean isStopped();
+    method @Deprecated public android.os.IBinder! onBind(android.content.Intent);
+    method @Deprecated protected abstract void onHandleWork(android.content.Intent);
+    method @Deprecated public boolean onStopCurrentWork();
+    method @Deprecated public void setInterruptIfStopped(boolean);
+  }
+
+  public final class LocaleManagerCompat {
+    method @AnyThread public static androidx.core.os.LocaleListCompat getApplicationLocales(android.content.Context);
+    method @AnyThread public static androidx.core.os.LocaleListCompat getSystemLocales(android.content.Context);
+  }
+
+  public final class MultiWindowModeChangedInfo {
+    ctor public MultiWindowModeChangedInfo(boolean);
+    ctor @RequiresApi(26) public MultiWindowModeChangedInfo(boolean, android.content.res.Configuration);
+    method @RequiresApi(26) public android.content.res.Configuration getNewConfig();
+    method public boolean isInMultiWindowMode();
+  }
+
+  public final class NavUtils {
+    method public static android.content.Intent? getParentActivityIntent(android.app.Activity);
+    method public static android.content.Intent? getParentActivityIntent(android.content.Context, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static android.content.Intent? getParentActivityIntent(android.content.Context, Class<?>) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static String? getParentActivityName(android.app.Activity);
+    method public static String? getParentActivityName(android.content.Context, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static void navigateUpFromSameTask(android.app.Activity);
+    method public static void navigateUpTo(android.app.Activity, android.content.Intent);
+    method public static boolean shouldUpRecreateTask(android.app.Activity, android.content.Intent);
+    field public static final String PARENT_ACTIVITY = "android.support.PARENT_ACTIVITY";
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface NotificationBuilderWithBuilderAccessor {
+    method public android.app.Notification.Builder! getBuilder();
+  }
+
+  public class NotificationChannelCompat {
+    method public boolean canBubble();
+    method public boolean canBypassDnd();
+    method public boolean canShowBadge();
+    method public android.media.AudioAttributes? getAudioAttributes();
+    method public String? getConversationId();
+    method public String? getDescription();
+    method public String? getGroup();
+    method public String getId();
+    method public int getImportance();
+    method public int getLightColor();
+    method @androidx.core.app.NotificationCompat.NotificationVisibility public int getLockscreenVisibility();
+    method public CharSequence? getName();
+    method public String? getParentChannelId();
+    method public android.net.Uri? getSound();
+    method public long[]? getVibrationPattern();
+    method public boolean isImportantConversation();
+    method public boolean shouldShowLights();
+    method public boolean shouldVibrate();
+    method public androidx.core.app.NotificationChannelCompat.Builder toBuilder();
+    field public static final String DEFAULT_CHANNEL_ID = "miscellaneous";
+  }
+
+  public static class NotificationChannelCompat.Builder {
+    ctor public NotificationChannelCompat.Builder(String, int);
+    method public androidx.core.app.NotificationChannelCompat build();
+    method public androidx.core.app.NotificationChannelCompat.Builder setConversationId(String, String);
+    method public androidx.core.app.NotificationChannelCompat.Builder setDescription(String?);
+    method public androidx.core.app.NotificationChannelCompat.Builder setGroup(String?);
+    method public androidx.core.app.NotificationChannelCompat.Builder setImportance(int);
+    method public androidx.core.app.NotificationChannelCompat.Builder setLightColor(int);
+    method public androidx.core.app.NotificationChannelCompat.Builder setLightsEnabled(boolean);
+    method public androidx.core.app.NotificationChannelCompat.Builder setName(CharSequence?);
+    method public androidx.core.app.NotificationChannelCompat.Builder setShowBadge(boolean);
+    method public androidx.core.app.NotificationChannelCompat.Builder setSound(android.net.Uri?, android.media.AudioAttributes?);
+    method public androidx.core.app.NotificationChannelCompat.Builder setVibrationEnabled(boolean);
+    method public androidx.core.app.NotificationChannelCompat.Builder setVibrationPattern(long[]?);
+  }
+
+  public class NotificationChannelGroupCompat {
+    method public java.util.List<androidx.core.app.NotificationChannelCompat!> getChannels();
+    method public String? getDescription();
+    method public String getId();
+    method public CharSequence? getName();
+    method public boolean isBlocked();
+    method public androidx.core.app.NotificationChannelGroupCompat.Builder toBuilder();
+  }
+
+  public static class NotificationChannelGroupCompat.Builder {
+    ctor public NotificationChannelGroupCompat.Builder(String);
+    method public androidx.core.app.NotificationChannelGroupCompat build();
+    method public androidx.core.app.NotificationChannelGroupCompat.Builder setDescription(String?);
+    method public androidx.core.app.NotificationChannelGroupCompat.Builder setName(CharSequence?);
+  }
+
+  public class NotificationCompat {
+    ctor @Deprecated public NotificationCompat();
+    method public static androidx.core.app.NotificationCompat.Action? getAction(android.app.Notification, int);
+    method public static int getActionCount(android.app.Notification);
+    method public static boolean getAllowSystemGeneratedContextualActions(android.app.Notification);
+    method public static boolean getAutoCancel(android.app.Notification);
+    method public static int getBadgeIconType(android.app.Notification);
+    method public static androidx.core.app.NotificationCompat.BubbleMetadata? getBubbleMetadata(android.app.Notification);
+    method public static String? getCategory(android.app.Notification);
+    method public static String? getChannelId(android.app.Notification);
+    method public static int getColor(android.app.Notification);
+    method @RequiresApi(19) public static CharSequence? getContentInfo(android.app.Notification);
+    method @RequiresApi(19) public static CharSequence? getContentText(android.app.Notification);
+    method @RequiresApi(19) public static CharSequence? getContentTitle(android.app.Notification);
+    method public static android.os.Bundle? getExtras(android.app.Notification);
+    method public static String? getGroup(android.app.Notification);
+    method @androidx.core.app.NotificationCompat.GroupAlertBehavior public static int getGroupAlertBehavior(android.app.Notification);
+    method @RequiresApi(21) public static java.util.List<androidx.core.app.NotificationCompat.Action!> getInvisibleActions(android.app.Notification);
+    method public static boolean getLocalOnly(android.app.Notification);
+    method public static androidx.core.content.LocusIdCompat? getLocusId(android.app.Notification);
+    method public static boolean getOngoing(android.app.Notification);
+    method public static boolean getOnlyAlertOnce(android.app.Notification);
+    method public static java.util.List<androidx.core.app.Person!> getPeople(android.app.Notification);
+    method public static android.app.Notification? getPublicVersion(android.app.Notification);
+    method public static CharSequence? getSettingsText(android.app.Notification);
+    method public static String? getShortcutId(android.app.Notification);
+    method @RequiresApi(19) public static boolean getShowWhen(android.app.Notification);
+    method public static String? getSortKey(android.app.Notification);
+    method @RequiresApi(19) public static CharSequence? getSubText(android.app.Notification);
+    method public static long getTimeoutAfter(android.app.Notification);
+    method @RequiresApi(19) public static boolean getUsesChronometer(android.app.Notification);
+    method @androidx.core.app.NotificationCompat.NotificationVisibility public static int getVisibility(android.app.Notification);
+    method public static boolean isGroupSummary(android.app.Notification);
+    method public static android.graphics.Bitmap? reduceLargeIconSize(android.content.Context, android.graphics.Bitmap?);
+    field public static final int BADGE_ICON_LARGE = 2; // 0x2
+    field public static final int BADGE_ICON_NONE = 0; // 0x0
+    field public static final int BADGE_ICON_SMALL = 1; // 0x1
+    field public static final String CATEGORY_ALARM = "alarm";
+    field public static final String CATEGORY_CALL = "call";
+    field public static final String CATEGORY_EMAIL = "email";
+    field public static final String CATEGORY_ERROR = "err";
+    field public static final String CATEGORY_EVENT = "event";
+    field public static final String CATEGORY_LOCATION_SHARING = "location_sharing";
+    field public static final String CATEGORY_MESSAGE = "msg";
+    field public static final String CATEGORY_MISSED_CALL = "missed_call";
+    field public static final String CATEGORY_NAVIGATION = "navigation";
+    field public static final String CATEGORY_PROGRESS = "progress";
+    field public static final String CATEGORY_PROMO = "promo";
+    field public static final String CATEGORY_RECOMMENDATION = "recommendation";
+    field public static final String CATEGORY_REMINDER = "reminder";
+    field public static final String CATEGORY_SERVICE = "service";
+    field public static final String CATEGORY_SOCIAL = "social";
+    field public static final String CATEGORY_STATUS = "status";
+    field public static final String CATEGORY_STOPWATCH = "stopwatch";
+    field public static final String CATEGORY_SYSTEM = "sys";
+    field public static final String CATEGORY_TRANSPORT = "transport";
+    field public static final String CATEGORY_WORKOUT = "workout";
+    field @ColorInt public static final int COLOR_DEFAULT = 0; // 0x0
+    field public static final int DEFAULT_ALL = -1; // 0xffffffff
+    field public static final int DEFAULT_LIGHTS = 4; // 0x4
+    field public static final int DEFAULT_SOUND = 1; // 0x1
+    field public static final int DEFAULT_VIBRATE = 2; // 0x2
+    field public static final String EXTRA_ANSWER_COLOR = "android.answerColor";
+    field public static final String EXTRA_ANSWER_INTENT = "android.answerIntent";
+    field public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents";
+    field public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
+    field public static final String EXTRA_BIG_TEXT = "android.bigText";
+    field public static final String EXTRA_CALL_IS_VIDEO = "android.callIsVideo";
+    field public static final String EXTRA_CALL_PERSON = "android.callPerson";
+    field public static final String EXTRA_CALL_PERSON_COMPAT = "android.callPersonCompat";
+    field public static final String EXTRA_CALL_TYPE = "android.callType";
+    field public static final String EXTRA_CHANNEL_GROUP_ID = "android.intent.extra.CHANNEL_GROUP_ID";
+    field public static final String EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID";
+    field public static final String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
+    field public static final String EXTRA_COLORIZED = "android.colorized";
+    field public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
+    field public static final String EXTRA_COMPAT_TEMPLATE = "androidx.core.app.extra.COMPAT_TEMPLATE";
+    field public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
+    field public static final String EXTRA_DECLINE_COLOR = "android.declineColor";
+    field public static final String EXTRA_DECLINE_INTENT = "android.declineIntent";
+    field public static final String EXTRA_HANG_UP_INTENT = "android.hangUpIntent";
+    field public static final String EXTRA_HIDDEN_CONVERSATION_TITLE = "android.hiddenConversationTitle";
+    field public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
+    field public static final String EXTRA_INFO_TEXT = "android.infoText";
+    field public static final String EXTRA_IS_GROUP_CONVERSATION = "android.isGroupConversation";
+    field public static final String EXTRA_LARGE_ICON = "android.largeIcon";
+    field public static final String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
+    field public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
+    field public static final String EXTRA_MESSAGES = "android.messages";
+    field public static final String EXTRA_MESSAGING_STYLE_USER = "android.messagingStyleUser";
+    field public static final String EXTRA_NOTIFICATION_ID = "android.intent.extra.NOTIFICATION_ID";
+    field public static final String EXTRA_NOTIFICATION_TAG = "android.intent.extra.NOTIFICATION_TAG";
+    field @Deprecated public static final String EXTRA_PEOPLE = "android.people";
+    field public static final String EXTRA_PEOPLE_LIST = "android.people.list";
+    field public static final String EXTRA_PICTURE = "android.picture";
+    field public static final String EXTRA_PICTURE_CONTENT_DESCRIPTION = "android.pictureContentDescription";
+    field public static final String EXTRA_PICTURE_ICON = "android.pictureIcon";
+    field public static final String EXTRA_PROGRESS = "android.progress";
+    field public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
+    field public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
+    field public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
+    field public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
+    field public static final String EXTRA_SHOW_BIG_PICTURE_WHEN_COLLAPSED = "android.showBigPictureWhenCollapsed";
+    field public static final String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
+    field public static final String EXTRA_SHOW_WHEN = "android.showWhen";
+    field public static final String EXTRA_SMALL_ICON = "android.icon";
+    field public static final String EXTRA_SUB_TEXT = "android.subText";
+    field public static final String EXTRA_SUMMARY_TEXT = "android.summaryText";
+    field public static final String EXTRA_TEMPLATE = "android.template";
+    field public static final String EXTRA_TEXT = "android.text";
+    field public static final String EXTRA_TEXT_LINES = "android.textLines";
+    field public static final String EXTRA_TITLE = "android.title";
+    field public static final String EXTRA_TITLE_BIG = "android.title.big";
+    field public static final String EXTRA_VERIFICATION_ICON = "android.verificationIcon";
+    field public static final String EXTRA_VERIFICATION_ICON_COMPAT = "android.verificationIconCompat";
+    field public static final String EXTRA_VERIFICATION_TEXT = "android.verificationText";
+    field public static final int FLAG_AUTO_CANCEL = 16; // 0x10
+    field public static final int FLAG_BUBBLE = 4096; // 0x1000
+    field public static final int FLAG_FOREGROUND_SERVICE = 64; // 0x40
+    field public static final int FLAG_GROUP_SUMMARY = 512; // 0x200
+    field @Deprecated public static final int FLAG_HIGH_PRIORITY = 128; // 0x80
+    field public static final int FLAG_INSISTENT = 4; // 0x4
+    field public static final int FLAG_LOCAL_ONLY = 256; // 0x100
+    field public static final int FLAG_NO_CLEAR = 32; // 0x20
+    field public static final int FLAG_ONGOING_EVENT = 2; // 0x2
+    field public static final int FLAG_ONLY_ALERT_ONCE = 8; // 0x8
+    field public static final int FLAG_SHOW_LIGHTS = 1; // 0x1
+    field public static final int FOREGROUND_SERVICE_DEFAULT = 0; // 0x0
+    field public static final int FOREGROUND_SERVICE_DEFERRED = 2; // 0x2
+    field public static final int FOREGROUND_SERVICE_IMMEDIATE = 1; // 0x1
+    field public static final int GROUP_ALERT_ALL = 0; // 0x0
+    field public static final int GROUP_ALERT_CHILDREN = 2; // 0x2
+    field public static final int GROUP_ALERT_SUMMARY = 1; // 0x1
+    field public static final String GROUP_KEY_SILENT = "silent";
+    field public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES = "android.intent.category.NOTIFICATION_PREFERENCES";
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final int MAX_ACTION_BUTTONS = 3; // 0x3
+    field public static final int PRIORITY_DEFAULT = 0; // 0x0
+    field public static final int PRIORITY_HIGH = 1; // 0x1
+    field public static final int PRIORITY_LOW = -1; // 0xffffffff
+    field public static final int PRIORITY_MAX = 2; // 0x2
+    field public static final int PRIORITY_MIN = -2; // 0xfffffffe
+    field public static final int STREAM_DEFAULT = -1; // 0xffffffff
+    field public static final int VISIBILITY_PRIVATE = 0; // 0x0
+    field public static final int VISIBILITY_PUBLIC = 1; // 0x1
+    field public static final int VISIBILITY_SECRET = -1; // 0xffffffff
+  }
+
+  public static class NotificationCompat.Action {
+    ctor public NotificationCompat.Action(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
+    ctor public NotificationCompat.Action(int, CharSequence?, android.app.PendingIntent?);
+    method public android.app.PendingIntent? getActionIntent();
+    method public boolean getAllowGeneratedReplies();
+    method public androidx.core.app.RemoteInput![]? getDataOnlyRemoteInputs();
+    method public android.os.Bundle getExtras();
+    method @Deprecated public int getIcon();
+    method public androidx.core.graphics.drawable.IconCompat? getIconCompat();
+    method public androidx.core.app.RemoteInput![]? getRemoteInputs();
+    method @androidx.core.app.NotificationCompat.Action.SemanticAction public int getSemanticAction();
+    method public boolean getShowsUserInterface();
+    method public CharSequence? getTitle();
+    method public boolean isAuthenticationRequired();
+    method public boolean isContextual();
+    field public static final int SEMANTIC_ACTION_ARCHIVE = 5; // 0x5
+    field public static final int SEMANTIC_ACTION_CALL = 10; // 0xa
+    field public static final int SEMANTIC_ACTION_DELETE = 4; // 0x4
+    field public static final int SEMANTIC_ACTION_MARK_AS_READ = 2; // 0x2
+    field public static final int SEMANTIC_ACTION_MARK_AS_UNREAD = 3; // 0x3
+    field public static final int SEMANTIC_ACTION_MUTE = 6; // 0x6
+    field public static final int SEMANTIC_ACTION_NONE = 0; // 0x0
+    field public static final int SEMANTIC_ACTION_REPLY = 1; // 0x1
+    field public static final int SEMANTIC_ACTION_THUMBS_DOWN = 9; // 0x9
+    field public static final int SEMANTIC_ACTION_THUMBS_UP = 8; // 0x8
+    field public static final int SEMANTIC_ACTION_UNMUTE = 7; // 0x7
+    field public android.app.PendingIntent? actionIntent;
+    field @Deprecated public int icon;
+    field public CharSequence! title;
+  }
+
+  public static final class NotificationCompat.Action.Builder {
+    ctor public NotificationCompat.Action.Builder(androidx.core.app.NotificationCompat.Action);
+    ctor public NotificationCompat.Action.Builder(androidx.core.graphics.drawable.IconCompat?, CharSequence?, android.app.PendingIntent?);
+    ctor public NotificationCompat.Action.Builder(int, CharSequence?, android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.Action.Builder addExtras(android.os.Bundle?);
+    method public androidx.core.app.NotificationCompat.Action.Builder addRemoteInput(androidx.core.app.RemoteInput?);
+    method public androidx.core.app.NotificationCompat.Action build();
+    method public androidx.core.app.NotificationCompat.Action.Builder extend(androidx.core.app.NotificationCompat.Action.Extender);
+    method @RequiresApi(19) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static androidx.core.app.NotificationCompat.Action.Builder fromAndroidAction(android.app.Notification.Action);
+    method public android.os.Bundle getExtras();
+    method public androidx.core.app.NotificationCompat.Action.Builder setAllowGeneratedReplies(boolean);
+    method public androidx.core.app.NotificationCompat.Action.Builder setAuthenticationRequired(boolean);
+    method public androidx.core.app.NotificationCompat.Action.Builder setContextual(boolean);
+    method public androidx.core.app.NotificationCompat.Action.Builder setSemanticAction(@androidx.core.app.NotificationCompat.Action.SemanticAction int);
+    method public androidx.core.app.NotificationCompat.Action.Builder setShowsUserInterface(boolean);
+  }
+
+  public static interface NotificationCompat.Action.Extender {
+    method public androidx.core.app.NotificationCompat.Action.Builder extend(androidx.core.app.NotificationCompat.Action.Builder);
+  }
+
+  @IntDef({androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_NONE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_REPLY, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_READ, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_UNREAD, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_DELETE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_ARCHIVE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_MUTE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_UNMUTE, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_THUMBS_UP, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_THUMBS_DOWN, androidx.core.app.NotificationCompat.Action.SEMANTIC_ACTION_CALL}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NotificationCompat.Action.SemanticAction {
+  }
+
+  public static final class NotificationCompat.Action.WearableExtender implements androidx.core.app.NotificationCompat.Action.Extender {
+    ctor public NotificationCompat.Action.WearableExtender();
+    ctor public NotificationCompat.Action.WearableExtender(androidx.core.app.NotificationCompat.Action);
+    method public androidx.core.app.NotificationCompat.Action.WearableExtender clone();
+    method public androidx.core.app.NotificationCompat.Action.Builder extend(androidx.core.app.NotificationCompat.Action.Builder);
+    method @Deprecated public CharSequence? getCancelLabel();
+    method @Deprecated public CharSequence? getConfirmLabel();
+    method public boolean getHintDisplayActionInline();
+    method public boolean getHintLaunchesActivity();
+    method @Deprecated public CharSequence? getInProgressLabel();
+    method public boolean isAvailableOffline();
+    method public androidx.core.app.NotificationCompat.Action.WearableExtender setAvailableOffline(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.Action.WearableExtender setCancelLabel(CharSequence?);
+    method @Deprecated public androidx.core.app.NotificationCompat.Action.WearableExtender setConfirmLabel(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Action.WearableExtender setHintDisplayActionInline(boolean);
+    method public androidx.core.app.NotificationCompat.Action.WearableExtender setHintLaunchesActivity(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.Action.WearableExtender setInProgressLabel(CharSequence?);
+  }
+
+  @IntDef({androidx.core.app.NotificationCompat.BADGE_ICON_NONE, androidx.core.app.NotificationCompat.BADGE_ICON_SMALL, androidx.core.app.NotificationCompat.BADGE_ICON_LARGE}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NotificationCompat.BadgeIconType {
+  }
+
+  public static class NotificationCompat.BigPictureStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor public NotificationCompat.BigPictureStyle();
+    ctor public NotificationCompat.BigPictureStyle(androidx.core.app.NotificationCompat.Builder?);
+    method public androidx.core.app.NotificationCompat.BigPictureStyle bigLargeIcon(android.graphics.Bitmap?);
+    method @RequiresApi(23) public androidx.core.app.NotificationCompat.BigPictureStyle bigLargeIcon(android.graphics.drawable.Icon?);
+    method public androidx.core.app.NotificationCompat.BigPictureStyle bigPicture(android.graphics.Bitmap?);
+    method @RequiresApi(31) public androidx.core.app.NotificationCompat.BigPictureStyle bigPicture(android.graphics.drawable.Icon?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static androidx.core.graphics.drawable.IconCompat? getPictureIcon(android.os.Bundle?);
+    method public androidx.core.app.NotificationCompat.BigPictureStyle setBigContentTitle(CharSequence?);
+    method @RequiresApi(31) public androidx.core.app.NotificationCompat.BigPictureStyle setContentDescription(CharSequence?);
+    method public androidx.core.app.NotificationCompat.BigPictureStyle setSummaryText(CharSequence?);
+    method @RequiresApi(31) public androidx.core.app.NotificationCompat.BigPictureStyle showBigPictureWhenCollapsed(boolean);
+  }
+
+  public static class NotificationCompat.BigTextStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor public NotificationCompat.BigTextStyle();
+    ctor public NotificationCompat.BigTextStyle(androidx.core.app.NotificationCompat.Builder?);
+    method public androidx.core.app.NotificationCompat.BigTextStyle bigText(CharSequence?);
+    method public androidx.core.app.NotificationCompat.BigTextStyle setBigContentTitle(CharSequence?);
+    method public androidx.core.app.NotificationCompat.BigTextStyle setSummaryText(CharSequence?);
+  }
+
+  public static final class NotificationCompat.BubbleMetadata {
+    method public static androidx.core.app.NotificationCompat.BubbleMetadata? fromPlatform(android.app.Notification.BubbleMetadata?);
+    method public boolean getAutoExpandBubble();
+    method public android.app.PendingIntent? getDeleteIntent();
+    method @Dimension(unit=androidx.annotation.Dimension.DP) public int getDesiredHeight();
+    method @DimenRes public int getDesiredHeightResId();
+    method public androidx.core.graphics.drawable.IconCompat? getIcon();
+    method public android.app.PendingIntent? getIntent();
+    method public String? getShortcutId();
+    method public boolean isNotificationSuppressed();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setFlags(int);
+    method public static android.app.Notification.BubbleMetadata? toPlatform(androidx.core.app.NotificationCompat.BubbleMetadata?);
+  }
+
+  public static final class NotificationCompat.BubbleMetadata.Builder {
+    ctor @Deprecated public NotificationCompat.BubbleMetadata.Builder();
+    ctor public NotificationCompat.BubbleMetadata.Builder(android.app.PendingIntent, androidx.core.graphics.drawable.IconCompat);
+    ctor @RequiresApi(30) public NotificationCompat.BubbleMetadata.Builder(String);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata build();
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setAutoExpandBubble(boolean);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setDeleteIntent(android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setDesiredHeight(@Dimension(unit=androidx.annotation.Dimension.DP) int);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setDesiredHeightResId(@DimenRes int);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setIcon(androidx.core.graphics.drawable.IconCompat);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setIntent(android.app.PendingIntent);
+    method public androidx.core.app.NotificationCompat.BubbleMetadata.Builder setSuppressNotification(boolean);
+  }
+
+  public static class NotificationCompat.Builder {
+    ctor @Deprecated public NotificationCompat.Builder(android.content.Context);
+    ctor @RequiresApi(19) public NotificationCompat.Builder(android.content.Context, android.app.Notification);
+    ctor public NotificationCompat.Builder(android.content.Context, String);
+    method public androidx.core.app.NotificationCompat.Builder addAction(androidx.core.app.NotificationCompat.Action?);
+    method public androidx.core.app.NotificationCompat.Builder addAction(int, CharSequence?, android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.Builder addExtras(android.os.Bundle?);
+    method @RequiresApi(21) public androidx.core.app.NotificationCompat.Builder addInvisibleAction(androidx.core.app.NotificationCompat.Action?);
+    method @RequiresApi(21) public androidx.core.app.NotificationCompat.Builder addInvisibleAction(int, CharSequence?, android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.Builder addPerson(androidx.core.app.Person?);
+    method @Deprecated public androidx.core.app.NotificationCompat.Builder addPerson(String?);
+    method public android.app.Notification build();
+    method public androidx.core.app.NotificationCompat.Builder clearActions();
+    method public androidx.core.app.NotificationCompat.Builder clearInvisibleActions();
+    method public androidx.core.app.NotificationCompat.Builder clearPeople();
+    method public android.widget.RemoteViews? createBigContentView();
+    method public android.widget.RemoteViews? createContentView();
+    method public android.widget.RemoteViews? createHeadsUpContentView();
+    method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Extender);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.widget.RemoteViews! getBigContentView();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.core.app.NotificationCompat.BubbleMetadata? getBubbleMetadata();
+    method @ColorInt @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int getColor();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.widget.RemoteViews! getContentView();
+    method public android.os.Bundle getExtras();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int getForegroundServiceBehavior();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.widget.RemoteViews! getHeadsUpContentView();
+    method @Deprecated public android.app.Notification getNotification();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int getPriority();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public long getWhenIfShowing();
+    method protected static CharSequence? limitCharSequenceLength(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setAllowSystemGeneratedContextualActions(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setAutoCancel(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setBadgeIconType(@androidx.core.app.NotificationCompat.BadgeIconType int);
+    method public androidx.core.app.NotificationCompat.Builder setBubbleMetadata(androidx.core.app.NotificationCompat.BubbleMetadata?);
+    method public androidx.core.app.NotificationCompat.Builder setCategory(String?);
+    method public androidx.core.app.NotificationCompat.Builder setChannelId(String);
+    method @RequiresApi(24) public androidx.core.app.NotificationCompat.Builder setChronometerCountDown(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setColor(@ColorInt int);
+    method public androidx.core.app.NotificationCompat.Builder setColorized(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setContent(android.widget.RemoteViews?);
+    method public androidx.core.app.NotificationCompat.Builder setContentInfo(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setContentIntent(android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.Builder setContentText(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setContentTitle(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setCustomBigContentView(android.widget.RemoteViews?);
+    method public androidx.core.app.NotificationCompat.Builder setCustomContentView(android.widget.RemoteViews?);
+    method public androidx.core.app.NotificationCompat.Builder setCustomHeadsUpContentView(android.widget.RemoteViews?);
+    method public androidx.core.app.NotificationCompat.Builder setDefaults(int);
+    method public androidx.core.app.NotificationCompat.Builder setDeleteIntent(android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.Builder setExtras(android.os.Bundle?);
+    method public androidx.core.app.NotificationCompat.Builder setForegroundServiceBehavior(@androidx.core.app.NotificationCompat.ServiceNotificationBehavior int);
+    method public androidx.core.app.NotificationCompat.Builder setFullScreenIntent(android.app.PendingIntent?, boolean);
+    method public androidx.core.app.NotificationCompat.Builder setGroup(String?);
+    method public androidx.core.app.NotificationCompat.Builder setGroupAlertBehavior(@androidx.core.app.NotificationCompat.GroupAlertBehavior int);
+    method public androidx.core.app.NotificationCompat.Builder setGroupSummary(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setLargeIcon(android.graphics.Bitmap?);
+    method @RequiresApi(23) public androidx.core.app.NotificationCompat.Builder setLargeIcon(android.graphics.drawable.Icon?);
+    method public androidx.core.app.NotificationCompat.Builder setLights(@ColorInt int, int, int);
+    method public androidx.core.app.NotificationCompat.Builder setLocalOnly(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setLocusId(androidx.core.content.LocusIdCompat?);
+    method @Deprecated public androidx.core.app.NotificationCompat.Builder setNotificationSilent();
+    method public androidx.core.app.NotificationCompat.Builder setNumber(int);
+    method public androidx.core.app.NotificationCompat.Builder setOngoing(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setOnlyAlertOnce(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setPriority(int);
+    method public androidx.core.app.NotificationCompat.Builder setProgress(int, int, boolean);
+    method public androidx.core.app.NotificationCompat.Builder setPublicVersion(android.app.Notification?);
+    method public androidx.core.app.NotificationCompat.Builder setRemoteInputHistory(CharSequence![]?);
+    method public androidx.core.app.NotificationCompat.Builder setSettingsText(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setShortcutId(String?);
+    method public androidx.core.app.NotificationCompat.Builder setShortcutInfo(androidx.core.content.pm.ShortcutInfoCompat?);
+    method public androidx.core.app.NotificationCompat.Builder setShowWhen(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setSilent(boolean);
+    method @RequiresApi(23) public androidx.core.app.NotificationCompat.Builder setSmallIcon(androidx.core.graphics.drawable.IconCompat);
+    method public androidx.core.app.NotificationCompat.Builder setSmallIcon(int);
+    method public androidx.core.app.NotificationCompat.Builder setSmallIcon(int, int);
+    method public androidx.core.app.NotificationCompat.Builder setSortKey(String?);
+    method public androidx.core.app.NotificationCompat.Builder setSound(android.net.Uri?);
+    method public androidx.core.app.NotificationCompat.Builder setSound(android.net.Uri?, @androidx.core.app.NotificationCompat.StreamType int);
+    method public androidx.core.app.NotificationCompat.Builder setStyle(androidx.core.app.NotificationCompat.Style?);
+    method public androidx.core.app.NotificationCompat.Builder setSubText(CharSequence?);
+    method public androidx.core.app.NotificationCompat.Builder setTicker(CharSequence?);
+    method @Deprecated public androidx.core.app.NotificationCompat.Builder setTicker(CharSequence?, android.widget.RemoteViews?);
+    method public androidx.core.app.NotificationCompat.Builder setTimeoutAfter(long);
+    method public androidx.core.app.NotificationCompat.Builder setUsesChronometer(boolean);
+    method public androidx.core.app.NotificationCompat.Builder setVibrate(long[]?);
+    method public androidx.core.app.NotificationCompat.Builder setVisibility(@androidx.core.app.NotificationCompat.NotificationVisibility int);
+    method public androidx.core.app.NotificationCompat.Builder setWhen(long);
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public java.util.ArrayList<androidx.core.app.NotificationCompat.Action!>! mActions;
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.Context! mContext;
+    field @Deprecated public java.util.ArrayList<java.lang.String!>! mPeople;
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public java.util.ArrayList<androidx.core.app.Person!> mPersonList;
+  }
+
+  public static class NotificationCompat.CallStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor public NotificationCompat.CallStyle();
+    ctor public NotificationCompat.CallStyle(androidx.core.app.NotificationCompat.Builder?);
+    method public static androidx.core.app.NotificationCompat.CallStyle forIncomingCall(androidx.core.app.Person, android.app.PendingIntent, android.app.PendingIntent);
+    method public static androidx.core.app.NotificationCompat.CallStyle forOngoingCall(androidx.core.app.Person, android.app.PendingIntent);
+    method public static androidx.core.app.NotificationCompat.CallStyle forScreeningCall(androidx.core.app.Person, android.app.PendingIntent, android.app.PendingIntent);
+    method @RequiresApi(20) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public java.util.ArrayList<androidx.core.app.NotificationCompat.Action!> getActionsListWithSystemActions();
+    method public androidx.core.app.NotificationCompat.CallStyle setAnswerButtonColorHint(@ColorInt int);
+    method public androidx.core.app.NotificationCompat.CallStyle setDeclineButtonColorHint(@ColorInt int);
+    method public androidx.core.app.NotificationCompat.CallStyle setIsVideo(boolean);
+    method public androidx.core.app.NotificationCompat.CallStyle setVerificationIcon(android.graphics.Bitmap?);
+    method @RequiresApi(23) public androidx.core.app.NotificationCompat.CallStyle setVerificationIcon(android.graphics.drawable.Icon?);
+    method public androidx.core.app.NotificationCompat.CallStyle setVerificationText(CharSequence?);
+    field public static final int CALL_TYPE_INCOMING = 1; // 0x1
+    field public static final int CALL_TYPE_ONGOING = 2; // 0x2
+    field public static final int CALL_TYPE_SCREENING = 3; // 0x3
+    field public static final int CALL_TYPE_UNKNOWN = 0; // 0x0
+  }
+
+  @IntDef({androidx.core.app.NotificationCompat.CallStyle.CALL_TYPE_UNKNOWN, androidx.core.app.NotificationCompat.CallStyle.CALL_TYPE_INCOMING, androidx.core.app.NotificationCompat.CallStyle.CALL_TYPE_ONGOING, androidx.core.app.NotificationCompat.CallStyle.CALL_TYPE_SCREENING}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NotificationCompat.CallStyle.CallType {
+  }
+
+  public static final class NotificationCompat.CarExtender implements androidx.core.app.NotificationCompat.Extender {
+    ctor public NotificationCompat.CarExtender();
+    ctor public NotificationCompat.CarExtender(android.app.Notification);
+    method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Builder);
+    method @ColorInt public int getColor();
+    method public android.graphics.Bitmap? getLargeIcon();
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation? getUnreadConversation();
+    method public androidx.core.app.NotificationCompat.CarExtender setColor(@ColorInt int);
+    method public androidx.core.app.NotificationCompat.CarExtender setLargeIcon(android.graphics.Bitmap?);
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender setUnreadConversation(androidx.core.app.NotificationCompat.CarExtender.UnreadConversation?);
+  }
+
+  @Deprecated public static class NotificationCompat.CarExtender.UnreadConversation {
+    method @Deprecated public long getLatestTimestamp();
+    method @Deprecated public String![]? getMessages();
+    method @Deprecated public String? getParticipant();
+    method @Deprecated public String![]? getParticipants();
+    method @Deprecated public android.app.PendingIntent? getReadPendingIntent();
+    method @Deprecated public androidx.core.app.RemoteInput? getRemoteInput();
+    method @Deprecated public android.app.PendingIntent? getReplyPendingIntent();
+  }
+
+  @Deprecated public static class NotificationCompat.CarExtender.UnreadConversation.Builder {
+    ctor @Deprecated public NotificationCompat.CarExtender.UnreadConversation.Builder(String);
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation.Builder addMessage(String?);
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation build();
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation.Builder setLatestTimestamp(long);
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation.Builder setReadPendingIntent(android.app.PendingIntent?);
+    method @Deprecated public androidx.core.app.NotificationCompat.CarExtender.UnreadConversation.Builder setReplyAction(android.app.PendingIntent?, androidx.core.app.RemoteInput?);
+  }
+
+  public static class NotificationCompat.DecoratedCustomViewStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor public NotificationCompat.DecoratedCustomViewStyle();
+  }
+
+  public static interface NotificationCompat.Extender {
+    method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Builder);
+  }
+
+  @IntDef({androidx.core.app.NotificationCompat.GROUP_ALERT_ALL, androidx.core.app.NotificationCompat.GROUP_ALERT_SUMMARY, androidx.core.app.NotificationCompat.GROUP_ALERT_CHILDREN}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NotificationCompat.GroupAlertBehavior {
+  }
+
+  public static class NotificationCompat.InboxStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor public NotificationCompat.InboxStyle();
+    ctor public NotificationCompat.InboxStyle(androidx.core.app.NotificationCompat.Builder?);
+    method public androidx.core.app.NotificationCompat.InboxStyle addLine(CharSequence?);
+    method public androidx.core.app.NotificationCompat.InboxStyle setBigContentTitle(CharSequence?);
+    method public androidx.core.app.NotificationCompat.InboxStyle setSummaryText(CharSequence?);
+  }
+
+  public static class NotificationCompat.MessagingStyle extends androidx.core.app.NotificationCompat.Style {
+    ctor public NotificationCompat.MessagingStyle(androidx.core.app.Person);
+    ctor @Deprecated public NotificationCompat.MessagingStyle(CharSequence);
+    method public androidx.core.app.NotificationCompat.MessagingStyle addHistoricMessage(androidx.core.app.NotificationCompat.MessagingStyle.Message?);
+    method public androidx.core.app.NotificationCompat.MessagingStyle addMessage(androidx.core.app.NotificationCompat.MessagingStyle.Message?);
+    method public androidx.core.app.NotificationCompat.MessagingStyle addMessage(CharSequence?, long, androidx.core.app.Person?);
+    method @Deprecated public androidx.core.app.NotificationCompat.MessagingStyle addMessage(CharSequence?, long, CharSequence?);
+    method public static androidx.core.app.NotificationCompat.MessagingStyle? extractMessagingStyleFromNotification(android.app.Notification);
+    method public CharSequence? getConversationTitle();
+    method public java.util.List<androidx.core.app.NotificationCompat.MessagingStyle.Message!> getHistoricMessages();
+    method public java.util.List<androidx.core.app.NotificationCompat.MessagingStyle.Message!> getMessages();
+    method public androidx.core.app.Person getUser();
+    method @Deprecated public CharSequence? getUserDisplayName();
+    method public boolean isGroupConversation();
+    method public androidx.core.app.NotificationCompat.MessagingStyle setConversationTitle(CharSequence?);
+    method public androidx.core.app.NotificationCompat.MessagingStyle setGroupConversation(boolean);
+    field public static final int MAXIMUM_RETAINED_MESSAGES = 25; // 0x19
+  }
+
+  public static final class NotificationCompat.MessagingStyle.Message {
+    ctor public NotificationCompat.MessagingStyle.Message(CharSequence?, long, androidx.core.app.Person?);
+    ctor @Deprecated public NotificationCompat.MessagingStyle.Message(CharSequence?, long, CharSequence?);
+    method public String? getDataMimeType();
+    method public android.net.Uri? getDataUri();
+    method public android.os.Bundle getExtras();
+    method public androidx.core.app.Person? getPerson();
+    method @Deprecated public CharSequence? getSender();
+    method public CharSequence? getText();
+    method public long getTimestamp();
+    method public androidx.core.app.NotificationCompat.MessagingStyle.Message setData(String?, android.net.Uri?);
+  }
+
+  @IntDef({androidx.core.app.NotificationCompat.VISIBILITY_PUBLIC, androidx.core.app.NotificationCompat.VISIBILITY_PRIVATE, androidx.core.app.NotificationCompat.VISIBILITY_SECRET}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NotificationCompat.NotificationVisibility {
+  }
+
+  @IntDef({androidx.core.app.NotificationCompat.FOREGROUND_SERVICE_DEFAULT, androidx.core.app.NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE, androidx.core.app.NotificationCompat.FOREGROUND_SERVICE_DEFERRED}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NotificationCompat.ServiceNotificationBehavior {
+  }
+
+  @IntDef({android.media.AudioManager.STREAM_VOICE_CALL, android.media.AudioManager.STREAM_SYSTEM, android.media.AudioManager.STREAM_RING, android.media.AudioManager.STREAM_MUSIC, android.media.AudioManager.STREAM_ALARM, android.media.AudioManager.STREAM_NOTIFICATION, android.media.AudioManager.STREAM_DTMF, android.media.AudioManager.STREAM_ACCESSIBILITY}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NotificationCompat.StreamType {
+  }
+
+  public abstract static class NotificationCompat.Style {
+    ctor public NotificationCompat.Style();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void addCompatExtras(android.os.Bundle);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void apply(androidx.core.app.NotificationBuilderWithBuilderAccessor!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.widget.RemoteViews applyStandardTemplate(boolean, int, boolean);
+    method public android.app.Notification? build();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void buildIntoRemoteViews(android.widget.RemoteViews!, android.widget.RemoteViews!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) protected void clearCompatExtraKeys(android.os.Bundle);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.Bitmap! createColoredBitmap(int, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean displayCustomViewInline();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static androidx.core.app.NotificationCompat.Style? extractStyleFromNotification(android.app.Notification);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) protected String? getClassName();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.widget.RemoteViews! makeBigContentView(androidx.core.app.NotificationBuilderWithBuilderAccessor!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.widget.RemoteViews! makeContentView(androidx.core.app.NotificationBuilderWithBuilderAccessor!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.widget.RemoteViews! makeHeadsUpContentView(androidx.core.app.NotificationBuilderWithBuilderAccessor!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) protected void restoreFromCompatExtras(android.os.Bundle);
+    method public void setBuilder(androidx.core.app.NotificationCompat.Builder?);
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) protected androidx.core.app.NotificationCompat.Builder! mBuilder;
+  }
+
+  public static final class NotificationCompat.TvExtender implements androidx.core.app.NotificationCompat.Extender {
+    ctor public NotificationCompat.TvExtender();
+    ctor public NotificationCompat.TvExtender(android.app.Notification);
+    method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Builder);
+    method public String? getChannelId();
+    method public android.app.PendingIntent? getContentIntent();
+    method public android.app.PendingIntent? getDeleteIntent();
+    method public boolean isAvailableOnTv();
+    method public boolean isSuppressShowOverApps();
+    method public androidx.core.app.NotificationCompat.TvExtender setChannelId(String?);
+    method public androidx.core.app.NotificationCompat.TvExtender setContentIntent(android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.TvExtender setDeleteIntent(android.app.PendingIntent?);
+    method public androidx.core.app.NotificationCompat.TvExtender setSuppressShowOverApps(boolean);
+  }
+
+  public static final class NotificationCompat.WearableExtender implements androidx.core.app.NotificationCompat.Extender {
+    ctor public NotificationCompat.WearableExtender();
+    ctor public NotificationCompat.WearableExtender(android.app.Notification);
+    method public androidx.core.app.NotificationCompat.WearableExtender addAction(androidx.core.app.NotificationCompat.Action);
+    method public androidx.core.app.NotificationCompat.WearableExtender addActions(java.util.List<androidx.core.app.NotificationCompat.Action!>);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender addPage(android.app.Notification);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender addPages(java.util.List<android.app.Notification!>);
+    method public androidx.core.app.NotificationCompat.WearableExtender clearActions();
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender clearPages();
+    method public androidx.core.app.NotificationCompat.WearableExtender clone();
+    method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Builder);
+    method public java.util.List<androidx.core.app.NotificationCompat.Action!> getActions();
+    method @Deprecated public android.graphics.Bitmap? getBackground();
+    method public String? getBridgeTag();
+    method public int getContentAction();
+    method @Deprecated public int getContentIcon();
+    method @Deprecated public int getContentIconGravity();
+    method public boolean getContentIntentAvailableOffline();
+    method @Deprecated public int getCustomContentHeight();
+    method @Deprecated public int getCustomSizePreset();
+    method public String? getDismissalId();
+    method @Deprecated public android.app.PendingIntent? getDisplayIntent();
+    method @Deprecated public int getGravity();
+    method @Deprecated public boolean getHintAmbientBigPicture();
+    method @Deprecated public boolean getHintAvoidBackgroundClipping();
+    method public boolean getHintContentIntentLaunchesActivity();
+    method @Deprecated public boolean getHintHideIcon();
+    method @Deprecated public int getHintScreenTimeout();
+    method @Deprecated public boolean getHintShowBackgroundOnly();
+    method @Deprecated public java.util.List<android.app.Notification!> getPages();
+    method public boolean getStartScrollBottom();
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setBackground(android.graphics.Bitmap?);
+    method public androidx.core.app.NotificationCompat.WearableExtender setBridgeTag(String?);
+    method public androidx.core.app.NotificationCompat.WearableExtender setContentAction(int);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setContentIcon(int);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setContentIconGravity(int);
+    method public androidx.core.app.NotificationCompat.WearableExtender setContentIntentAvailableOffline(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setCustomContentHeight(int);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setCustomSizePreset(int);
+    method public androidx.core.app.NotificationCompat.WearableExtender setDismissalId(String?);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setDisplayIntent(android.app.PendingIntent?);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setGravity(int);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintAmbientBigPicture(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintAvoidBackgroundClipping(boolean);
+    method public androidx.core.app.NotificationCompat.WearableExtender setHintContentIntentLaunchesActivity(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintHideIcon(boolean);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintScreenTimeout(int);
+    method @Deprecated public androidx.core.app.NotificationCompat.WearableExtender setHintShowBackgroundOnly(boolean);
+    method public androidx.core.app.NotificationCompat.WearableExtender setStartScrollBottom(boolean);
+    field @Deprecated public static final int SCREEN_TIMEOUT_LONG = -1; // 0xffffffff
+    field @Deprecated public static final int SCREEN_TIMEOUT_SHORT = 0; // 0x0
+    field @Deprecated public static final int SIZE_DEFAULT = 0; // 0x0
+    field @Deprecated public static final int SIZE_FULL_SCREEN = 5; // 0x5
+    field @Deprecated public static final int SIZE_LARGE = 4; // 0x4
+    field @Deprecated public static final int SIZE_MEDIUM = 3; // 0x3
+    field @Deprecated public static final int SIZE_SMALL = 2; // 0x2
+    field @Deprecated public static final int SIZE_XSMALL = 1; // 0x1
+    field public static final int UNSET_ACTION_INDEX = -1; // 0xffffffff
+  }
+
+  public final class NotificationCompatExtras {
+    field public static final String EXTRA_ACTION_EXTRAS = "android.support.actionExtras";
+    field public static final String EXTRA_GROUP_KEY = "android.support.groupKey";
+    field public static final String EXTRA_GROUP_SUMMARY = "android.support.isGroupSummary";
+    field public static final String EXTRA_LOCAL_ONLY = "android.support.localOnly";
+    field public static final String EXTRA_REMOTE_INPUTS = "android.support.remoteInputs";
+    field public static final String EXTRA_SORT_KEY = "android.support.sortKey";
+  }
+
+  public abstract class NotificationCompatSideChannelService extends android.app.Service {
+    ctor public NotificationCompatSideChannelService();
+    method public abstract void cancel(String!, int, String!);
+    method public abstract void cancelAll(String!);
+    method public abstract void notify(String!, int, String!, android.app.Notification!);
+    method public android.os.IBinder! onBind(android.content.Intent!);
+  }
+
+  public final class NotificationManagerCompat {
+    method public boolean areNotificationsEnabled();
+    method public boolean canUseFullScreenIntent();
+    method public void cancel(int);
+    method public void cancel(String?, int);
+    method public void cancelAll();
+    method public void createNotificationChannel(android.app.NotificationChannel);
+    method public void createNotificationChannel(androidx.core.app.NotificationChannelCompat);
+    method public void createNotificationChannelGroup(android.app.NotificationChannelGroup);
+    method public void createNotificationChannelGroup(androidx.core.app.NotificationChannelGroupCompat);
+    method public void createNotificationChannelGroups(java.util.List<android.app.NotificationChannelGroup!>);
+    method public void createNotificationChannelGroupsCompat(java.util.List<androidx.core.app.NotificationChannelGroupCompat!>);
+    method public void createNotificationChannels(java.util.List<android.app.NotificationChannel!>);
+    method public void createNotificationChannelsCompat(java.util.List<androidx.core.app.NotificationChannelCompat!>);
+    method public void deleteNotificationChannel(String);
+    method public void deleteNotificationChannelGroup(String);
+    method public void deleteUnlistedNotificationChannels(java.util.Collection<java.lang.String!>);
+    method public static androidx.core.app.NotificationManagerCompat from(android.content.Context);
+    method public java.util.List<android.service.notification.StatusBarNotification!> getActiveNotifications();
+    method @androidx.core.app.NotificationManagerCompat.InterruptionFilter public int getCurrentInterruptionFilter();
+    method public static java.util.Set<java.lang.String!> getEnabledListenerPackages(android.content.Context);
+    method public int getImportance();
+    method public android.app.NotificationChannel? getNotificationChannel(String);
+    method public android.app.NotificationChannel? getNotificationChannel(String, String);
+    method public androidx.core.app.NotificationChannelCompat? getNotificationChannelCompat(String);
+    method public androidx.core.app.NotificationChannelCompat? getNotificationChannelCompat(String, String);
+    method public android.app.NotificationChannelGroup? getNotificationChannelGroup(String);
+    method public androidx.core.app.NotificationChannelGroupCompat? getNotificationChannelGroupCompat(String);
+    method public java.util.List<android.app.NotificationChannelGroup!> getNotificationChannelGroups();
+    method public java.util.List<androidx.core.app.NotificationChannelGroupCompat!> getNotificationChannelGroupsCompat();
+    method public java.util.List<android.app.NotificationChannel!> getNotificationChannels();
+    method public java.util.List<androidx.core.app.NotificationChannelCompat!> getNotificationChannelsCompat();
+    method @RequiresPermission(android.Manifest.permission.POST_NOTIFICATIONS) public void notify(int, android.app.Notification);
+    method @RequiresPermission(android.Manifest.permission.POST_NOTIFICATIONS) public void notify(String?, int, android.app.Notification);
+    method @RequiresPermission(android.Manifest.permission.POST_NOTIFICATIONS) public void notify(java.util.List<androidx.core.app.NotificationManagerCompat.NotificationWithIdAndTag!>);
+    field public static final String ACTION_BIND_SIDE_CHANNEL = "android.support.BIND_NOTIFICATION_SIDE_CHANNEL";
+    field public static final String EXTRA_USE_SIDE_CHANNEL = "android.support.useSideChannel";
+    field public static final int IMPORTANCE_DEFAULT = 3; // 0x3
+    field public static final int IMPORTANCE_HIGH = 4; // 0x4
+    field public static final int IMPORTANCE_LOW = 2; // 0x2
+    field public static final int IMPORTANCE_MAX = 5; // 0x5
+    field public static final int IMPORTANCE_MIN = 1; // 0x1
+    field public static final int IMPORTANCE_NONE = 0; // 0x0
+    field public static final int IMPORTANCE_UNSPECIFIED = -1000; // 0xfffffc18
+    field public static final int INTERRUPTION_FILTER_ALARMS = 4; // 0x4
+    field public static final int INTERRUPTION_FILTER_ALL = 1; // 0x1
+    field public static final int INTERRUPTION_FILTER_NONE = 3; // 0x3
+    field public static final int INTERRUPTION_FILTER_PRIORITY = 2; // 0x2
+    field public static final int INTERRUPTION_FILTER_UNKNOWN = 0; // 0x0
+  }
+
+  @IntDef({androidx.core.app.NotificationManagerCompat.INTERRUPTION_FILTER_UNKNOWN, androidx.core.app.NotificationManagerCompat.INTERRUPTION_FILTER_ALL, androidx.core.app.NotificationManagerCompat.INTERRUPTION_FILTER_PRIORITY, androidx.core.app.NotificationManagerCompat.INTERRUPTION_FILTER_NONE, androidx.core.app.NotificationManagerCompat.INTERRUPTION_FILTER_ALARMS}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NotificationManagerCompat.InterruptionFilter {
+  }
+
+  public static class NotificationManagerCompat.NotificationWithIdAndTag {
+    ctor public NotificationManagerCompat.NotificationWithIdAndTag(int, android.app.Notification);
+    ctor public NotificationManagerCompat.NotificationWithIdAndTag(String?, int, android.app.Notification);
+  }
+
+  public interface OnMultiWindowModeChangedProvider {
+    method public void addOnMultiWindowModeChangedListener(androidx.core.util.Consumer<androidx.core.app.MultiWindowModeChangedInfo!>);
+    method public void removeOnMultiWindowModeChangedListener(androidx.core.util.Consumer<androidx.core.app.MultiWindowModeChangedInfo!>);
+  }
+
+  public interface OnNewIntentProvider {
+    method public void addOnNewIntentListener(androidx.core.util.Consumer<android.content.Intent!>);
+    method public void removeOnNewIntentListener(androidx.core.util.Consumer<android.content.Intent!>);
+  }
+
+  public interface OnPictureInPictureModeChangedProvider {
+    method public void addOnPictureInPictureModeChangedListener(androidx.core.util.Consumer<androidx.core.app.PictureInPictureModeChangedInfo!>);
+    method public void removeOnPictureInPictureModeChangedListener(androidx.core.util.Consumer<androidx.core.app.PictureInPictureModeChangedInfo!>);
+  }
+
+  public final class PendingIntentCompat {
+    method public static android.app.PendingIntent getActivities(android.content.Context, int, android.content.Intent![], int, android.os.Bundle?, boolean);
+    method public static android.app.PendingIntent getActivities(android.content.Context, int, android.content.Intent![], int, boolean);
+    method public static android.app.PendingIntent? getActivity(android.content.Context, int, android.content.Intent, int, android.os.Bundle?, boolean);
+    method public static android.app.PendingIntent? getActivity(android.content.Context, int, android.content.Intent, int, boolean);
+    method public static android.app.PendingIntent? getBroadcast(android.content.Context, int, android.content.Intent, int, boolean);
+    method @RequiresApi(26) public static android.app.PendingIntent getForegroundService(android.content.Context, int, android.content.Intent, int, boolean);
+    method public static android.app.PendingIntent? getService(android.content.Context, int, android.content.Intent, int, boolean);
+    method public static void send(android.app.PendingIntent, android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished?, android.os.Handler?) throws android.app.PendingIntent.CanceledException;
+    method public static void send(android.app.PendingIntent, android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished?, android.os.Handler?, String?, android.os.Bundle?) throws android.app.PendingIntent.CanceledException;
+    method public static void send(android.app.PendingIntent, int, android.app.PendingIntent.OnFinished?, android.os.Handler?) throws android.app.PendingIntent.CanceledException;
+  }
+
+  public class Person {
+    method @RequiresApi(28) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static androidx.core.app.Person fromAndroidPerson(android.app.Person);
+    method public static androidx.core.app.Person fromBundle(android.os.Bundle);
+    method @RequiresApi(22) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static androidx.core.app.Person fromPersistableBundle(android.os.PersistableBundle);
+    method public androidx.core.graphics.drawable.IconCompat? getIcon();
+    method public String? getKey();
+    method public CharSequence? getName();
+    method public String? getUri();
+    method public boolean isBot();
+    method public boolean isImportant();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public String resolveToLegacyUri();
+    method @RequiresApi(28) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.app.Person toAndroidPerson();
+    method public androidx.core.app.Person.Builder toBuilder();
+    method public android.os.Bundle toBundle();
+    method @RequiresApi(22) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.os.PersistableBundle toPersistableBundle();
+  }
+
+  public static class Person.Builder {
+    ctor public Person.Builder();
+    method public androidx.core.app.Person build();
+    method public androidx.core.app.Person.Builder setBot(boolean);
+    method public androidx.core.app.Person.Builder setIcon(androidx.core.graphics.drawable.IconCompat?);
+    method public androidx.core.app.Person.Builder setImportant(boolean);
+    method public androidx.core.app.Person.Builder setKey(String?);
+    method public androidx.core.app.Person.Builder setName(CharSequence?);
+    method public androidx.core.app.Person.Builder setUri(String?);
+  }
+
+  public final class PictureInPictureModeChangedInfo {
+    ctor public PictureInPictureModeChangedInfo(boolean);
+    ctor @RequiresApi(26) public PictureInPictureModeChangedInfo(boolean, android.content.res.Configuration);
+    method @RequiresApi(26) public android.content.res.Configuration getNewConfig();
+    method public boolean isInPictureInPictureMode();
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize(jetifyAs="android.support.v4.app.RemoteActionCompat") public final class RemoteActionCompat implements androidx.versionedparcelable.VersionedParcelable {
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public RemoteActionCompat();
+    ctor public RemoteActionCompat(androidx.core.app.RemoteActionCompat);
+    ctor public RemoteActionCompat(androidx.core.graphics.drawable.IconCompat, CharSequence, CharSequence, android.app.PendingIntent);
+    method @RequiresApi(26) public static androidx.core.app.RemoteActionCompat createFromRemoteAction(android.app.RemoteAction);
+    method public android.app.PendingIntent getActionIntent();
+    method public CharSequence getContentDescription();
+    method public androidx.core.graphics.drawable.IconCompat getIcon();
+    method public CharSequence getTitle();
+    method public boolean isEnabled();
+    method public void setEnabled(boolean);
+    method public void setShouldShowIcon(boolean);
+    method public boolean shouldShowIcon();
+    method @RequiresApi(26) public android.app.RemoteAction toRemoteAction();
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @androidx.versionedparcelable.ParcelField(4) public android.app.PendingIntent mActionIntent;
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @androidx.versionedparcelable.ParcelField(3) public CharSequence mContentDescription;
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @androidx.versionedparcelable.ParcelField(5) public boolean mEnabled;
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @androidx.versionedparcelable.ParcelField(1) public androidx.core.graphics.drawable.IconCompat mIcon;
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @androidx.versionedparcelable.ParcelField(6) public boolean mShouldShowIcon;
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @androidx.versionedparcelable.ParcelField(2) public CharSequence mTitle;
+  }
+
+  public final class RemoteInput {
+    method public static void addDataResultToIntent(androidx.core.app.RemoteInput, android.content.Intent, java.util.Map<java.lang.String!,android.net.Uri!>);
+    method public static void addResultsToIntent(androidx.core.app.RemoteInput![], android.content.Intent, android.os.Bundle);
+    method public boolean getAllowFreeFormInput();
+    method public java.util.Set<java.lang.String!>? getAllowedDataTypes();
+    method public CharSequence![]? getChoices();
+    method public static java.util.Map<java.lang.String!,android.net.Uri!>? getDataResultsFromIntent(android.content.Intent, String);
+    method @androidx.core.app.RemoteInput.EditChoicesBeforeSending public int getEditChoicesBeforeSending();
+    method public android.os.Bundle getExtras();
+    method public CharSequence? getLabel();
+    method public String getResultKey();
+    method public static android.os.Bundle? getResultsFromIntent(android.content.Intent);
+    method @androidx.core.app.RemoteInput.Source public static int getResultsSource(android.content.Intent);
+    method public boolean isDataOnly();
+    method public static void setResultsSource(android.content.Intent, @androidx.core.app.RemoteInput.Source int);
+    field public static final int EDIT_CHOICES_BEFORE_SENDING_AUTO = 0; // 0x0
+    field public static final int EDIT_CHOICES_BEFORE_SENDING_DISABLED = 1; // 0x1
+    field public static final int EDIT_CHOICES_BEFORE_SENDING_ENABLED = 2; // 0x2
+    field public static final String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
+    field public static final String RESULTS_CLIP_LABEL = "android.remoteinput.results";
+    field public static final int SOURCE_CHOICE = 1; // 0x1
+    field public static final int SOURCE_FREE_FORM_INPUT = 0; // 0x0
+  }
+
+  public static final class RemoteInput.Builder {
+    ctor public RemoteInput.Builder(String);
+    method public androidx.core.app.RemoteInput.Builder addExtras(android.os.Bundle);
+    method public androidx.core.app.RemoteInput build();
+    method public android.os.Bundle getExtras();
+    method public androidx.core.app.RemoteInput.Builder setAllowDataType(String, boolean);
+    method public androidx.core.app.RemoteInput.Builder setAllowFreeFormInput(boolean);
+    method public androidx.core.app.RemoteInput.Builder setChoices(CharSequence![]?);
+    method public androidx.core.app.RemoteInput.Builder setEditChoicesBeforeSending(@androidx.core.app.RemoteInput.EditChoicesBeforeSending int);
+    method public androidx.core.app.RemoteInput.Builder setLabel(CharSequence?);
+  }
+
+  @IntDef({androidx.core.app.RemoteInput.EDIT_CHOICES_BEFORE_SENDING_AUTO, androidx.core.app.RemoteInput.EDIT_CHOICES_BEFORE_SENDING_DISABLED, androidx.core.app.RemoteInput.EDIT_CHOICES_BEFORE_SENDING_ENABLED}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface RemoteInput.EditChoicesBeforeSending {
+  }
+
+  @IntDef({androidx.core.app.RemoteInput.SOURCE_FREE_FORM_INPUT, androidx.core.app.RemoteInput.SOURCE_CHOICE}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface RemoteInput.Source {
+  }
+
+  public final class ServiceCompat {
+    method public static void startForeground(android.app.Service, int, android.app.Notification, int);
+    method public static void stopForeground(android.app.Service, @androidx.core.app.ServiceCompat.StopForegroundFlags int);
+    field public static final int START_STICKY = 1; // 0x1
+    field public static final int STOP_FOREGROUND_DETACH = 2; // 0x2
+    field public static final int STOP_FOREGROUND_REMOVE = 1; // 0x1
+  }
+
+  @IntDef(flag=true, value={androidx.core.app.ServiceCompat.STOP_FOREGROUND_REMOVE, androidx.core.app.ServiceCompat.STOP_FOREGROUND_DETACH}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ServiceCompat.StopForegroundFlags {
+  }
+
+  public final class ShareCompat {
+    method @Deprecated public static void configureMenuItem(android.view.Menu, @IdRes int, androidx.core.app.ShareCompat.IntentBuilder);
+    method @Deprecated public static void configureMenuItem(android.view.MenuItem, androidx.core.app.ShareCompat.IntentBuilder);
+    method public static android.content.ComponentName? getCallingActivity(android.app.Activity);
+    method public static String? getCallingPackage(android.app.Activity);
+    field public static final String EXTRA_CALLING_ACTIVITY = "androidx.core.app.EXTRA_CALLING_ACTIVITY";
+    field public static final String EXTRA_CALLING_ACTIVITY_INTEROP = "android.support.v4.app.EXTRA_CALLING_ACTIVITY";
+    field public static final String EXTRA_CALLING_PACKAGE = "androidx.core.app.EXTRA_CALLING_PACKAGE";
+    field public static final String EXTRA_CALLING_PACKAGE_INTEROP = "android.support.v4.app.EXTRA_CALLING_PACKAGE";
+  }
+
+  public static class ShareCompat.IntentBuilder {
+    ctor public ShareCompat.IntentBuilder(android.content.Context);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailBcc(String);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailBcc(String![]);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailCc(String);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailCc(String![]);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailTo(String);
+    method public androidx.core.app.ShareCompat.IntentBuilder addEmailTo(String![]);
+    method public androidx.core.app.ShareCompat.IntentBuilder addStream(android.net.Uri);
+    method public android.content.Intent createChooserIntent();
+    method @Deprecated public static androidx.core.app.ShareCompat.IntentBuilder from(android.app.Activity);
+    method public android.content.Intent getIntent();
+    method public androidx.core.app.ShareCompat.IntentBuilder setChooserTitle(@StringRes int);
+    method public androidx.core.app.ShareCompat.IntentBuilder setChooserTitle(CharSequence?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setEmailBcc(String![]?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setEmailCc(String![]?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setEmailTo(String![]?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setHtmlText(String?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setStream(android.net.Uri?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setSubject(String?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setText(CharSequence?);
+    method public androidx.core.app.ShareCompat.IntentBuilder setType(String?);
+    method public void startChooser();
+  }
+
+  public static class ShareCompat.IntentReader {
+    ctor public ShareCompat.IntentReader(android.app.Activity);
+    ctor public ShareCompat.IntentReader(android.content.Context, android.content.Intent);
+    method @Deprecated public static androidx.core.app.ShareCompat.IntentReader from(android.app.Activity);
+    method public android.content.ComponentName? getCallingActivity();
+    method public android.graphics.drawable.Drawable? getCallingActivityIcon();
+    method public android.graphics.drawable.Drawable? getCallingApplicationIcon();
+    method public CharSequence? getCallingApplicationLabel();
+    method public String? getCallingPackage();
+    method public String![]? getEmailBcc();
+    method public String![]? getEmailCc();
+    method public String![]? getEmailTo();
+    method public String? getHtmlText();
+    method public android.net.Uri? getStream();
+    method public android.net.Uri? getStream(int);
+    method public int getStreamCount();
+    method public String? getSubject();
+    method public CharSequence? getText();
+    method public String? getType();
+    method public boolean isMultipleShare();
+    method public boolean isShareIntent();
+    method public boolean isSingleShare();
+  }
+
+  public abstract class SharedElementCallback {
+    ctor public SharedElementCallback();
+    method public android.os.Parcelable! onCaptureSharedElementSnapshot(android.view.View!, android.graphics.Matrix!, android.graphics.RectF!);
+    method public android.view.View! onCreateSnapshotView(android.content.Context!, android.os.Parcelable!);
+    method public void onMapSharedElements(java.util.List<java.lang.String!>!, java.util.Map<java.lang.String!,android.view.View!>!);
+    method public void onRejectSharedElements(java.util.List<android.view.View!>!);
+    method public void onSharedElementEnd(java.util.List<java.lang.String!>!, java.util.List<android.view.View!>!, java.util.List<android.view.View!>!);
+    method public void onSharedElementStart(java.util.List<java.lang.String!>!, java.util.List<android.view.View!>!, java.util.List<android.view.View!>!);
+    method public void onSharedElementsArrived(java.util.List<java.lang.String!>!, java.util.List<android.view.View!>!, androidx.core.app.SharedElementCallback.OnSharedElementsReadyListener!);
+  }
+
+  public static interface SharedElementCallback.OnSharedElementsReadyListener {
+    method public void onSharedElementsReady();
+  }
+
+  public final class TaskStackBuilder implements java.lang.Iterable<android.content.Intent> {
+    method public androidx.core.app.TaskStackBuilder addNextIntent(android.content.Intent);
+    method public androidx.core.app.TaskStackBuilder addNextIntentWithParentStack(android.content.Intent);
+    method public androidx.core.app.TaskStackBuilder addParentStack(android.app.Activity);
+    method public androidx.core.app.TaskStackBuilder addParentStack(android.content.ComponentName);
+    method public androidx.core.app.TaskStackBuilder addParentStack(Class<?>);
+    method public static androidx.core.app.TaskStackBuilder create(android.content.Context);
+    method public android.content.Intent? editIntentAt(int);
+    method @Deprecated public static androidx.core.app.TaskStackBuilder! from(android.content.Context!);
+    method @Deprecated public android.content.Intent! getIntent(int);
+    method public int getIntentCount();
+    method public android.content.Intent![] getIntents();
+    method public android.app.PendingIntent? getPendingIntent(int, int);
+    method public android.app.PendingIntent? getPendingIntent(int, int, android.os.Bundle?);
+    method @Deprecated public java.util.Iterator<android.content.Intent!> iterator();
+    method public void startActivities();
+    method public void startActivities(android.os.Bundle?);
+  }
+
+  public static interface TaskStackBuilder.SupportParentable {
+    method public android.content.Intent? getSupportParentActivityIntent();
+  }
+
+}
+
+package androidx.core.content {
+
+  public final class ContentProviderCompat {
+    method public static android.content.Context requireContext(android.content.ContentProvider);
+  }
+
+  public final class ContentResolverCompat {
+    method public static android.database.Cursor? query(android.content.ContentResolver, android.net.Uri, String![]?, String?, String![]?, String?, androidx.core.os.CancellationSignal?);
+  }
+
+  public class ContextCompat {
+    ctor protected ContextCompat();
+    method public static int checkSelfPermission(android.content.Context, String);
+    method public static android.content.Context? createDeviceProtectedStorageContext(android.content.Context);
+    method public static String? getAttributionTag(android.content.Context);
+    method public static java.io.File getCodeCacheDir(android.content.Context);
+    method @ColorInt public static int getColor(android.content.Context, @ColorRes int);
+    method public static android.content.res.ColorStateList? getColorStateList(android.content.Context, @ColorRes int);
+    method public static android.content.Context getContextForLanguage(android.content.Context);
+    method public static java.io.File? getDataDir(android.content.Context);
+    method public static android.view.Display getDisplayOrDefault(@DisplayContext android.content.Context);
+    method public static android.graphics.drawable.Drawable? getDrawable(android.content.Context, @DrawableRes int);
+    method public static java.io.File![] getExternalCacheDirs(android.content.Context);
+    method public static java.io.File![] getExternalFilesDirs(android.content.Context, String?);
+    method public static java.util.concurrent.Executor getMainExecutor(android.content.Context);
+    method public static java.io.File? getNoBackupFilesDir(android.content.Context);
+    method public static java.io.File![] getObbDirs(android.content.Context);
+    method public static String getString(android.content.Context, int);
+    method public static <T> T? getSystemService(android.content.Context, Class<T!>);
+    method public static String? getSystemServiceName(android.content.Context, Class<?>);
+    method public static boolean isDeviceProtectedStorage(android.content.Context);
+    method public static android.content.Intent? registerReceiver(android.content.Context, android.content.BroadcastReceiver?, android.content.IntentFilter, int);
+    method public static android.content.Intent? registerReceiver(android.content.Context, android.content.BroadcastReceiver?, android.content.IntentFilter, String?, android.os.Handler?, int);
+    method public static boolean startActivities(android.content.Context, android.content.Intent![]);
+    method public static boolean startActivities(android.content.Context, android.content.Intent![], android.os.Bundle?);
+    method public static void startActivity(android.content.Context, android.content.Intent, android.os.Bundle?);
+    method public static void startForegroundService(android.content.Context, android.content.Intent);
+    field public static final int RECEIVER_EXPORTED = 2; // 0x2
+    field public static final int RECEIVER_NOT_EXPORTED = 4; // 0x4
+    field public static final int RECEIVER_VISIBLE_TO_INSTANT_APPS = 1; // 0x1
+  }
+
+  public class FileProvider extends android.content.ContentProvider {
+    ctor public FileProvider();
+    ctor protected FileProvider(@XmlRes int);
+    method public int delete(android.net.Uri, String?, String![]?);
+    method public String? getType(android.net.Uri);
+    method public static android.net.Uri! getUriForFile(android.content.Context, String, java.io.File);
+    method public static android.net.Uri getUriForFile(android.content.Context, String, java.io.File, String);
+    method public android.net.Uri! insert(android.net.Uri, android.content.ContentValues);
+    method public boolean onCreate();
+    method public android.database.Cursor query(android.net.Uri, String![]?, String?, String![]?, String?);
+    method public int update(android.net.Uri, android.content.ContentValues, String?, String![]?);
+  }
+
+  public final class IntentCompat {
+    method public static android.content.Intent createManageUnusedAppRestrictionsIntent(android.content.Context, String);
+    method public static android.os.Parcelable![]? getParcelableArrayExtra(android.content.Intent, String?, Class<? extends android.os.Parcelable>);
+    method public static <T> java.util.ArrayList<T!>? getParcelableArrayListExtra(android.content.Intent, String?, Class<? extends T>);
+    method public static <T> T? getParcelableExtra(android.content.Intent, String?, Class<T!>);
+    method public static android.content.Intent makeMainSelectorActivity(String, String);
+    field public static final String ACTION_CREATE_REMINDER = "android.intent.action.CREATE_REMINDER";
+    field public static final String CATEGORY_LEANBACK_LAUNCHER = "android.intent.category.LEANBACK_LAUNCHER";
+    field public static final String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
+    field public static final String EXTRA_START_PLAYBACK = "android.intent.extra.START_PLAYBACK";
+    field public static final String EXTRA_TIME = "android.intent.extra.TIME";
+  }
+
+  public class IntentSanitizer {
+    method public android.content.Intent sanitize(android.content.Intent, androidx.core.util.Consumer<java.lang.String!>);
+    method public android.content.Intent sanitizeByFiltering(android.content.Intent);
+    method public android.content.Intent sanitizeByThrowing(android.content.Intent);
+  }
+
+  public static final class IntentSanitizer.Builder {
+    ctor public IntentSanitizer.Builder();
+    method public androidx.core.content.IntentSanitizer.Builder allowAction(androidx.core.util.Predicate<java.lang.String!>);
+    method public androidx.core.content.IntentSanitizer.Builder allowAction(String);
+    method public androidx.core.content.IntentSanitizer.Builder allowAnyComponent();
+    method public androidx.core.content.IntentSanitizer.Builder allowCategory(androidx.core.util.Predicate<java.lang.String!>);
+    method public androidx.core.content.IntentSanitizer.Builder allowCategory(String);
+    method public androidx.core.content.IntentSanitizer.Builder allowClipData(androidx.core.util.Predicate<android.content.ClipData!>);
+    method public androidx.core.content.IntentSanitizer.Builder allowClipDataText();
+    method public androidx.core.content.IntentSanitizer.Builder allowClipDataUri(androidx.core.util.Predicate<android.net.Uri!>);
+    method public androidx.core.content.IntentSanitizer.Builder allowClipDataUriWithAuthority(String);
+    method public androidx.core.content.IntentSanitizer.Builder allowComponent(android.content.ComponentName);
+    method public androidx.core.content.IntentSanitizer.Builder allowComponent(androidx.core.util.Predicate<android.content.ComponentName!>);
+    method public androidx.core.content.IntentSanitizer.Builder allowComponentWithPackage(String);
+    method public androidx.core.content.IntentSanitizer.Builder allowData(androidx.core.util.Predicate<android.net.Uri!>);
+    method public androidx.core.content.IntentSanitizer.Builder allowDataWithAuthority(String);
+    method public androidx.core.content.IntentSanitizer.Builder allowExtra(String, androidx.core.util.Predicate<java.lang.Object!>);
+    method public androidx.core.content.IntentSanitizer.Builder allowExtra(String, Class<?>);
+    method public <T> androidx.core.content.IntentSanitizer.Builder allowExtra(String, Class<T!>, androidx.core.util.Predicate<T!>);
+    method public androidx.core.content.IntentSanitizer.Builder allowExtraOutput(androidx.core.util.Predicate<android.net.Uri!>);
+    method public androidx.core.content.IntentSanitizer.Builder allowExtraOutput(String);
+    method public androidx.core.content.IntentSanitizer.Builder allowExtraStream(androidx.core.util.Predicate<android.net.Uri!>);
+    method public androidx.core.content.IntentSanitizer.Builder allowExtraStreamUriWithAuthority(String);
+    method public androidx.core.content.IntentSanitizer.Builder allowFlags(int);
+    method public androidx.core.content.IntentSanitizer.Builder allowHistoryStackFlags();
+    method public androidx.core.content.IntentSanitizer.Builder allowIdentifier();
+    method public androidx.core.content.IntentSanitizer.Builder allowPackage(androidx.core.util.Predicate<java.lang.String!>);
+    method public androidx.core.content.IntentSanitizer.Builder allowPackage(String);
+    method public androidx.core.content.IntentSanitizer.Builder allowReceiverFlags();
+    method public androidx.core.content.IntentSanitizer.Builder allowSelector();
+    method public androidx.core.content.IntentSanitizer.Builder allowSourceBounds();
+    method public androidx.core.content.IntentSanitizer.Builder allowType(androidx.core.util.Predicate<java.lang.String!>);
+    method public androidx.core.content.IntentSanitizer.Builder allowType(String);
+    method public androidx.core.content.IntentSanitizer build();
+  }
+
+  public final class LocusIdCompat {
+    ctor public LocusIdCompat(String);
+    method public String getId();
+    method @RequiresApi(29) public android.content.LocusId toLocusId();
+    method @RequiresApi(29) public static androidx.core.content.LocusIdCompat toLocusIdCompat(android.content.LocusId);
+  }
+
+  public final class MimeTypeFilter {
+    method public static boolean matches(String?, String);
+    method public static String? matches(String?, String![]);
+    method public static String? matches(String![]?, String);
+    method public static String![] matchesMany(String![]?, String);
+  }
+
+  public interface OnConfigurationChangedProvider {
+    method public void addOnConfigurationChangedListener(androidx.core.util.Consumer<android.content.res.Configuration!>);
+    method public void removeOnConfigurationChangedListener(androidx.core.util.Consumer<android.content.res.Configuration!>);
+  }
+
+  public interface OnTrimMemoryProvider {
+    method public void addOnTrimMemoryListener(androidx.core.util.Consumer<java.lang.Integer!>);
+    method public void removeOnTrimMemoryListener(androidx.core.util.Consumer<java.lang.Integer!>);
+  }
+
+  public final class PackageManagerCompat {
+    method public static com.google.common.util.concurrent.ListenableFuture<java.lang.Integer!> getUnusedAppRestrictionsStatus(android.content.Context);
+    field public static final String ACTION_PERMISSION_REVOCATION_SETTINGS = "android.intent.action.AUTO_REVOKE_PERMISSIONS";
+  }
+
+  public final class PermissionChecker {
+    method @androidx.core.content.PermissionChecker.PermissionResult public static int checkCallingOrSelfPermission(android.content.Context, String);
+    method @androidx.core.content.PermissionChecker.PermissionResult public static int checkCallingPermission(android.content.Context, String, String?);
+    method @androidx.core.content.PermissionChecker.PermissionResult public static int checkPermission(android.content.Context, String, int, int, String?);
+    method @androidx.core.content.PermissionChecker.PermissionResult public static int checkSelfPermission(android.content.Context, String);
+    field public static final int PERMISSION_DENIED = -1; // 0xffffffff
+    field public static final int PERMISSION_DENIED_APP_OP = -2; // 0xfffffffe
+    field public static final int PERMISSION_GRANTED = 0; // 0x0
+  }
+
+  @IntDef({androidx.core.content.PermissionChecker.PERMISSION_GRANTED, androidx.core.content.PermissionChecker.PERMISSION_DENIED, androidx.core.content.PermissionChecker.PERMISSION_DENIED_APP_OP}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PermissionChecker.PermissionResult {
+  }
+
+  @Deprecated public final class SharedPreferencesCompat {
+  }
+
+  @Deprecated public static final class SharedPreferencesCompat.EditorCompat {
+    method @Deprecated public void apply(android.content.SharedPreferences.Editor);
+    method @Deprecated public static androidx.core.content.SharedPreferencesCompat.EditorCompat! getInstance();
+  }
+
+  public class UnusedAppRestrictionsBackportCallback {
+    method public void onResult(boolean, boolean) throws android.os.RemoteException;
+  }
+
+  public abstract class UnusedAppRestrictionsBackportService extends android.app.Service {
+    ctor public UnusedAppRestrictionsBackportService();
+    method protected abstract void isPermissionRevocationEnabled(androidx.core.content.UnusedAppRestrictionsBackportCallback);
+    method public android.os.IBinder? onBind(android.content.Intent?);
+    field public static final String ACTION_UNUSED_APP_RESTRICTIONS_BACKPORT_CONNECTION = "android.support.unusedapprestrictions.action.CustomUnusedAppRestrictionsBackportService";
+  }
+
+  public final class UnusedAppRestrictionsConstants {
+    field public static final int API_30 = 4; // 0x4
+    field public static final int API_30_BACKPORT = 3; // 0x3
+    field public static final int API_31 = 5; // 0x5
+    field public static final int DISABLED = 2; // 0x2
+    field public static final int ERROR = 0; // 0x0
+    field public static final int FEATURE_NOT_AVAILABLE = 1; // 0x1
+  }
+
+  public class UriMatcherCompat {
+    method public static androidx.core.util.Predicate<android.net.Uri!> asPredicate(android.content.UriMatcher);
+  }
+
+}
+
+package androidx.core.content.pm {
+
+  @Deprecated public final class ActivityInfoCompat {
+    field @Deprecated public static final int CONFIG_UI_MODE = 512; // 0x200
+  }
+
+  public final class PackageInfoCompat {
+    method public static long getLongVersionCode(android.content.pm.PackageInfo);
+    method public static java.util.List<android.content.pm.Signature!> getSignatures(android.content.pm.PackageManager, String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public static boolean hasSignatures(android.content.pm.PackageManager, String, @Size(min=1) java.util.Map<byte[]!,java.lang.Integer!>, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
+  }
+
+  public final class PermissionInfoCompat {
+    method public static int getProtection(android.content.pm.PermissionInfo);
+    method public static int getProtectionFlags(android.content.pm.PermissionInfo);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public abstract class ShortcutInfoChangeListener {
+    ctor public ShortcutInfoChangeListener();
+    method @AnyThread public void onAllShortcutsRemoved();
+    method @AnyThread public void onShortcutAdded(java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>);
+    method @AnyThread public void onShortcutRemoved(java.util.List<java.lang.String!>);
+    method @AnyThread public void onShortcutUpdated(java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>);
+    method @AnyThread public void onShortcutUsageReported(java.util.List<java.lang.String!>);
+  }
+
+  public class ShortcutInfoCompat {
+    method public android.content.ComponentName? getActivity();
+    method public java.util.Set<java.lang.String!>? getCategories();
+    method public CharSequence? getDisabledMessage();
+    method public int getDisabledReason();
+    method @androidx.core.content.pm.ShortcutInfoCompat.Surface public int getExcludedFromSurfaces();
+    method public android.os.PersistableBundle? getExtras();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.core.graphics.drawable.IconCompat! getIcon();
+    method public String getId();
+    method public android.content.Intent getIntent();
+    method public android.content.Intent![] getIntents();
+    method public long getLastChangedTimestamp();
+    method public androidx.core.content.LocusIdCompat? getLocusId();
+    method public CharSequence? getLongLabel();
+    method public String getPackage();
+    method public int getRank();
+    method public CharSequence getShortLabel();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.os.Bundle? getTransientExtras();
+    method public android.os.UserHandle? getUserHandle();
+    method public boolean hasKeyFieldsOnly();
+    method public boolean isCached();
+    method public boolean isDeclaredInManifest();
+    method public boolean isDynamic();
+    method public boolean isEnabled();
+    method public boolean isExcludedFromSurfaces(@androidx.core.content.pm.ShortcutInfoCompat.Surface int);
+    method public boolean isImmutable();
+    method public boolean isPinned();
+    method @RequiresApi(25) public android.content.pm.ShortcutInfo! toShortcutInfo();
+    field public static final int SURFACE_LAUNCHER = 1; // 0x1
+  }
+
+  public static class ShortcutInfoCompat.Builder {
+    ctor @RequiresApi(25) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public ShortcutInfoCompat.Builder(android.content.Context, android.content.pm.ShortcutInfo);
+    ctor public ShortcutInfoCompat.Builder(android.content.Context, String);
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public ShortcutInfoCompat.Builder(androidx.core.content.pm.ShortcutInfoCompat);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder addCapabilityBinding(String);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder addCapabilityBinding(String, String, java.util.List<java.lang.String!>);
+    method public androidx.core.content.pm.ShortcutInfoCompat build();
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setActivity(android.content.ComponentName);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setAlwaysBadged();
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setCategories(java.util.Set<java.lang.String!>);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setDisabledMessage(CharSequence);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setExcludedFromSurfaces(int);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setExtras(android.os.PersistableBundle);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIcon(androidx.core.graphics.drawable.IconCompat!);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIntent(android.content.Intent);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIntents(android.content.Intent![]);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIsConversation();
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLocusId(androidx.core.content.LocusIdCompat?);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLabel(CharSequence);
+    method @Deprecated public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLived();
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLived(boolean);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setPerson(androidx.core.app.Person);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setPersons(androidx.core.app.Person![]);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setRank(int);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setShortLabel(CharSequence);
+    method public androidx.core.content.pm.ShortcutInfoCompat.Builder setSliceUri(android.net.Uri);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.core.content.pm.ShortcutInfoCompat.Builder setTransientExtras(android.os.Bundle);
+  }
+
+  @IntDef(flag=true, value={androidx.core.content.pm.ShortcutInfoCompat.SURFACE_LAUNCHER}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ShortcutInfoCompat.Surface {
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public abstract class ShortcutInfoCompatSaver<T> {
+    ctor public ShortcutInfoCompatSaver();
+    method @AnyThread public abstract T! addShortcuts(java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>!);
+    method @WorkerThread public java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>! getShortcuts() throws java.lang.Exception;
+    method @AnyThread public abstract T! removeAllShortcuts();
+    method @AnyThread public abstract T! removeShortcuts(java.util.List<java.lang.String!>!);
+  }
+
+  public class ShortcutManagerCompat {
+    method public static boolean addDynamicShortcuts(android.content.Context, java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>);
+    method public static android.content.Intent createShortcutResultIntent(android.content.Context, androidx.core.content.pm.ShortcutInfoCompat);
+    method public static void disableShortcuts(android.content.Context, java.util.List<java.lang.String!>, CharSequence?);
+    method public static void enableShortcuts(android.content.Context, java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>);
+    method public static java.util.List<androidx.core.content.pm.ShortcutInfoCompat!> getDynamicShortcuts(android.content.Context);
+    method public static int getIconMaxHeight(android.content.Context);
+    method public static int getIconMaxWidth(android.content.Context);
+    method public static int getMaxShortcutCountPerActivity(android.content.Context);
+    method public static java.util.List<androidx.core.content.pm.ShortcutInfoCompat!> getShortcuts(android.content.Context, @androidx.core.content.pm.ShortcutManagerCompat.ShortcutMatchFlags int);
+    method public static boolean isRateLimitingActive(android.content.Context);
+    method public static boolean isRequestPinShortcutSupported(android.content.Context);
+    method public static boolean pushDynamicShortcut(android.content.Context, androidx.core.content.pm.ShortcutInfoCompat);
+    method public static void removeAllDynamicShortcuts(android.content.Context);
+    method public static void removeDynamicShortcuts(android.content.Context, java.util.List<java.lang.String!>);
+    method public static void removeLongLivedShortcuts(android.content.Context, java.util.List<java.lang.String!>);
+    method public static void reportShortcutUsed(android.content.Context, String);
+    method public static boolean requestPinShortcut(android.content.Context, androidx.core.content.pm.ShortcutInfoCompat, android.content.IntentSender?);
+    method public static boolean setDynamicShortcuts(android.content.Context, java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>);
+    method public static boolean updateShortcuts(android.content.Context, java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>);
+    field public static final String EXTRA_SHORTCUT_ID = "android.intent.extra.shortcut.ID";
+    field public static final int FLAG_MATCH_CACHED = 8; // 0x8
+    field public static final int FLAG_MATCH_DYNAMIC = 2; // 0x2
+    field public static final int FLAG_MATCH_MANIFEST = 1; // 0x1
+    field public static final int FLAG_MATCH_PINNED = 4; // 0x4
+  }
+
+  @IntDef(flag=true, value={androidx.core.content.pm.ShortcutManagerCompat.FLAG_MATCH_MANIFEST, androidx.core.content.pm.ShortcutManagerCompat.FLAG_MATCH_DYNAMIC, androidx.core.content.pm.ShortcutManagerCompat.FLAG_MATCH_PINNED, androidx.core.content.pm.ShortcutManagerCompat.FLAG_MATCH_CACHED}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ShortcutManagerCompat.ShortcutMatchFlags {
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public class ShortcutXmlParser {
+    method @WorkerThread public static java.util.List<java.lang.String!> getShortcutIds(android.content.Context);
+    method @VisibleForTesting public static java.util.List<java.lang.String!> parseShortcutIds(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+  }
+
+}
+
+package androidx.core.content.res {
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public class CamColor {
+    method public static void getM3HCTfromColor(@ColorInt int, @Size(3) float[]);
+    method public static int toColor(@FloatRange(from=0.0, to=360.0) float, @FloatRange(from=0.0, to=java.lang.Double.POSITIVE_INFINITY, toInclusive=false) float, @FloatRange(from=0.0, to=100.0) float);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class ColorStateListInflaterCompat {
+    method public static android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static android.content.res.ColorStateList createFromXmlInner(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static android.content.res.ColorStateList? inflate(android.content.res.Resources, @XmlRes int, android.content.res.Resources.Theme?);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class ComplexColorCompat {
+    method @ColorInt public int getColor();
+    method public android.graphics.Shader? getShader();
+    method public static androidx.core.content.res.ComplexColorCompat? inflate(android.content.res.Resources, @ColorRes int, android.content.res.Resources.Theme?);
+    method public boolean isGradient();
+    method public boolean isStateful();
+    method public boolean onStateChanged(int[]!);
+    method public void setColor(@ColorInt int);
+    method public boolean willDraw();
+  }
+
+  public final class ConfigurationHelper {
+    method public static int getDensityDpi(android.content.res.Resources);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class FontResourcesParserCompat {
+    method public static androidx.core.content.res.FontResourcesParserCompat.FamilyResourceEntry? parse(org.xmlpull.v1.XmlPullParser, android.content.res.Resources) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static java.util.List<java.util.List<byte[]!>!> readCerts(android.content.res.Resources, @ArrayRes int);
+    field public static final int FETCH_STRATEGY_ASYNC = 1; // 0x1
+    field public static final int FETCH_STRATEGY_BLOCKING = 0; // 0x0
+    field public static final int INFINITE_TIMEOUT_VALUE = -1; // 0xffffffff
+  }
+
+  public static interface FontResourcesParserCompat.FamilyResourceEntry {
+  }
+
+  @IntDef({androidx.core.content.res.FontResourcesParserCompat.FETCH_STRATEGY_BLOCKING, androidx.core.content.res.FontResourcesParserCompat.FETCH_STRATEGY_ASYNC}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface FontResourcesParserCompat.FetchStrategy {
+  }
+
+  public static final class FontResourcesParserCompat.FontFamilyFilesResourceEntry implements androidx.core.content.res.FontResourcesParserCompat.FamilyResourceEntry {
+    ctor public FontResourcesParserCompat.FontFamilyFilesResourceEntry(androidx.core.content.res.FontResourcesParserCompat.FontFileResourceEntry![]);
+    method public androidx.core.content.res.FontResourcesParserCompat.FontFileResourceEntry![] getEntries();
+  }
+
+  public static final class FontResourcesParserCompat.FontFileResourceEntry {
+    ctor public FontResourcesParserCompat.FontFileResourceEntry(String, int, boolean, String?, int, int);
+    method public String getFileName();
+    method public int getResourceId();
+    method public int getTtcIndex();
+    method public String? getVariationSettings();
+    method public int getWeight();
+    method public boolean isItalic();
+  }
+
+  public static final class FontResourcesParserCompat.ProviderResourceEntry implements androidx.core.content.res.FontResourcesParserCompat.FamilyResourceEntry {
+    ctor public FontResourcesParserCompat.ProviderResourceEntry(androidx.core.provider.FontRequest, @androidx.core.content.res.FontResourcesParserCompat.FetchStrategy int, int);
+    method @androidx.core.content.res.FontResourcesParserCompat.FetchStrategy public int getFetchStrategy();
+    method public androidx.core.provider.FontRequest getRequest();
+    method public int getTimeout();
+  }
+
+  public final class ResourcesCompat {
+    method public static void clearCachesForTheme(android.content.res.Resources.Theme);
+    method public static android.graphics.Typeface? getCachedFont(android.content.Context, @FontRes int) throws android.content.res.Resources.NotFoundException;
+    method @ColorInt public static int getColor(android.content.res.Resources, @ColorRes int, android.content.res.Resources.Theme?) throws android.content.res.Resources.NotFoundException;
+    method public static android.content.res.ColorStateList? getColorStateList(android.content.res.Resources, @ColorRes int, android.content.res.Resources.Theme?) throws android.content.res.Resources.NotFoundException;
+    method public static android.graphics.drawable.Drawable? getDrawable(android.content.res.Resources, @DrawableRes int, android.content.res.Resources.Theme?) throws android.content.res.Resources.NotFoundException;
+    method public static android.graphics.drawable.Drawable? getDrawableForDensity(android.content.res.Resources, @DrawableRes int, int, android.content.res.Resources.Theme?) throws android.content.res.Resources.NotFoundException;
+    method public static float getFloat(android.content.res.Resources, @DimenRes int);
+    method public static android.graphics.Typeface? getFont(android.content.Context, @FontRes int) throws android.content.res.Resources.NotFoundException;
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static android.graphics.Typeface? getFont(android.content.Context, @FontRes int, android.util.TypedValue, int, androidx.core.content.res.ResourcesCompat.FontCallback?) throws android.content.res.Resources.NotFoundException;
+    method public static void getFont(android.content.Context, @FontRes int, androidx.core.content.res.ResourcesCompat.FontCallback, android.os.Handler?) throws android.content.res.Resources.NotFoundException;
+    field @AnyRes public static final int ID_NULL = 0; // 0x0
+  }
+
+  public abstract static class ResourcesCompat.FontCallback {
+    ctor public ResourcesCompat.FontCallback();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final void callbackFailAsync(@androidx.core.provider.FontsContractCompat.FontRequestCallback.FontRequestFailReason int, android.os.Handler?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final void callbackSuccessAsync(android.graphics.Typeface, android.os.Handler?);
+    method public abstract void onFontRetrievalFailed(@androidx.core.provider.FontsContractCompat.FontRequestCallback.FontRequestFailReason int);
+    method public abstract void onFontRetrieved(android.graphics.Typeface);
+  }
+
+  public static final class ResourcesCompat.ThemeCompat {
+    method public static void rebase(android.content.res.Resources.Theme);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class TypedArrayUtils {
+    method public static int getAttr(android.content.Context, int, int);
+    method public static boolean getBoolean(android.content.res.TypedArray, @StyleableRes int, @StyleableRes int, boolean);
+    method public static android.graphics.drawable.Drawable? getDrawable(android.content.res.TypedArray, @StyleableRes int, @StyleableRes int);
+    method public static int getInt(android.content.res.TypedArray, @StyleableRes int, @StyleableRes int, int);
+    method public static boolean getNamedBoolean(android.content.res.TypedArray, org.xmlpull.v1.XmlPullParser, String, @StyleableRes int, boolean);
+    method @ColorInt public static int getNamedColor(android.content.res.TypedArray, org.xmlpull.v1.XmlPullParser, String, @StyleableRes int, @ColorInt int);
+    method public static android.content.res.ColorStateList? getNamedColorStateList(android.content.res.TypedArray, org.xmlpull.v1.XmlPullParser, android.content.res.Resources.Theme?, String, @StyleableRes int);
+    method public static androidx.core.content.res.ComplexColorCompat! getNamedComplexColor(android.content.res.TypedArray, org.xmlpull.v1.XmlPullParser, android.content.res.Resources.Theme?, String, @StyleableRes int, @ColorInt int);
+    method public static float getNamedFloat(android.content.res.TypedArray, org.xmlpull.v1.XmlPullParser, String, @StyleableRes int, float);
+    method public static int getNamedInt(android.content.res.TypedArray, org.xmlpull.v1.XmlPullParser, String, @StyleableRes int, int);
+    method @AnyRes public static int getNamedResourceId(android.content.res.TypedArray, org.xmlpull.v1.XmlPullParser, String, @StyleableRes int, @AnyRes int);
+    method public static String? getNamedString(android.content.res.TypedArray, org.xmlpull.v1.XmlPullParser, String, @StyleableRes int);
+    method @AnyRes public static int getResourceId(android.content.res.TypedArray, @StyleableRes int, @StyleableRes int, @AnyRes int);
+    method public static String? getString(android.content.res.TypedArray, @StyleableRes int, @StyleableRes int);
+    method public static CharSequence? getText(android.content.res.TypedArray, @StyleableRes int, @StyleableRes int);
+    method public static CharSequence![]? getTextArray(android.content.res.TypedArray, @StyleableRes int, @StyleableRes int);
+    method public static boolean hasAttribute(org.xmlpull.v1.XmlPullParser, String);
+    method public static android.content.res.TypedArray obtainAttributes(android.content.res.Resources, android.content.res.Resources.Theme?, android.util.AttributeSet, int[]);
+    method public static android.util.TypedValue? peekNamedValue(android.content.res.TypedArray, org.xmlpull.v1.XmlPullParser, String, int);
+  }
+
+}
+
+package androidx.core.database {
+
+  public final class CursorWindowCompat {
+    method public static android.database.CursorWindow create(String?, long);
+  }
+
+  @Deprecated public final class DatabaseUtilsCompat {
+    method @Deprecated public static String![]! appendSelectionArgs(String![]!, String![]!);
+    method @Deprecated public static String! concatenateWhere(String!, String!);
+  }
+
+}
+
+package androidx.core.database.sqlite {
+
+  public final class SQLiteCursorCompat {
+    method public static void setFillWindowForwardOnly(android.database.sqlite.SQLiteCursor, boolean);
+  }
+
+}
+
+package androidx.core.graphics {
+
+  public final class BitmapCompat {
+    method public static android.graphics.Bitmap createScaledBitmap(android.graphics.Bitmap, int, int, android.graphics.Rect?, boolean);
+    method public static int getAllocationByteCount(android.graphics.Bitmap);
+    method public static boolean hasMipMap(android.graphics.Bitmap);
+    method public static void setHasMipMap(android.graphics.Bitmap, boolean);
+  }
+
+  public class BlendModeColorFilterCompat {
+    method public static android.graphics.ColorFilter? createBlendModeColorFilterCompat(int, androidx.core.graphics.BlendModeCompat);
+  }
+
+  public enum BlendModeCompat {
+    enum_constant public static final androidx.core.graphics.BlendModeCompat CLEAR;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat COLOR;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat COLOR_BURN;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat COLOR_DODGE;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DARKEN;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat DIFFERENCE;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DST;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DST_ATOP;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DST_IN;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DST_OUT;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat DST_OVER;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat EXCLUSION;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat HARD_LIGHT;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat HUE;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat LIGHTEN;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat LUMINOSITY;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat MODULATE;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat MULTIPLY;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat OVERLAY;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat PLUS;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat SATURATION;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SCREEN;
+    enum_constant @RequiresApi(android.os.Build.VERSION_CODES.Q) public static final androidx.core.graphics.BlendModeCompat SOFT_LIGHT;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SRC;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SRC_ATOP;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SRC_IN;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SRC_OUT;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat SRC_OVER;
+    enum_constant public static final androidx.core.graphics.BlendModeCompat XOR;
+  }
+
+  public final class ColorUtils {
+    method @ColorInt public static int HSLToColor(float[]);
+    method @ColorInt public static int LABToColor(@FloatRange(from=0.0f, to=100) double, @FloatRange(from=0xffffff80, to=127) double, @FloatRange(from=0xffffff80, to=127) double);
+    method public static void LABToXYZ(@FloatRange(from=0.0f, to=100) double, @FloatRange(from=0xffffff80, to=127) double, @FloatRange(from=0xffffff80, to=127) double, double[]);
+    method @ColorInt public static int M3HCTToColor(@FloatRange(from=0.0, to=360, toInclusive=false) float, @FloatRange(from=0.0, to=java.lang.Double.POSITIVE_INFINITY, toInclusive=false) float, @FloatRange(from=0.0, to=100) float);
+    method public static void RGBToHSL(@IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, float[]);
+    method public static void RGBToLAB(@IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, double[]);
+    method public static void RGBToXYZ(@IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, @IntRange(from=0, to=255) int, double[]);
+    method @ColorInt public static int XYZToColor(@FloatRange(from=0.0f, to=95.047) double, @FloatRange(from=0.0f, to=0x64) double, @FloatRange(from=0.0f, to=108.883) double);
+    method public static void XYZToLAB(@FloatRange(from=0.0f, to=95.047) double, @FloatRange(from=0.0f, to=0x64) double, @FloatRange(from=0.0f, to=108.883) double, double[]);
+    method @ColorInt public static int blendARGB(@ColorInt int, @ColorInt int, @FloatRange(from=0.0, to=1.0) float);
+    method public static void blendHSL(float[], float[], @FloatRange(from=0.0, to=1.0) float, float[]);
+    method public static void blendLAB(double[], double[], @FloatRange(from=0.0, to=1.0) double, double[]);
+    method public static double calculateContrast(@ColorInt int, @ColorInt int);
+    method @FloatRange(from=0.0, to=1.0) public static double calculateLuminance(@ColorInt int);
+    method public static int calculateMinimumAlpha(@ColorInt int, @ColorInt int, float);
+    method public static void colorToHSL(@ColorInt int, float[]);
+    method public static void colorToLAB(@ColorInt int, double[]);
+    method public static void colorToM3HCT(@ColorInt int, @Size(3) float[]);
+    method public static void colorToXYZ(@ColorInt int, double[]);
+    method @RequiresApi(26) public static android.graphics.Color compositeColors(android.graphics.Color, android.graphics.Color);
+    method public static int compositeColors(@ColorInt int, @ColorInt int);
+    method public static double distanceEuclidean(double[], double[]);
+    method @ColorInt public static int setAlphaComponent(@ColorInt int, @IntRange(from=0, to=255) int);
+  }
+
+  public final class Insets {
+    method public static androidx.core.graphics.Insets add(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+    method public static androidx.core.graphics.Insets max(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+    method public static androidx.core.graphics.Insets min(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+    method public static androidx.core.graphics.Insets of(android.graphics.Rect);
+    method public static androidx.core.graphics.Insets of(int, int, int, int);
+    method public static androidx.core.graphics.Insets subtract(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+    method @RequiresApi(api=29) public static androidx.core.graphics.Insets toCompatInsets(android.graphics.Insets);
+    method @RequiresApi(29) public android.graphics.Insets toPlatformInsets();
+    method @Deprecated @RequiresApi(api=29) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static androidx.core.graphics.Insets wrap(android.graphics.Insets);
+    field public static final androidx.core.graphics.Insets NONE;
+    field public final int bottom;
+    field public final int left;
+    field public final int right;
+    field public final int top;
+  }
+
+  public final class PaintCompat {
+    method public static boolean hasGlyph(android.graphics.Paint, String);
+    method public static boolean setBlendMode(android.graphics.Paint, androidx.core.graphics.BlendModeCompat?);
+  }
+
+  public final class PathSegment {
+    ctor public PathSegment(android.graphics.PointF, float, android.graphics.PointF, float);
+    method public android.graphics.PointF getEnd();
+    method public float getEndFraction();
+    method public android.graphics.PointF getStart();
+    method public float getStartFraction();
+  }
+
+  public final class PathUtils {
+    method @RequiresApi(26) public static java.util.Collection<androidx.core.graphics.PathSegment!> flatten(android.graphics.Path);
+    method @RequiresApi(26) public static java.util.Collection<androidx.core.graphics.PathSegment!> flatten(android.graphics.Path, @FloatRange(from=0) float);
+  }
+
+  public class TypefaceCompat {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @VisibleForTesting public static void clearCache();
+    method public static android.graphics.Typeface create(android.content.Context, android.graphics.Typeface?, int);
+    method public static android.graphics.Typeface create(android.content.Context, android.graphics.Typeface?, @IntRange(from=1, to=1000) int, boolean);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static android.graphics.Typeface? createFromFontInfo(android.content.Context, android.os.CancellationSignal?, androidx.core.provider.FontsContractCompat.FontInfo![], int);
+    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static android.graphics.Typeface? createFromResourcesFamilyXml(android.content.Context, androidx.core.content.res.FontResourcesParserCompat.FamilyResourceEntry, android.content.res.Resources, int, int, androidx.core.content.res.ResourcesCompat.FontCallback?, android.os.Handler?, boolean);
+    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static android.graphics.Typeface? createFromResourcesFontFile(android.content.Context, android.content.res.Resources, int, String!, int);
+    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static android.graphics.Typeface? findFromCache(android.content.res.Resources, int, int);
+  }
+
+  @RequiresApi(26) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class TypefaceCompatApi26Impl {
+    ctor public TypefaceCompatApi26Impl();
+    method protected android.graphics.Typeface? createFromFamiliesWithDefault(Object!);
+    method public android.graphics.Typeface? createFromFontFamilyFilesResourceEntry(android.content.Context!, androidx.core.content.res.FontResourcesParserCompat.FontFamilyFilesResourceEntry!, android.content.res.Resources!, int);
+    method public android.graphics.Typeface? createFromFontInfo(android.content.Context!, android.os.CancellationSignal?, androidx.core.provider.FontsContractCompat.FontInfo![], int);
+    method public android.graphics.Typeface? createFromResourcesFontFile(android.content.Context!, android.content.res.Resources!, int, String!, int);
+    method protected java.lang.reflect.Method! obtainAbortCreationMethod(Class<?>!) throws java.lang.NoSuchMethodException;
+    method protected java.lang.reflect.Method! obtainAddFontFromAssetManagerMethod(Class<?>!) throws java.lang.NoSuchMethodException;
+    method protected java.lang.reflect.Method! obtainAddFontFromBufferMethod(Class<?>!) throws java.lang.NoSuchMethodException;
+    method protected java.lang.reflect.Method! obtainCreateFromFamiliesWithDefaultMethod(Class<?>!) throws java.lang.NoSuchMethodException;
+    method protected Class<?>! obtainFontFamily() throws java.lang.ClassNotFoundException;
+    method protected java.lang.reflect.Constructor<?>! obtainFontFamilyCtor(Class<?>!) throws java.lang.NoSuchMethodException;
+    method protected java.lang.reflect.Method! obtainFreezeMethod(Class<?>!) throws java.lang.NoSuchMethodException;
+    field protected final java.lang.reflect.Method! mAbortCreation;
+    field protected final java.lang.reflect.Method! mAddFontFromAssetManager;
+    field protected final java.lang.reflect.Method! mAddFontFromBuffer;
+    field protected final java.lang.reflect.Method! mCreateFromFamiliesWithDefault;
+    field protected final Class<?>! mFontFamily;
+    field protected final java.lang.reflect.Constructor<?>! mFontFamilyCtor;
+    field protected final java.lang.reflect.Method! mFreeze;
+  }
+
+  @RequiresApi(28) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class TypefaceCompatApi28Impl extends androidx.core.graphics.TypefaceCompatApi26Impl {
+    ctor public TypefaceCompatApi28Impl();
+  }
+
+  @RequiresApi(29) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public class TypefaceCompatApi29Impl {
+    ctor public TypefaceCompatApi29Impl();
+    method public android.graphics.Typeface? createFromFontFamilyFilesResourceEntry(android.content.Context!, androidx.core.content.res.FontResourcesParserCompat.FontFamilyFilesResourceEntry!, android.content.res.Resources!, int);
+    method public android.graphics.Typeface? createFromFontInfo(android.content.Context!, android.os.CancellationSignal?, androidx.core.provider.FontsContractCompat.FontInfo![], int);
+    method protected android.graphics.Typeface! createFromInputStream(android.content.Context!, java.io.InputStream!);
+    method public android.graphics.Typeface? createFromResourcesFontFile(android.content.Context!, android.content.res.Resources!, int, String!, int);
+    method protected androidx.core.provider.FontsContractCompat.FontInfo! findBestInfo(androidx.core.provider.FontsContractCompat.FontInfo![]!, int);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class TypefaceCompatUtil {
+    method public static void closeQuietly(java.io.Closeable?);
+    method @RequiresApi(19) public static java.nio.ByteBuffer? copyToDirectBuffer(android.content.Context, android.content.res.Resources, int);
+    method public static boolean copyToFile(java.io.File, android.content.res.Resources, int);
+    method public static boolean copyToFile(java.io.File, java.io.InputStream);
+    method public static java.io.File? getTempFile(android.content.Context);
+    method @RequiresApi(19) public static java.nio.ByteBuffer? mmap(android.content.Context, android.os.CancellationSignal?, android.net.Uri);
+  }
+
+}
+
+package androidx.core.graphics.drawable {
+
+  public final class DrawableCompat {
+    method public static void applyTheme(android.graphics.drawable.Drawable, android.content.res.Resources.Theme);
+    method public static boolean canApplyTheme(android.graphics.drawable.Drawable);
+    method public static void clearColorFilter(android.graphics.drawable.Drawable);
+    method public static int getAlpha(android.graphics.drawable.Drawable);
+    method public static android.graphics.ColorFilter? getColorFilter(android.graphics.drawable.Drawable);
+    method public static int getLayoutDirection(android.graphics.drawable.Drawable);
+    method public static void inflate(android.graphics.drawable.Drawable, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme?) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static boolean isAutoMirrored(android.graphics.drawable.Drawable);
+    method @Deprecated public static void jumpToCurrentState(android.graphics.drawable.Drawable);
+    method public static void setAutoMirrored(android.graphics.drawable.Drawable, boolean);
+    method public static void setHotspot(android.graphics.drawable.Drawable, float, float);
+    method public static void setHotspotBounds(android.graphics.drawable.Drawable, int, int, int, int);
+    method public static boolean setLayoutDirection(android.graphics.drawable.Drawable, int);
+    method public static void setTint(android.graphics.drawable.Drawable, @ColorInt int);
+    method public static void setTintList(android.graphics.drawable.Drawable, android.content.res.ColorStateList?);
+    method public static void setTintMode(android.graphics.drawable.Drawable, android.graphics.PorterDuff.Mode?);
+    method public static <T extends android.graphics.drawable.Drawable> T! unwrap(android.graphics.drawable.Drawable);
+    method public static android.graphics.drawable.Drawable wrap(android.graphics.drawable.Drawable);
+  }
+
+  @androidx.versionedparcelable.VersionedParcelize(allowSerialization=true, ignoreParcelables=true, isCustom=true, jetifyAs="android.support.v4.graphics.drawable.IconCompat") public class IconCompat extends androidx.versionedparcelable.CustomVersionedParcelable {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void addToShortcutIntent(android.content.Intent, android.graphics.drawable.Drawable?, android.content.Context);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void checkResource(android.content.Context);
+    method public static androidx.core.graphics.drawable.IconCompat? createFromBundle(android.os.Bundle);
+    method @RequiresApi(23) public static androidx.core.graphics.drawable.IconCompat? createFromIcon(android.content.Context, android.graphics.drawable.Icon);
+    method @RequiresApi(23) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static androidx.core.graphics.drawable.IconCompat? createFromIcon(android.graphics.drawable.Icon);
+    method @RequiresApi(23) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static androidx.core.graphics.drawable.IconCompat? createFromIconOrNullIfZeroResId(android.graphics.drawable.Icon);
+    method public static androidx.core.graphics.drawable.IconCompat createWithAdaptiveBitmap(android.graphics.Bitmap);
+    method public static androidx.core.graphics.drawable.IconCompat createWithAdaptiveBitmapContentUri(android.net.Uri);
+    method public static androidx.core.graphics.drawable.IconCompat createWithAdaptiveBitmapContentUri(String);
+    method public static androidx.core.graphics.drawable.IconCompat createWithBitmap(android.graphics.Bitmap);
+    method public static androidx.core.graphics.drawable.IconCompat createWithContentUri(android.net.Uri);
+    method public static androidx.core.graphics.drawable.IconCompat createWithContentUri(String);
+    method public static androidx.core.graphics.drawable.IconCompat createWithData(byte[], int, int);
+    method public static androidx.core.graphics.drawable.IconCompat createWithResource(android.content.Context, @DrawableRes int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static androidx.core.graphics.drawable.IconCompat createWithResource(android.content.res.Resources?, String, @DrawableRes int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.graphics.Bitmap? getBitmap();
+    method @DrawableRes public int getResId();
+    method public String getResPackage();
+    method public int getType();
+    method public android.net.Uri getUri();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public java.io.InputStream? getUriInputStream(android.content.Context);
+    method public android.graphics.drawable.Drawable? loadDrawable(android.content.Context);
+    method public androidx.core.graphics.drawable.IconCompat setTint(@ColorInt int);
+    method public androidx.core.graphics.drawable.IconCompat setTintList(android.content.res.ColorStateList?);
+    method public androidx.core.graphics.drawable.IconCompat setTintMode(android.graphics.PorterDuff.Mode?);
+    method public android.os.Bundle toBundle();
+    method @Deprecated @RequiresApi(23) public android.graphics.drawable.Icon toIcon();
+    method @RequiresApi(23) public android.graphics.drawable.Icon toIcon(android.content.Context?);
+    field public static final int TYPE_ADAPTIVE_BITMAP = 5; // 0x5
+    field public static final int TYPE_BITMAP = 1; // 0x1
+    field public static final int TYPE_DATA = 3; // 0x3
+    field public static final int TYPE_RESOURCE = 2; // 0x2
+    field public static final int TYPE_UNKNOWN = -1; // 0xffffffff
+    field public static final int TYPE_URI = 4; // 0x4
+    field public static final int TYPE_URI_ADAPTIVE_BITMAP = 6; // 0x6
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @androidx.versionedparcelable.ParcelField(value=1, defaultValue="androidx.core.graphics.drawable.IconCompat.TYPE_UNKNOWN") public int mType;
+  }
+
+  public abstract class RoundedBitmapDrawable extends android.graphics.drawable.Drawable {
+    method public void draw(android.graphics.Canvas);
+    method public final android.graphics.Bitmap? getBitmap();
+    method public float getCornerRadius();
+    method public int getGravity();
+    method public int getOpacity();
+    method public final android.graphics.Paint getPaint();
+    method public boolean hasAntiAlias();
+    method public boolean hasMipMap();
+    method public boolean isCircular();
+    method public void setAlpha(int);
+    method public void setAntiAlias(boolean);
+    method public void setCircular(boolean);
+    method public void setColorFilter(android.graphics.ColorFilter!);
+    method public void setCornerRadius(float);
+    method public void setDither(boolean);
+    method public void setGravity(int);
+    method public void setMipMap(boolean);
+    method public void setTargetDensity(android.graphics.Canvas);
+    method public void setTargetDensity(android.util.DisplayMetrics);
+    method public void setTargetDensity(int);
+  }
+
+  public final class RoundedBitmapDrawableFactory {
+    method public static androidx.core.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, android.graphics.Bitmap?);
+    method public static androidx.core.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, java.io.InputStream);
+    method public static androidx.core.graphics.drawable.RoundedBitmapDrawable create(android.content.res.Resources, String);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface TintAwareDrawable {
+    method public void setTint(@ColorInt int);
+    method public void setTintList(android.content.res.ColorStateList!);
+    method public void setTintMode(android.graphics.PorterDuff.Mode!);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface WrappedDrawable {
+    method public android.graphics.drawable.Drawable! getWrappedDrawable();
+    method public void setWrappedDrawable(android.graphics.drawable.Drawable!);
+  }
+
+}
+
+package androidx.core.hardware.display {
+
+  public final class DisplayManagerCompat {
+    method public android.view.Display? getDisplay(int);
+    method public android.view.Display![] getDisplays();
+    method public android.view.Display![] getDisplays(String?);
+    method public static androidx.core.hardware.display.DisplayManagerCompat getInstance(android.content.Context);
+    field public static final String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
+  }
+
+}
+
+package androidx.core.hardware.fingerprint {
+
+  @Deprecated public class FingerprintManagerCompat {
+    method @Deprecated @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) public void authenticate(androidx.core.hardware.fingerprint.FingerprintManagerCompat.CryptoObject?, int, androidx.core.os.CancellationSignal?, androidx.core.hardware.fingerprint.FingerprintManagerCompat.AuthenticationCallback, android.os.Handler?);
+    method @Deprecated public static androidx.core.hardware.fingerprint.FingerprintManagerCompat from(android.content.Context);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) public boolean hasEnrolledFingerprints();
+    method @Deprecated @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) public boolean isHardwareDetected();
+  }
+
+  @Deprecated public abstract static class FingerprintManagerCompat.AuthenticationCallback {
+    ctor @Deprecated public FingerprintManagerCompat.AuthenticationCallback();
+    method @Deprecated public void onAuthenticationError(int, CharSequence!);
+    method @Deprecated public void onAuthenticationFailed();
+    method @Deprecated public void onAuthenticationHelp(int, CharSequence!);
+    method @Deprecated public void onAuthenticationSucceeded(androidx.core.hardware.fingerprint.FingerprintManagerCompat.AuthenticationResult!);
+  }
+
+  @Deprecated public static final class FingerprintManagerCompat.AuthenticationResult {
+    ctor @Deprecated public FingerprintManagerCompat.AuthenticationResult(androidx.core.hardware.fingerprint.FingerprintManagerCompat.CryptoObject!);
+    method @Deprecated public androidx.core.hardware.fingerprint.FingerprintManagerCompat.CryptoObject! getCryptoObject();
+  }
+
+  @Deprecated public static class FingerprintManagerCompat.CryptoObject {
+    ctor @Deprecated public FingerprintManagerCompat.CryptoObject(java.security.Signature);
+    ctor @Deprecated public FingerprintManagerCompat.CryptoObject(javax.crypto.Cipher);
+    ctor @Deprecated public FingerprintManagerCompat.CryptoObject(javax.crypto.Mac);
+    method @Deprecated public javax.crypto.Cipher? getCipher();
+    method @Deprecated public javax.crypto.Mac? getMac();
+    method @Deprecated public java.security.Signature? getSignature();
+  }
+
+}
+
+package androidx.core.internal.view {
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface SupportMenu extends android.view.Menu {
+    method public void setGroupDividerEnabled(boolean);
+    field public static final int CATEGORY_MASK = -65536; // 0xffff0000
+    field public static final int CATEGORY_SHIFT = 16; // 0x10
+    field public static final int FLAG_KEEP_OPEN_ON_SUBMENU_OPENED = 4; // 0x4
+    field public static final int SUPPORTED_MODIFIERS_MASK = 69647; // 0x1100f
+    field public static final int USER_MASK = 65535; // 0xffff
+    field public static final int USER_SHIFT = 0; // 0x0
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface SupportMenuItem extends android.view.MenuItem {
+    method public int getAlphabeticModifiers();
+    method public CharSequence? getContentDescription();
+    method public android.content.res.ColorStateList? getIconTintList();
+    method public android.graphics.PorterDuff.Mode? getIconTintMode();
+    method public int getNumericModifiers();
+    method public androidx.core.view.ActionProvider? getSupportActionProvider();
+    method public CharSequence? getTooltipText();
+    method public boolean requiresActionButton();
+    method public boolean requiresOverflow();
+    method public android.view.MenuItem setAlphabeticShortcut(char, int);
+    method public androidx.core.internal.view.SupportMenuItem setContentDescription(CharSequence?);
+    method public android.view.MenuItem setIconTintList(android.content.res.ColorStateList?);
+    method public android.view.MenuItem setIconTintMode(android.graphics.PorterDuff.Mode?);
+    method public android.view.MenuItem setNumericShortcut(char, int);
+    method public android.view.MenuItem setShortcut(char, char, int, int);
+    method public androidx.core.internal.view.SupportMenuItem setSupportActionProvider(androidx.core.view.ActionProvider?);
+    method public androidx.core.internal.view.SupportMenuItem setTooltipText(CharSequence?);
+    field public static final int SHOW_AS_ACTION_ALWAYS = 2; // 0x2
+    field public static final int SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = 8; // 0x8
+    field public static final int SHOW_AS_ACTION_IF_ROOM = 1; // 0x1
+    field public static final int SHOW_AS_ACTION_NEVER = 0; // 0x0
+    field public static final int SHOW_AS_ACTION_WITH_TEXT = 4; // 0x4
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface SupportSubMenu extends androidx.core.internal.view.SupportMenu android.view.SubMenu {
+  }
+
+}
+
+package androidx.core.location {
+
+  public abstract class GnssStatusCompat {
+    method @FloatRange(from=0, to=360) public abstract float getAzimuthDegrees(@IntRange(from=0) int);
+    method @FloatRange(from=0, to=63) public abstract float getBasebandCn0DbHz(@IntRange(from=0) int);
+    method @FloatRange(from=0) public abstract float getCarrierFrequencyHz(@IntRange(from=0) int);
+    method @FloatRange(from=0, to=63) public abstract float getCn0DbHz(@IntRange(from=0) int);
+    method public abstract int getConstellationType(@IntRange(from=0) int);
+    method @FloatRange(from=0xffffffa6, to=90) public abstract float getElevationDegrees(@IntRange(from=0) int);
+    method @IntRange(from=0) public abstract int getSatelliteCount();
+    method @IntRange(from=1, to=200) public abstract int getSvid(@IntRange(from=0) int);
+    method public abstract boolean hasAlmanacData(@IntRange(from=0) int);
+    method public abstract boolean hasBasebandCn0DbHz(@IntRange(from=0) int);
+    method public abstract boolean hasCarrierFrequencyHz(@IntRange(from=0) int);
+    method public abstract boolean hasEphemerisData(@IntRange(from=0) int);
+    method public abstract boolean usedInFix(@IntRange(from=0) int);
+    method @RequiresApi(android.os.Build.VERSION_CODES.N) public static androidx.core.location.GnssStatusCompat wrap(android.location.GnssStatus);
+    method public static androidx.core.location.GnssStatusCompat wrap(android.location.GpsStatus);
+    field public static final int CONSTELLATION_BEIDOU = 5; // 0x5
+    field public static final int CONSTELLATION_GALILEO = 6; // 0x6
+    field public static final int CONSTELLATION_GLONASS = 3; // 0x3
+    field public static final int CONSTELLATION_GPS = 1; // 0x1
+    field public static final int CONSTELLATION_IRNSS = 7; // 0x7
+    field public static final int CONSTELLATION_QZSS = 4; // 0x4
+    field public static final int CONSTELLATION_SBAS = 2; // 0x2
+    field public static final int CONSTELLATION_UNKNOWN = 0; // 0x0
+  }
+
+  public abstract static class GnssStatusCompat.Callback {
+    ctor public GnssStatusCompat.Callback();
+    method public void onFirstFix(@IntRange(from=0) int);
+    method public void onSatelliteStatusChanged(androidx.core.location.GnssStatusCompat);
+    method public void onStarted();
+    method public void onStopped();
+  }
+
+  public final class LocationCompat {
+    method public static float getBearingAccuracyDegrees(android.location.Location);
+    method public static long getElapsedRealtimeMillis(android.location.Location);
+    method public static long getElapsedRealtimeNanos(android.location.Location);
+    method @FloatRange(from=0.0) public static float getMslAltitudeAccuracyMeters(android.location.Location);
+    method public static double getMslAltitudeMeters(android.location.Location);
+    method public static float getSpeedAccuracyMetersPerSecond(android.location.Location);
+    method public static float getVerticalAccuracyMeters(android.location.Location);
+    method public static boolean hasBearingAccuracy(android.location.Location);
+    method public static boolean hasMslAltitude(android.location.Location);
+    method public static boolean hasMslAltitudeAccuracy(android.location.Location);
+    method public static boolean hasSpeedAccuracy(android.location.Location);
+    method public static boolean hasVerticalAccuracy(android.location.Location);
+    method public static boolean isMock(android.location.Location);
+    method public static void removeBearingAccuracy(android.location.Location);
+    method public static void removeMslAltitude(android.location.Location);
+    method public static void removeMslAltitudeAccuracy(android.location.Location);
+    method public static void removeSpeedAccuracy(android.location.Location);
+    method public static void removeVerticalAccuracy(android.location.Location);
+    method public static void setBearingAccuracyDegrees(android.location.Location, float);
+    method public static void setMock(android.location.Location, boolean);
+    method public static void setMslAltitudeAccuracyMeters(android.location.Location, @FloatRange(from=0.0) float);
+    method public static void setMslAltitudeMeters(android.location.Location, double);
+    method public static void setSpeedAccuracyMetersPerSecond(android.location.Location, float);
+    method public static void setVerticalAccuracyMeters(android.location.Location, float);
+    field public static final String EXTRA_BEARING_ACCURACY = "bearingAccuracy";
+    field public static final String EXTRA_IS_MOCK = "mockLocation";
+    field public static final String EXTRA_MSL_ALTITUDE = "androidx.core.location.extra.MSL_ALTITUDE";
+    field public static final String EXTRA_MSL_ALTITUDE_ACCURACY = "androidx.core.location.extra.MSL_ALTITUDE_ACCURACY";
+    field public static final String EXTRA_SPEED_ACCURACY = "speedAccuracy";
+    field public static final String EXTRA_VERTICAL_ACCURACY = "verticalAccuracy";
+  }
+
+  public interface LocationListenerCompat extends android.location.LocationListener {
+    method public default void onStatusChanged(String, int, android.os.Bundle?);
+  }
+
+  public final class LocationManagerCompat {
+    method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static void getCurrentLocation(android.location.LocationManager, String, androidx.core.os.CancellationSignal?, java.util.concurrent.Executor, androidx.core.util.Consumer<android.location.Location!>);
+    method public static String? getGnssHardwareModelName(android.location.LocationManager);
+    method public static int getGnssYearOfHardware(android.location.LocationManager);
+    method public static boolean hasProvider(android.location.LocationManager, String);
+    method public static boolean isLocationEnabled(android.location.LocationManager);
+    method @RequiresApi(android.os.Build.VERSION_CODES.N) @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static boolean registerGnssMeasurementsCallback(android.location.LocationManager, android.location.GnssMeasurementsEvent.Callback, android.os.Handler);
+    method @RequiresApi(android.os.Build.VERSION_CODES.N) @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static boolean registerGnssMeasurementsCallback(android.location.LocationManager, java.util.concurrent.Executor, android.location.GnssMeasurementsEvent.Callback);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static boolean registerGnssStatusCallback(android.location.LocationManager, androidx.core.location.GnssStatusCompat.Callback, android.os.Handler);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static boolean registerGnssStatusCallback(android.location.LocationManager, java.util.concurrent.Executor, androidx.core.location.GnssStatusCompat.Callback);
+    method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static void removeUpdates(android.location.LocationManager, androidx.core.location.LocationListenerCompat);
+    method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static void requestLocationUpdates(android.location.LocationManager, String, androidx.core.location.LocationRequestCompat, androidx.core.location.LocationListenerCompat, android.os.Looper);
+    method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static void requestLocationUpdates(android.location.LocationManager, String, androidx.core.location.LocationRequestCompat, java.util.concurrent.Executor, androidx.core.location.LocationListenerCompat);
+    method @RequiresApi(android.os.Build.VERSION_CODES.N) public static void unregisterGnssMeasurementsCallback(android.location.LocationManager, android.location.GnssMeasurementsEvent.Callback);
+    method public static void unregisterGnssStatusCallback(android.location.LocationManager, androidx.core.location.GnssStatusCompat.Callback);
+  }
+
+  public final class LocationRequestCompat {
+    method @IntRange(from=1) public long getDurationMillis();
+    method @IntRange(from=0) public long getIntervalMillis();
+    method @IntRange(from=0) public long getMaxUpdateDelayMillis();
+    method @IntRange(from=1, to=java.lang.Integer.MAX_VALUE) public int getMaxUpdates();
+    method @FloatRange(from=0, to=java.lang.Float.MAX_VALUE) public float getMinUpdateDistanceMeters();
+    method @IntRange(from=0) public long getMinUpdateIntervalMillis();
+    method public int getQuality();
+    method @RequiresApi(31) public android.location.LocationRequest toLocationRequest();
+    method @RequiresApi(19) public android.location.LocationRequest? toLocationRequest(String);
+    field public static final long PASSIVE_INTERVAL = 9223372036854775807L; // 0x7fffffffffffffffL
+    field public static final int QUALITY_BALANCED_POWER_ACCURACY = 102; // 0x66
+    field public static final int QUALITY_HIGH_ACCURACY = 100; // 0x64
+    field public static final int QUALITY_LOW_POWER = 104; // 0x68
+  }
+
+  public static final class LocationRequestCompat.Builder {
+    ctor public LocationRequestCompat.Builder(androidx.core.location.LocationRequestCompat);
+    ctor public LocationRequestCompat.Builder(long);
+    method public androidx.core.location.LocationRequestCompat build();
+    method public androidx.core.location.LocationRequestCompat.Builder clearMinUpdateIntervalMillis();
+    method public androidx.core.location.LocationRequestCompat.Builder setDurationMillis(@IntRange(from=1) long);
+    method public androidx.core.location.LocationRequestCompat.Builder setIntervalMillis(@IntRange(from=0) long);
+    method public androidx.core.location.LocationRequestCompat.Builder setMaxUpdateDelayMillis(@IntRange(from=0) long);
+    method public androidx.core.location.LocationRequestCompat.Builder setMaxUpdates(@IntRange(from=1, to=java.lang.Integer.MAX_VALUE) int);
+    method public androidx.core.location.LocationRequestCompat.Builder setMinUpdateDistanceMeters(@FloatRange(from=0, to=java.lang.Float.MAX_VALUE) float);
+    method public androidx.core.location.LocationRequestCompat.Builder setMinUpdateIntervalMillis(@IntRange(from=0) long);
+    method public androidx.core.location.LocationRequestCompat.Builder setQuality(int);
+  }
+
+}
+
+package androidx.core.math {
+
+  public class MathUtils {
+    method public static int addExact(int, int);
+    method public static long addExact(long, long);
+    method public static double clamp(double, double, double);
+    method public static float clamp(float, float, float);
+    method public static int clamp(int, int, int);
+    method public static long clamp(long, long, long);
+    method public static int decrementExact(int);
+    method public static long decrementExact(long);
+    method public static int incrementExact(int);
+    method public static long incrementExact(long);
+    method public static int multiplyExact(int, int);
+    method public static long multiplyExact(long, long);
+    method public static int negateExact(int);
+    method public static long negateExact(long);
+    method public static int subtractExact(int, int);
+    method public static long subtractExact(long, long);
+    method public static int toIntExact(long);
+  }
+
+}
+
+package androidx.core.net {
+
+  public final class ConnectivityManagerCompat {
+    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public static android.net.NetworkInfo? getNetworkInfoFromBroadcast(android.net.ConnectivityManager, android.content.Intent);
+    method @androidx.core.net.ConnectivityManagerCompat.RestrictBackgroundStatus public static int getRestrictBackgroundStatus(android.net.ConnectivityManager);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public static boolean isActiveNetworkMetered(android.net.ConnectivityManager);
+    field public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1; // 0x1
+    field public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3; // 0x3
+    field public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2; // 0x2
+  }
+
+  @IntDef({androidx.core.net.ConnectivityManagerCompat.RESTRICT_BACKGROUND_STATUS_DISABLED, androidx.core.net.ConnectivityManagerCompat.RESTRICT_BACKGROUND_STATUS_WHITELISTED, androidx.core.net.ConnectivityManagerCompat.RESTRICT_BACKGROUND_STATUS_ENABLED}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ConnectivityManagerCompat.RestrictBackgroundStatus {
+  }
+
+  public final class MailTo {
+    method public String? getBcc();
+    method public String? getBody();
+    method public String? getCc();
+    method public java.util.Map<java.lang.String!,java.lang.String!>? getHeaders();
+    method public String? getSubject();
+    method public String? getTo();
+    method public static boolean isMailTo(android.net.Uri?);
+    method public static boolean isMailTo(String?);
+    method public static androidx.core.net.MailTo parse(android.net.Uri) throws androidx.core.net.ParseException;
+    method public static androidx.core.net.MailTo parse(String) throws androidx.core.net.ParseException;
+    field public static final String MAILTO_SCHEME = "mailto:";
+  }
+
+  public class ParseException extends java.lang.RuntimeException {
+    field public final String response;
+  }
+
+  public final class TrafficStatsCompat {
+    method @Deprecated public static void clearThreadStatsTag();
+    method @Deprecated public static int getThreadStatsTag();
+    method @Deprecated public static void incrementOperationCount(int);
+    method @Deprecated public static void incrementOperationCount(int, int);
+    method @Deprecated public static void setThreadStatsTag(int);
+    method public static void tagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
+    method @Deprecated public static void tagSocket(java.net.Socket!) throws java.net.SocketException;
+    method public static void untagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
+    method @Deprecated public static void untagSocket(java.net.Socket!) throws java.net.SocketException;
+  }
+
+  public final class UriCompat {
+    method public static String toSafeString(android.net.Uri);
+  }
+
+}
+
+package androidx.core.os {
+
+  public final class BuildCompat {
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.N) public static boolean isAtLeastN();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.N_MR1) public static boolean isAtLeastNMR1();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.O) public static boolean isAtLeastO();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.O_MR1) public static boolean isAtLeastOMR1();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.P) public static boolean isAtLeastP();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.Q) public static boolean isAtLeastQ();
+    method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.R) public static boolean isAtLeastR();
+    method @Deprecated @ChecksSdkIntAtLeast(api=31, codename="S") public static boolean isAtLeastS();
+    method @Deprecated @ChecksSdkIntAtLeast(api=32, codename="Sv2") public static boolean isAtLeastSv2();
+    method @Deprecated @ChecksSdkIntAtLeast(api=33, codename="Tiramisu") public static boolean isAtLeastT();
+    method @Deprecated @ChecksSdkIntAtLeast(api=34, codename="UpsideDownCake") public static boolean isAtLeastU();
+    method @SuppressCompatibility @ChecksSdkIntAtLeast(codename="VanillaIceCream") @androidx.core.os.BuildCompat.PrereleaseSdkCheck public static boolean isAtLeastV();
+    field @ChecksSdkIntAtLeast(extension=android.os.ext.SdkExtensions.AD_SERVICES) public static final int AD_SERVICES_EXTENSION_INT;
+    field public static final androidx.core.os.BuildCompat INSTANCE;
+    field @ChecksSdkIntAtLeast(extension=android.os.Build.VERSION_CODES.R) public static final int R_EXTENSION_INT;
+    field @ChecksSdkIntAtLeast(extension=android.os.Build.VERSION_CODES.S) public static final int S_EXTENSION_INT;
+    field @ChecksSdkIntAtLeast(extension=android.os.Build.VERSION_CODES.TIRAMISU) public static final int T_EXTENSION_INT;
+  }
+
+  @SuppressCompatibility @kotlin.RequiresOptIn @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public static @interface BuildCompat.PrereleaseSdkCheck {
+  }
+
+  public final class BundleCompat {
+    method public static android.os.IBinder? getBinder(android.os.Bundle, String?);
+    method public static <T> T? getParcelable(android.os.Bundle, String?, Class<T!>);
+    method public static android.os.Parcelable![]? getParcelableArray(android.os.Bundle, String?, Class<? extends android.os.Parcelable>);
+    method public static <T> java.util.ArrayList<T!>? getParcelableArrayList(android.os.Bundle, String?, Class<? extends T>);
+    method public static <T> android.util.SparseArray<T!>? getSparseParcelableArray(android.os.Bundle, String?, Class<? extends T>);
+    method public static void putBinder(android.os.Bundle, String?, android.os.IBinder?);
+  }
+
+  public final class CancellationSignal {
+    ctor public CancellationSignal();
+    method public void cancel();
+    method public Object? getCancellationSignalObject();
+    method public boolean isCanceled();
+    method public void setOnCancelListener(androidx.core.os.CancellationSignal.OnCancelListener?);
+    method public void throwIfCanceled();
+  }
+
+  public static interface CancellationSignal.OnCancelListener {
+    method public void onCancel();
+  }
+
+  public final class ConfigurationCompat {
+    method public static androidx.core.os.LocaleListCompat getLocales(android.content.res.Configuration);
+    method public static void setLocales(android.content.res.Configuration, androidx.core.os.LocaleListCompat);
+  }
+
+  public final class EnvironmentCompat {
+    method public static String getStorageState(java.io.File);
+    field public static final String MEDIA_UNKNOWN = "unknown";
+  }
+
+  public final class ExecutorCompat {
+    method public static java.util.concurrent.Executor create(android.os.Handler);
+  }
+
+  public final class HandlerCompat {
+    method public static android.os.Handler createAsync(android.os.Looper);
+    method public static android.os.Handler createAsync(android.os.Looper, android.os.Handler.Callback);
+    method @RequiresApi(16) public static boolean hasCallbacks(android.os.Handler, Runnable);
+    method public static boolean postDelayed(android.os.Handler, Runnable, Object?, long);
+  }
+
+  public final class LocaleListCompat {
+    method public static androidx.core.os.LocaleListCompat create(java.util.Locale!...);
+    method public static androidx.core.os.LocaleListCompat forLanguageTags(String?);
+    method public java.util.Locale? get(int);
+    method @Size(min=1) public static androidx.core.os.LocaleListCompat getAdjustedDefault();
+    method @Size(min=1) public static androidx.core.os.LocaleListCompat getDefault();
+    method public static androidx.core.os.LocaleListCompat getEmptyLocaleList();
+    method public java.util.Locale? getFirstMatch(String![]);
+    method @IntRange(from=0xffffffff) public int indexOf(java.util.Locale?);
+    method public boolean isEmpty();
+    method @RequiresApi(21) public static boolean matchesLanguageAndScript(java.util.Locale, java.util.Locale);
+    method @IntRange(from=0) public int size();
+    method public String toLanguageTags();
+    method public Object? unwrap();
+    method @RequiresApi(24) public static androidx.core.os.LocaleListCompat wrap(android.os.LocaleList);
+    method @Deprecated @RequiresApi(24) public static androidx.core.os.LocaleListCompat! wrap(Object!);
+  }
+
+  public final class MessageCompat {
+    method public static boolean isAsynchronous(android.os.Message);
+    method public static void setAsynchronous(android.os.Message, boolean);
+  }
+
+  public class OperationCanceledException extends java.lang.RuntimeException {
+    ctor public OperationCanceledException();
+    ctor public OperationCanceledException(String?);
+  }
+
+  public final class ParcelCompat {
+    method public static <T> Object![]? readArray(android.os.Parcel, ClassLoader?, Class<T!>);
+    method public static <T> java.util.ArrayList<T!>? readArrayList(android.os.Parcel, ClassLoader?, Class<? extends T>);
+    method public static boolean readBoolean(android.os.Parcel);
+    method public static <K, V> java.util.HashMap<K!,V!>? readHashMap(android.os.Parcel, ClassLoader?, Class<? extends K>, Class<? extends V>);
+    method public static <T> void readList(android.os.Parcel, java.util.List<? super T>, ClassLoader?, Class<T!>);
+    method public static <K, V> void readMap(android.os.Parcel, java.util.Map<? super K,? super V>, ClassLoader?, Class<K!>, Class<V!>);
+    method public static <T extends android.os.Parcelable> T? readParcelable(android.os.Parcel, ClassLoader?, Class<T!>);
+    method @Deprecated public static <T> T![]? readParcelableArray(android.os.Parcel, ClassLoader?, Class<T!>);
+    method public static <T> android.os.Parcelable![]? readParcelableArrayTyped(android.os.Parcel, ClassLoader?, Class<T!>);
+    method @RequiresApi(30) public static <T> android.os.Parcelable.Creator<T!>? readParcelableCreator(android.os.Parcel, ClassLoader?, Class<T!>);
+    method @RequiresApi(api=android.os.Build.VERSION_CODES.Q) public static <T> java.util.List<T!> readParcelableList(android.os.Parcel, java.util.List<T!>, ClassLoader?, Class<T!>);
+    method public static <T extends java.io.Serializable> T? readSerializable(android.os.Parcel, ClassLoader?, Class<T!>);
+    method public static <T> android.util.SparseArray<T!>? readSparseArray(android.os.Parcel, ClassLoader?, Class<? extends T>);
+    method public static void writeBoolean(android.os.Parcel, boolean);
+  }
+
+  @Deprecated public final class ParcelableCompat {
+    method @Deprecated public static <T> android.os.Parcelable.Creator<T!>! newCreator(androidx.core.os.ParcelableCompatCreatorCallbacks<T!>!);
+  }
+
+  @Deprecated public interface ParcelableCompatCreatorCallbacks<T> {
+    method @Deprecated public T! createFromParcel(android.os.Parcel!, ClassLoader!);
+    method @Deprecated public T![]! newArray(int);
+  }
+
+  public final class ProcessCompat {
+    method public static boolean isApplicationUid(int);
+  }
+
+  @Deprecated public final class TraceCompat {
+    method @Deprecated public static void beginAsyncSection(String, int);
+    method @Deprecated public static void beginSection(String);
+    method @Deprecated public static void endAsyncSection(String, int);
+    method @Deprecated public static void endSection();
+    method @Deprecated public static boolean isEnabled();
+    method @Deprecated public static void setCounter(String, int);
+  }
+
+  @RequiresApi(17) public class UserHandleCompat {
+    method public static android.os.UserHandle getUserHandleForUid(int);
+  }
+
+  public class UserManagerCompat {
+    method public static boolean isUserUnlocked(android.content.Context);
+  }
+
+}
+
+package androidx.core.provider {
+
+  public final class DocumentsContractCompat {
+    method public static android.net.Uri? buildChildDocumentsUri(String, String?);
+    method public static android.net.Uri? buildChildDocumentsUriUsingTree(android.net.Uri, String);
+    method public static android.net.Uri? buildDocumentUri(String, String);
+    method public static android.net.Uri? buildDocumentUriUsingTree(android.net.Uri, String);
+    method public static android.net.Uri? buildTreeDocumentUri(String, String);
+    method public static android.net.Uri? createDocument(android.content.ContentResolver, android.net.Uri, String, String) throws java.io.FileNotFoundException;
+    method public static String? getDocumentId(android.net.Uri);
+    method public static String? getTreeDocumentId(android.net.Uri);
+    method public static boolean isDocumentUri(android.content.Context, android.net.Uri?);
+    method public static boolean isTreeUri(android.net.Uri);
+    method public static boolean removeDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+    method public static android.net.Uri? renameDocument(android.content.ContentResolver, android.net.Uri, String) throws java.io.FileNotFoundException;
+  }
+
+  public static final class DocumentsContractCompat.DocumentCompat {
+    field public static final int FLAG_VIRTUAL_DOCUMENT = 512; // 0x200
+  }
+
+  public final class FontRequest {
+    ctor public FontRequest(String, String, String, @ArrayRes int);
+    ctor public FontRequest(String, String, String, java.util.List<java.util.List<byte[]!>!>);
+    method public java.util.List<java.util.List<byte[]!>!>? getCertificates();
+    method @ArrayRes public int getCertificatesArrayResId();
+    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public String! getIdentifier();
+    method public String getProviderAuthority();
+    method public String getProviderPackage();
+    method public String getQuery();
+  }
+
+  public class FontsContractCompat {
+    method public static android.graphics.Typeface? buildTypeface(android.content.Context, android.os.CancellationSignal?, androidx.core.provider.FontsContractCompat.FontInfo![]);
+    method public static androidx.core.provider.FontsContractCompat.FontFamilyResult fetchFonts(android.content.Context, android.os.CancellationSignal?, androidx.core.provider.FontRequest) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static android.graphics.Typeface! getFontSync(android.content.Context!, androidx.core.provider.FontRequest!, androidx.core.content.res.ResourcesCompat.FontCallback?, android.os.Handler?, boolean, int, int);
+    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @VisibleForTesting public static android.content.pm.ProviderInfo? getProvider(android.content.pm.PackageManager, androidx.core.provider.FontRequest, android.content.res.Resources?) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @Deprecated @RequiresApi(19) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static java.util.Map<android.net.Uri!,java.nio.ByteBuffer!>! prepareFontData(android.content.Context!, androidx.core.provider.FontsContractCompat.FontInfo![]!, android.os.CancellationSignal!);
+    method public static void requestFont(android.content.Context, androidx.core.provider.FontRequest, androidx.core.provider.FontsContractCompat.FontRequestCallback, android.os.Handler);
+    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static void resetCache();
+    field @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final String PARCEL_FONT_RESULTS = "font_results";
+  }
+
+  public static final class FontsContractCompat.Columns implements android.provider.BaseColumns {
+    ctor public FontsContractCompat.Columns();
+    field public static final String FILE_ID = "file_id";
+    field public static final String ITALIC = "font_italic";
+    field public static final String RESULT_CODE = "result_code";
+    field public static final int RESULT_CODE_FONT_NOT_FOUND = 1; // 0x1
+    field public static final int RESULT_CODE_FONT_UNAVAILABLE = 2; // 0x2
+    field public static final int RESULT_CODE_MALFORMED_QUERY = 3; // 0x3
+    field public static final int RESULT_CODE_OK = 0; // 0x0
+    field public static final String TTC_INDEX = "font_ttc_index";
+    field public static final String VARIATION_SETTINGS = "font_variation_settings";
+    field public static final String WEIGHT = "font_weight";
+  }
+
+  public static class FontsContractCompat.FontFamilyResult {
+    ctor @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public FontsContractCompat.FontFamilyResult(int, androidx.core.provider.FontsContractCompat.FontInfo![]?);
+    method public androidx.core.provider.FontsContractCompat.FontInfo![]! getFonts();
+    method public int getStatusCode();
+    field public static final int STATUS_OK = 0; // 0x0
+    field public static final int STATUS_UNEXPECTED_DATA_PROVIDED = 2; // 0x2
+    field public static final int STATUS_WRONG_CERTIFICATES = 1; // 0x1
+  }
+
+  public static class FontsContractCompat.FontInfo {
+    ctor @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public FontsContractCompat.FontInfo(android.net.Uri, @IntRange(from=0) int, @IntRange(from=1, to=1000) int, boolean, int);
+    method public int getResultCode();
+    method @IntRange(from=0) public int getTtcIndex();
+    method public android.net.Uri getUri();
+    method @IntRange(from=1, to=1000) public int getWeight();
+    method public boolean isItalic();
+  }
+
+  public static class FontsContractCompat.FontRequestCallback {
+    ctor public FontsContractCompat.FontRequestCallback();
+    method public void onTypefaceRequestFailed(@androidx.core.provider.FontsContractCompat.FontRequestCallback.FontRequestFailReason int);
+    method public void onTypefaceRetrieved(android.graphics.Typeface!);
+    field public static final int FAIL_REASON_FONT_LOAD_ERROR = -3; // 0xfffffffd
+    field public static final int FAIL_REASON_FONT_NOT_FOUND = 1; // 0x1
+    field public static final int FAIL_REASON_FONT_UNAVAILABLE = 2; // 0x2
+    field public static final int FAIL_REASON_MALFORMED_QUERY = 3; // 0x3
+    field public static final int FAIL_REASON_PROVIDER_NOT_FOUND = -1; // 0xffffffff
+    field public static final int FAIL_REASON_SECURITY_VIOLATION = -4; // 0xfffffffc
+    field public static final int FAIL_REASON_WRONG_CERTIFICATES = -2; // 0xfffffffe
+    field @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final int RESULT_OK = 0; // 0x0
+  }
+
+  @IntDef({androidx.core.provider.FontsContractCompat.FontRequestCallback.FAIL_REASON_PROVIDER_NOT_FOUND, androidx.core.provider.FontsContractCompat.FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR, androidx.core.provider.FontsContractCompat.FontRequestCallback.FAIL_REASON_FONT_NOT_FOUND, androidx.core.provider.FontsContractCompat.FontRequestCallback.FAIL_REASON_FONT_UNAVAILABLE, androidx.core.provider.FontsContractCompat.FontRequestCallback.FAIL_REASON_MALFORMED_QUERY, androidx.core.provider.FontsContractCompat.FontRequestCallback.FAIL_REASON_WRONG_CERTIFICATES, androidx.core.provider.FontsContractCompat.FontRequestCallback.FAIL_REASON_SECURITY_VIOLATION, androidx.core.provider.FontsContractCompat.FontRequestCallback.RESULT_OK}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface FontsContractCompat.FontRequestCallback.FontRequestFailReason {
+  }
+
+  @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class SelfDestructiveThread {
+    ctor @Deprecated public SelfDestructiveThread(String!, int, int);
+    method @Deprecated @VisibleForTesting public int getGeneration();
+    method @Deprecated @VisibleForTesting public boolean isRunning();
+    method @Deprecated public <T> void postAndReply(java.util.concurrent.Callable<T!>!, androidx.core.provider.SelfDestructiveThread.ReplyCallback<T!>!);
+    method @Deprecated public <T> T! postAndWait(java.util.concurrent.Callable<T!>!, int) throws java.lang.InterruptedException;
+  }
+
+  @Deprecated public static interface SelfDestructiveThread.ReplyCallback<T> {
+    method @Deprecated public void onReply(T!);
+  }
+
+}
+
+package androidx.core.service.quicksettings {
+
+  public class PendingIntentActivityWrapper {
+    ctor public PendingIntentActivityWrapper(android.content.Context, int, android.content.Intent, int, android.os.Bundle?, boolean);
+    ctor public PendingIntentActivityWrapper(android.content.Context, int, android.content.Intent, int, boolean);
+    method public android.content.Context getContext();
+    method public int getFlags();
+    method public android.content.Intent getIntent();
+    method public android.os.Bundle getOptions();
+    method public android.app.PendingIntent? getPendingIntent();
+    method public int getRequestCode();
+    method public boolean isMutable();
+  }
+
+  public class TileServiceCompat {
+    method public static void startActivityAndCollapse(android.service.quicksettings.TileService, androidx.core.service.quicksettings.PendingIntentActivityWrapper);
+  }
+
+}
+
+package androidx.core.telephony {
+
+  @RequiresApi(22) public class SubscriptionManagerCompat {
+    method public static int getSlotIndex(int);
+  }
+
+  public class TelephonyManagerCompat {
+    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static String? getImei(android.telephony.TelephonyManager);
+    method public static int getSubscriptionId(android.telephony.TelephonyManager);
+  }
+
+}
+
+package androidx.core.telephony.mbms {
+
+  public final class MbmsHelper {
+    method public static CharSequence? getBestNameForService(android.content.Context, android.telephony.mbms.ServiceInfo);
+  }
+
+}
+
+package androidx.core.text {
+
+  public final class BidiFormatter {
+    method public static androidx.core.text.BidiFormatter! getInstance();
+    method public static androidx.core.text.BidiFormatter! getInstance(boolean);
+    method public static androidx.core.text.BidiFormatter! getInstance(java.util.Locale!);
+    method public boolean getStereoReset();
+    method public boolean isRtl(CharSequence!);
+    method public boolean isRtl(String!);
+    method public boolean isRtlContext();
+    method public CharSequence! unicodeWrap(CharSequence!);
+    method public CharSequence! unicodeWrap(CharSequence!, androidx.core.text.TextDirectionHeuristicCompat!);
+    method public CharSequence! unicodeWrap(CharSequence!, androidx.core.text.TextDirectionHeuristicCompat!, boolean);
+    method public CharSequence! unicodeWrap(CharSequence!, boolean);
+    method public String! unicodeWrap(String!);
+    method public String! unicodeWrap(String!, androidx.core.text.TextDirectionHeuristicCompat!);
+    method public String! unicodeWrap(String!, androidx.core.text.TextDirectionHeuristicCompat!, boolean);
+    method public String! unicodeWrap(String!, boolean);
+  }
+
+  public static final class BidiFormatter.Builder {
+    ctor public BidiFormatter.Builder();
+    ctor public BidiFormatter.Builder(boolean);
+    ctor public BidiFormatter.Builder(java.util.Locale!);
+    method public androidx.core.text.BidiFormatter! build();
+    method public androidx.core.text.BidiFormatter.Builder! setTextDirectionHeuristic(androidx.core.text.TextDirectionHeuristicCompat!);
+    method public androidx.core.text.BidiFormatter.Builder! stereoReset(boolean);
+  }
+
+  public final class HtmlCompat {
+    method public static android.text.Spanned fromHtml(String, int);
+    method public static android.text.Spanned fromHtml(String, int, android.text.Html.ImageGetter?, android.text.Html.TagHandler?);
+    method public static String toHtml(android.text.Spanned, int);
+    field public static final int FROM_HTML_MODE_COMPACT = 63; // 0x3f
+    field public static final int FROM_HTML_MODE_LEGACY = 0; // 0x0
+    field public static final int FROM_HTML_OPTION_USE_CSS_COLORS = 256; // 0x100
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_BLOCKQUOTE = 32; // 0x20
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_DIV = 16; // 0x10
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_HEADING = 2; // 0x2
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_LIST = 8; // 0x8
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM = 4; // 0x4
+    field public static final int FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH = 1; // 0x1
+    field public static final int TO_HTML_PARAGRAPH_LINES_CONSECUTIVE = 0; // 0x0
+    field public static final int TO_HTML_PARAGRAPH_LINES_INDIVIDUAL = 1; // 0x1
+  }
+
+  public final class ICUCompat {
+    method public static String? maximizeAndGetScript(java.util.Locale);
+  }
+
+  public class PrecomputedTextCompat implements android.text.Spannable {
+    method public char charAt(int);
+    method public static androidx.core.text.PrecomputedTextCompat! create(CharSequence, androidx.core.text.PrecomputedTextCompat.Params);
+    method @IntRange(from=0) public int getParagraphCount();
+    method @IntRange(from=0) public int getParagraphEnd(@IntRange(from=0) int);
+    method @IntRange(from=0) public int getParagraphStart(@IntRange(from=0) int);
+    method public androidx.core.text.PrecomputedTextCompat.Params getParams();
+    method @RequiresApi(28) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.text.PrecomputedText? getPrecomputedText();
+    method public int getSpanEnd(Object!);
+    method public int getSpanFlags(Object!);
+    method public int getSpanStart(Object!);
+    method public <T> T![]! getSpans(int, int, Class<T!>!);
+    method @UiThread public static java.util.concurrent.Future<androidx.core.text.PrecomputedTextCompat!>! getTextFuture(CharSequence, androidx.core.text.PrecomputedTextCompat.Params, java.util.concurrent.Executor?);
+    method public int length();
+    method public int nextSpanTransition(int, int, Class!);
+    method public void removeSpan(Object!);
+    method public void setSpan(Object!, int, int, int);
+    method public CharSequence! subSequence(int, int);
+  }
+
+  public static final class PrecomputedTextCompat.Params {
+    ctor @RequiresApi(28) public PrecomputedTextCompat.Params(android.text.PrecomputedText.Params);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean equalsWithoutTextDirection(androidx.core.text.PrecomputedTextCompat.Params);
+    method @RequiresApi(23) public int getBreakStrategy();
+    method @RequiresApi(23) public int getHyphenationFrequency();
+    method @RequiresApi(18) public android.text.TextDirectionHeuristic? getTextDirection();
+    method public android.text.TextPaint getTextPaint();
+  }
+
+  public static class PrecomputedTextCompat.Params.Builder {
+    ctor public PrecomputedTextCompat.Params.Builder(android.text.TextPaint);
+    method public androidx.core.text.PrecomputedTextCompat.Params build();
+    method @RequiresApi(23) public androidx.core.text.PrecomputedTextCompat.Params.Builder! setBreakStrategy(int);
+    method @RequiresApi(23) public androidx.core.text.PrecomputedTextCompat.Params.Builder! setHyphenationFrequency(int);
+    method @RequiresApi(18) public androidx.core.text.PrecomputedTextCompat.Params.Builder! setTextDirection(android.text.TextDirectionHeuristic);
+  }
+
+  public interface TextDirectionHeuristicCompat {
+    method public boolean isRtl(char[]!, int, int);
+    method public boolean isRtl(CharSequence!, int, int);
+  }
+
+  public final class TextDirectionHeuristicsCompat {
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! ANYRTL_LTR;
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! FIRSTSTRONG_LTR;
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! FIRSTSTRONG_RTL;
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! LOCALE;
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! LTR;
+    field public static final androidx.core.text.TextDirectionHeuristicCompat! RTL;
+  }
+
+  public final class TextUtilsCompat {
+    method public static int getLayoutDirectionFromLocale(java.util.Locale?);
+    method public static String htmlEncode(String);
+  }
+
+}
+
+package androidx.core.text.method {
+
+  public class LinkMovementMethodCompat extends android.text.method.LinkMovementMethod {
+    method public static androidx.core.text.method.LinkMovementMethodCompat getInstance();
+  }
+
+}
+
+package androidx.core.text.util {
+
+  public final class LinkifyCompat {
+    method public static boolean addLinks(android.text.Spannable, @androidx.core.text.util.LinkifyCompat.LinkifyMask int);
+    method public static boolean addLinks(android.text.Spannable, java.util.regex.Pattern, String?);
+    method public static boolean addLinks(android.text.Spannable, java.util.regex.Pattern, String?, android.text.util.Linkify.MatchFilter?, android.text.util.Linkify.TransformFilter?);
+    method public static boolean addLinks(android.text.Spannable, java.util.regex.Pattern, String?, String![]?, android.text.util.Linkify.MatchFilter?, android.text.util.Linkify.TransformFilter?);
+    method public static boolean addLinks(android.widget.TextView, @androidx.core.text.util.LinkifyCompat.LinkifyMask int);
+    method public static void addLinks(android.widget.TextView, java.util.regex.Pattern, String?);
+    method public static void addLinks(android.widget.TextView, java.util.regex.Pattern, String?, android.text.util.Linkify.MatchFilter?, android.text.util.Linkify.TransformFilter?);
+    method public static void addLinks(android.widget.TextView, java.util.regex.Pattern, String?, String![]?, android.text.util.Linkify.MatchFilter?, android.text.util.Linkify.TransformFilter?);
+  }
+
+  @IntDef(flag=true, value={android.text.util.Linkify.WEB_URLS, android.text.util.Linkify.EMAIL_ADDRESSES, android.text.util.Linkify.PHONE_NUMBERS, android.text.util.Linkify.MAP_ADDRESSES, android.text.util.Linkify.ALL}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface LinkifyCompat.LinkifyMask {
+  }
+
+  @RequiresApi(android.os.Build.VERSION_CODES.LOLLIPOP) public final class LocalePreferences {
+    method public static String getCalendarType();
+    method public static String getCalendarType(boolean);
+    method public static String getCalendarType(java.util.Locale);
+    method public static String getCalendarType(java.util.Locale, boolean);
+    method public static String getFirstDayOfWeek();
+    method public static String getFirstDayOfWeek(boolean);
+    method public static String getFirstDayOfWeek(java.util.Locale);
+    method public static String getFirstDayOfWeek(java.util.Locale, boolean);
+    method public static String getHourCycle();
+    method public static String getHourCycle(boolean);
+    method public static String getHourCycle(java.util.Locale);
+    method public static String getHourCycle(java.util.Locale, boolean);
+    method public static String getTemperatureUnit();
+    method public static String getTemperatureUnit(boolean);
+    method public static String getTemperatureUnit(java.util.Locale);
+    method public static String getTemperatureUnit(java.util.Locale, boolean);
+  }
+
+  public static class LocalePreferences.CalendarType {
+    field public static final String CHINESE = "chinese";
+    field public static final String DANGI = "dangi";
+    field public static final String DEFAULT = "";
+    field public static final String GREGORIAN = "gregorian";
+    field public static final String HEBREW = "hebrew";
+    field public static final String INDIAN = "indian";
+    field public static final String ISLAMIC = "islamic";
+    field public static final String ISLAMIC_CIVIL = "islamic-civil";
+    field public static final String ISLAMIC_RGSA = "islamic-rgsa";
+    field public static final String ISLAMIC_TBLA = "islamic-tbla";
+    field public static final String ISLAMIC_UMALQURA = "islamic-umalqura";
+    field public static final String PERSIAN = "persian";
+  }
+
+  public static class LocalePreferences.FirstDayOfWeek {
+    field public static final String DEFAULT = "";
+    field public static final String FRIDAY = "fri";
+    field public static final String MONDAY = "mon";
+    field public static final String SATURDAY = "sat";
+    field public static final String SUNDAY = "sun";
+    field public static final String THURSDAY = "thu";
+    field public static final String TUESDAY = "tue";
+    field public static final String WEDNESDAY = "wed";
+  }
+
+  public static class LocalePreferences.HourCycle {
+    field public static final String DEFAULT = "";
+    field public static final String H11 = "h11";
+    field public static final String H12 = "h12";
+    field public static final String H23 = "h23";
+    field public static final String H24 = "h24";
+  }
+
+  public static class LocalePreferences.TemperatureUnit {
+    field public static final String CELSIUS = "celsius";
+    field public static final String DEFAULT = "";
+    field public static final String FAHRENHEIT = "fahrenhe";
+    field public static final String KELVIN = "kelvin";
+  }
+
+}
+
+package androidx.core.util {
+
+  public class AtomicFile {
+    ctor public AtomicFile(java.io.File);
+    method public void delete();
+    method public void failWrite(java.io.FileOutputStream?);
+    method public void finishWrite(java.io.FileOutputStream?);
+    method public java.io.File getBaseFile();
+    method public java.io.FileInputStream openRead() throws java.io.FileNotFoundException;
+    method public byte[] readFully() throws java.io.IOException;
+    method public java.io.FileOutputStream startWrite() throws java.io.IOException;
+  }
+
+  public interface Consumer<T> {
+    method public void accept(T!);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class DebugUtils {
+    method public static void buildShortClassTag(Object!, StringBuilder!);
+  }
+
+  @java.lang.FunctionalInterface public interface Function<T, R> {
+    method public R! apply(T!);
+  }
+
+  @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class LogWriter extends java.io.Writer {
+    ctor @Deprecated public LogWriter(String!);
+    method @Deprecated public void close();
+    method @Deprecated public void flush();
+    method @Deprecated public void write(char[]!, int, int);
+  }
+
+  public class ObjectsCompat {
+    method public static boolean equals(Object?, Object?);
+    method public static int hash(java.lang.Object!...);
+    method public static int hashCode(Object?);
+    method public static <T> T requireNonNull(T?);
+    method public static <T> T requireNonNull(T?, String);
+    method public static String? toString(Object?, String?);
+  }
+
+  public class Pair<F, S> {
+    ctor public Pair(F!, S!);
+    method public static <A, B> androidx.core.util.Pair<A!,B!> create(A!, B!);
+    field public final F! first;
+    field public final S! second;
+  }
+
+  public final class PatternsCompat {
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final java.util.regex.Pattern AUTOLINK_EMAIL_ADDRESS;
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final java.util.regex.Pattern AUTOLINK_WEB_URL;
+    field public static final java.util.regex.Pattern DOMAIN_NAME;
+    field public static final java.util.regex.Pattern EMAIL_ADDRESS;
+    field public static final java.util.regex.Pattern IP_ADDRESS;
+    field public static final java.util.regex.Pattern WEB_URL;
+  }
+
+  public final class Pools {
+  }
+
+  public static interface Pools.Pool<T> {
+    method public T? acquire();
+    method public boolean release(T);
+  }
+
+  public static class Pools.SimplePool<T> implements androidx.core.util.Pools.Pool<T> {
+    ctor public Pools.SimplePool(int);
+    method public T! acquire();
+    method public boolean release(T);
+  }
+
+  public static class Pools.SynchronizedPool<T> extends androidx.core.util.Pools.SimplePool<T> {
+    ctor public Pools.SynchronizedPool(int);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class Preconditions {
+    method public static void checkArgument(boolean);
+    method public static void checkArgument(boolean, Object);
+    method public static void checkArgument(boolean, String, java.lang.Object!...);
+    method public static float checkArgumentFinite(float, String);
+    method public static double checkArgumentInRange(double, double, double, String);
+    method public static float checkArgumentInRange(float, float, float, String);
+    method public static int checkArgumentInRange(int, int, int, String);
+    method public static long checkArgumentInRange(long, long, long, String);
+    method @IntRange(from=0) public static int checkArgumentNonnegative(int);
+    method @IntRange(from=0) public static int checkArgumentNonnegative(int, String?);
+    method public static int checkFlagsArgument(int, int);
+    method public static <T> T checkNotNull(T?);
+    method public static <T> T checkNotNull(T?, Object);
+    method public static void checkState(boolean);
+    method public static void checkState(boolean, String?);
+    method public static <T extends java.lang.CharSequence> T checkStringNotEmpty(T?);
+    method public static <T extends java.lang.CharSequence> T checkStringNotEmpty(T?, Object);
+    method public static <T extends java.lang.CharSequence> T checkStringNotEmpty(T?, String, java.lang.Object!...);
+  }
+
+  public interface Predicate<T> {
+    method public default androidx.core.util.Predicate<T!>! and(androidx.core.util.Predicate<? super T>!);
+    method public static <T> androidx.core.util.Predicate<T!>! isEqual(Object!);
+    method public default androidx.core.util.Predicate<T!>! negate();
+    method public static <T> androidx.core.util.Predicate<T!>! not(androidx.core.util.Predicate<? super T>!);
+    method public default androidx.core.util.Predicate<T!>! or(androidx.core.util.Predicate<? super T>!);
+    method public boolean test(T!);
+  }
+
+  public final class SizeFCompat {
+    ctor public SizeFCompat(float, float);
+    method public float getHeight();
+    method public float getWidth();
+    method @RequiresApi(21) public android.util.SizeF toSizeF();
+    method @RequiresApi(21) public static androidx.core.util.SizeFCompat toSizeFCompat(android.util.SizeF);
+  }
+
+  public interface Supplier<T> {
+    method public T! get();
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class TimeUtils {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static void formatDuration(long, java.io.PrintWriter!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static void formatDuration(long, java.io.PrintWriter!, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static void formatDuration(long, StringBuilder!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static void formatDuration(long, long, java.io.PrintWriter!);
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final int HUNDRED_DAY_FIELD_LEN = 19; // 0x13
+  }
+
+  public class TypedValueCompat {
+    method public static float deriveDimension(int, float, android.util.DisplayMetrics);
+    method public static float dpToPx(float, android.util.DisplayMetrics);
+    method public static int getUnitFromComplexDimension(int);
+    method public static float pxToDp(float, android.util.DisplayMetrics);
+    method public static float pxToSp(float, android.util.DisplayMetrics);
+    method public static float spToPx(float, android.util.DisplayMetrics);
+  }
+
+}
+
+package androidx.core.view {
+
+  public class AccessibilityDelegateCompat {
+    ctor public AccessibilityDelegateCompat();
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public AccessibilityDelegateCompat(android.view.View.AccessibilityDelegate);
+    method public boolean dispatchPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public androidx.core.view.accessibility.AccessibilityNodeProviderCompat? getAccessibilityNodeProvider(android.view.View);
+    method public void onInitializeAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public void onInitializeAccessibilityNodeInfo(android.view.View, androidx.core.view.accessibility.AccessibilityNodeInfoCompat);
+    method public void onPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public boolean onRequestSendAccessibilityEvent(android.view.ViewGroup, android.view.View, android.view.accessibility.AccessibilityEvent);
+    method public boolean performAccessibilityAction(android.view.View, int, android.os.Bundle?);
+    method public void sendAccessibilityEvent(android.view.View, int);
+    method public void sendAccessibilityEventUnchecked(android.view.View, android.view.accessibility.AccessibilityEvent);
+  }
+
+  public abstract class ActionProvider {
+    ctor public ActionProvider(android.content.Context);
+    method public android.content.Context getContext();
+    method public boolean hasSubMenu();
+    method public boolean isVisible();
+    method public abstract android.view.View onCreateActionView();
+    method public android.view.View onCreateActionView(android.view.MenuItem);
+    method public boolean onPerformDefaultAction();
+    method public void onPrepareSubMenu(android.view.SubMenu);
+    method public boolean overridesItemVisibility();
+    method public void refreshVisibility();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void reset();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSubUiVisibilityListener(androidx.core.view.ActionProvider.SubUiVisibilityListener?);
+    method public void setVisibilityListener(androidx.core.view.ActionProvider.VisibilityListener?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void subUiVisibilityChanged(boolean);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static interface ActionProvider.SubUiVisibilityListener {
+    method public void onSubUiVisibilityChanged(boolean);
+  }
+
+  public static interface ActionProvider.VisibilityListener {
+    method public void onActionProviderVisibilityChanged(boolean);
+  }
+
+  public final class ContentInfoCompat {
+    method public android.content.ClipData getClip();
+    method public android.os.Bundle? getExtras();
+    method @androidx.core.view.ContentInfoCompat.Flags public int getFlags();
+    method public android.net.Uri? getLinkUri();
+    method @androidx.core.view.ContentInfoCompat.Source public int getSource();
+    method @RequiresApi(31) public static android.util.Pair<android.view.ContentInfo!,android.view.ContentInfo!> partition(android.view.ContentInfo, java.util.function.Predicate<android.content.ClipData.Item!>);
+    method public android.util.Pair<androidx.core.view.ContentInfoCompat!,androidx.core.view.ContentInfoCompat!> partition(androidx.core.util.Predicate<android.content.ClipData.Item!>);
+    method @RequiresApi(31) public android.view.ContentInfo toContentInfo();
+    method @RequiresApi(31) public static androidx.core.view.ContentInfoCompat toContentInfoCompat(android.view.ContentInfo);
+    field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
+    field public static final int SOURCE_APP = 0; // 0x0
+    field public static final int SOURCE_AUTOFILL = 4; // 0x4
+    field public static final int SOURCE_CLIPBOARD = 1; // 0x1
+    field public static final int SOURCE_DRAG_AND_DROP = 3; // 0x3
+    field public static final int SOURCE_INPUT_METHOD = 2; // 0x2
+    field public static final int SOURCE_PROCESS_TEXT = 5; // 0x5
+  }
+
+  public static final class ContentInfoCompat.Builder {
+    ctor public ContentInfoCompat.Builder(android.content.ClipData, @androidx.core.view.ContentInfoCompat.Source int);
+    ctor public ContentInfoCompat.Builder(androidx.core.view.ContentInfoCompat);
+    method public androidx.core.view.ContentInfoCompat build();
+    method public androidx.core.view.ContentInfoCompat.Builder setClip(android.content.ClipData);
+    method public androidx.core.view.ContentInfoCompat.Builder setExtras(android.os.Bundle?);
+    method public androidx.core.view.ContentInfoCompat.Builder setFlags(@androidx.core.view.ContentInfoCompat.Flags int);
+    method public androidx.core.view.ContentInfoCompat.Builder setLinkUri(android.net.Uri?);
+    method public androidx.core.view.ContentInfoCompat.Builder setSource(@androidx.core.view.ContentInfoCompat.Source int);
+  }
+
+  @IntDef(flag=true, value={androidx.core.view.ContentInfoCompat.FLAG_CONVERT_TO_PLAIN_TEXT}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ContentInfoCompat.Flags {
+  }
+
+  @IntDef({androidx.core.view.ContentInfoCompat.SOURCE_APP, androidx.core.view.ContentInfoCompat.SOURCE_CLIPBOARD, androidx.core.view.ContentInfoCompat.SOURCE_INPUT_METHOD, androidx.core.view.ContentInfoCompat.SOURCE_DRAG_AND_DROP, androidx.core.view.ContentInfoCompat.SOURCE_AUTOFILL, androidx.core.view.ContentInfoCompat.SOURCE_PROCESS_TEXT}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ContentInfoCompat.Source {
+  }
+
+  public final class DisplayCompat {
+    method public static androidx.core.view.DisplayCompat.ModeCompat getMode(android.content.Context, android.view.Display);
+    method public static androidx.core.view.DisplayCompat.ModeCompat![] getSupportedModes(android.content.Context, android.view.Display);
+  }
+
+  public static final class DisplayCompat.ModeCompat {
+    method public int getPhysicalHeight();
+    method public int getPhysicalWidth();
+    method @Deprecated public boolean isNative();
+    method @RequiresApi(android.os.Build.VERSION_CODES.M) public android.view.Display.Mode? toMode();
+  }
+
+  public final class DisplayCutoutCompat {
+    ctor public DisplayCutoutCompat(android.graphics.Rect?, java.util.List<android.graphics.Rect!>?);
+    ctor public DisplayCutoutCompat(androidx.core.graphics.Insets, android.graphics.Rect?, android.graphics.Rect?, android.graphics.Rect?, android.graphics.Rect?, androidx.core.graphics.Insets);
+    method public java.util.List<android.graphics.Rect!> getBoundingRects();
+    method public int getSafeInsetBottom();
+    method public int getSafeInsetLeft();
+    method public int getSafeInsetRight();
+    method public int getSafeInsetTop();
+    method public androidx.core.graphics.Insets getWaterfallInsets();
+  }
+
+  public final class DragAndDropPermissionsCompat {
+    method public void release();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static androidx.core.view.DragAndDropPermissionsCompat? request(android.app.Activity, android.view.DragEvent);
+  }
+
+  public class DragStartHelper {
+    ctor public DragStartHelper(android.view.View, androidx.core.view.DragStartHelper.OnDragStartListener);
+    method public void attach();
+    method public void detach();
+    method public void getTouchPosition(android.graphics.Point);
+    method public boolean onLongClick(android.view.View);
+    method public boolean onTouch(android.view.View, android.view.MotionEvent);
+  }
+
+  public static interface DragStartHelper.OnDragStartListener {
+    method public boolean onDragStart(android.view.View, androidx.core.view.DragStartHelper);
+  }
+
+  public final class GestureDetectorCompat {
+    ctor public GestureDetectorCompat(android.content.Context, android.view.GestureDetector.OnGestureListener);
+    ctor public GestureDetectorCompat(android.content.Context, android.view.GestureDetector.OnGestureListener, android.os.Handler?);
+    method public boolean isLongpressEnabled();
+    method public boolean onTouchEvent(android.view.MotionEvent);
+    method public void setIsLongpressEnabled(boolean);
+    method public void setOnDoubleTapListener(android.view.GestureDetector.OnDoubleTapListener?);
+  }
+
+  public final class GravityCompat {
+    method public static void apply(int, int, int, android.graphics.Rect, android.graphics.Rect, int);
+    method public static void apply(int, int, int, android.graphics.Rect, int, int, android.graphics.Rect, int);
+    method public static void applyDisplay(int, android.graphics.Rect, android.graphics.Rect, int);
+    method public static int getAbsoluteGravity(int, int);
+    field public static final int END = 8388613; // 0x800005
+    field public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = 8388615; // 0x800007
+    field public static final int RELATIVE_LAYOUT_DIRECTION = 8388608; // 0x800000
+    field public static final int START = 8388611; // 0x800003
+  }
+
+  public final class HapticFeedbackConstantsCompat {
+    field public static final int CLOCK_TICK = 4; // 0x4
+    field public static final int CONFIRM = 16; // 0x10
+    field public static final int CONTEXT_CLICK = 6; // 0x6
+    field public static final int DRAG_START = 25; // 0x19
+    field public static final int FLAG_IGNORE_VIEW_SETTING = 1; // 0x1
+    field public static final int GESTURE_END = 13; // 0xd
+    field public static final int GESTURE_START = 12; // 0xc
+    field public static final int GESTURE_THRESHOLD_ACTIVATE = 23; // 0x17
+    field public static final int GESTURE_THRESHOLD_DEACTIVATE = 24; // 0x18
+    field public static final int KEYBOARD_PRESS = 3; // 0x3
+    field public static final int KEYBOARD_RELEASE = 7; // 0x7
+    field public static final int KEYBOARD_TAP = 3; // 0x3
+    field public static final int LONG_PRESS = 0; // 0x0
+    field public static final int NO_HAPTICS = -1; // 0xffffffff
+    field public static final int REJECT = 17; // 0x11
+    field public static final int SEGMENT_FREQUENT_TICK = 27; // 0x1b
+    field public static final int SEGMENT_TICK = 26; // 0x1a
+    field public static final int TEXT_HANDLE_MOVE = 9; // 0x9
+    field public static final int TOGGLE_OFF = 22; // 0x16
+    field public static final int TOGGLE_ON = 21; // 0x15
+    field public static final int VIRTUAL_KEY = 1; // 0x1
+    field public static final int VIRTUAL_KEY_RELEASE = 8; // 0x8
+  }
+
+  public final class InputDeviceCompat {
+    field public static final int SOURCE_ANY = -256; // 0xffffff00
+    field public static final int SOURCE_CLASS_BUTTON = 1; // 0x1
+    field public static final int SOURCE_CLASS_JOYSTICK = 16; // 0x10
+    field public static final int SOURCE_CLASS_MASK = 255; // 0xff
+    field public static final int SOURCE_CLASS_NONE = 0; // 0x0
+    field public static final int SOURCE_CLASS_POINTER = 2; // 0x2
+    field public static final int SOURCE_CLASS_POSITION = 8; // 0x8
+    field public static final int SOURCE_CLASS_TRACKBALL = 4; // 0x4
+    field public static final int SOURCE_DPAD = 513; // 0x201
+    field public static final int SOURCE_GAMEPAD = 1025; // 0x401
+    field public static final int SOURCE_HDMI = 33554433; // 0x2000001
+    field public static final int SOURCE_JOYSTICK = 16777232; // 0x1000010
+    field public static final int SOURCE_KEYBOARD = 257; // 0x101
+    field public static final int SOURCE_MOUSE = 8194; // 0x2002
+    field public static final int SOURCE_ROTARY_ENCODER = 4194304; // 0x400000
+    field public static final int SOURCE_STYLUS = 16386; // 0x4002
+    field public static final int SOURCE_TOUCHPAD = 1048584; // 0x100008
+    field public static final int SOURCE_TOUCHSCREEN = 4098; // 0x1002
+    field public static final int SOURCE_TOUCH_NAVIGATION = 2097152; // 0x200000
+    field public static final int SOURCE_TRACKBALL = 65540; // 0x10004
+    field public static final int SOURCE_UNKNOWN = 0; // 0x0
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class KeyEventDispatcher {
+    method public static boolean dispatchBeforeHierarchy(android.view.View, android.view.KeyEvent);
+    method public static boolean dispatchKeyEvent(androidx.core.view.KeyEventDispatcher.Component, android.view.View?, android.view.Window.Callback?, android.view.KeyEvent);
+  }
+
+  public static interface KeyEventDispatcher.Component {
+    method public boolean superDispatchKeyEvent(android.view.KeyEvent);
+  }
+
+  public final class LayoutInflaterCompat {
+    method @Deprecated public static androidx.core.view.LayoutInflaterFactory! getFactory(android.view.LayoutInflater!);
+    method @Deprecated public static void setFactory(android.view.LayoutInflater, androidx.core.view.LayoutInflaterFactory);
+    method public static void setFactory2(android.view.LayoutInflater, android.view.LayoutInflater.Factory2);
+  }
+
+  @Deprecated public interface LayoutInflaterFactory {
+    method @Deprecated public android.view.View! onCreateView(android.view.View!, String!, android.content.Context!, android.util.AttributeSet!);
+  }
+
+  public final class MarginLayoutParamsCompat {
+    method public static int getLayoutDirection(android.view.ViewGroup.MarginLayoutParams);
+    method public static int getMarginEnd(android.view.ViewGroup.MarginLayoutParams);
+    method public static int getMarginStart(android.view.ViewGroup.MarginLayoutParams);
+    method public static boolean isMarginRelative(android.view.ViewGroup.MarginLayoutParams);
+    method public static void resolveLayoutDirection(android.view.ViewGroup.MarginLayoutParams, int);
+    method public static void setLayoutDirection(android.view.ViewGroup.MarginLayoutParams, int);
+    method public static void setMarginEnd(android.view.ViewGroup.MarginLayoutParams, int);
+    method public static void setMarginStart(android.view.ViewGroup.MarginLayoutParams, int);
+  }
+
+  public final class MenuCompat {
+    method public static void setGroupDividerEnabled(android.view.Menu, boolean);
+    method @Deprecated public static void setShowAsAction(android.view.MenuItem!, int);
+  }
+
+  public interface MenuHost {
+    method public void addMenuProvider(androidx.core.view.MenuProvider);
+    method public void addMenuProvider(androidx.core.view.MenuProvider, androidx.lifecycle.LifecycleOwner);
+    method public void addMenuProvider(androidx.core.view.MenuProvider, androidx.lifecycle.LifecycleOwner, androidx.lifecycle.Lifecycle.State);
+    method public void invalidateMenu();
+    method public void removeMenuProvider(androidx.core.view.MenuProvider);
+  }
+
+  public class MenuHostHelper {
+    ctor public MenuHostHelper(Runnable);
+    method public void addMenuProvider(androidx.core.view.MenuProvider);
+    method public void addMenuProvider(androidx.core.view.MenuProvider, androidx.lifecycle.LifecycleOwner);
+    method public void addMenuProvider(androidx.core.view.MenuProvider, androidx.lifecycle.LifecycleOwner, androidx.lifecycle.Lifecycle.State);
+    method public void onCreateMenu(android.view.Menu, android.view.MenuInflater);
+    method public void onMenuClosed(android.view.Menu);
+    method public boolean onMenuItemSelected(android.view.MenuItem);
+    method public void onPrepareMenu(android.view.Menu);
+    method public void removeMenuProvider(androidx.core.view.MenuProvider);
+  }
+
+  public final class MenuItemCompat {
+    method @Deprecated public static boolean collapseActionView(android.view.MenuItem!);
+    method @Deprecated public static boolean expandActionView(android.view.MenuItem!);
+    method public static androidx.core.view.ActionProvider? getActionProvider(android.view.MenuItem);
+    method @Deprecated public static android.view.View! getActionView(android.view.MenuItem!);
+    method public static int getAlphabeticModifiers(android.view.MenuItem);
+    method public static CharSequence? getContentDescription(android.view.MenuItem);
+    method public static android.content.res.ColorStateList? getIconTintList(android.view.MenuItem);
+    method public static android.graphics.PorterDuff.Mode? getIconTintMode(android.view.MenuItem);
+    method public static int getNumericModifiers(android.view.MenuItem);
+    method public static CharSequence? getTooltipText(android.view.MenuItem);
+    method @Deprecated public static boolean isActionViewExpanded(android.view.MenuItem!);
+    method public static android.view.MenuItem? setActionProvider(android.view.MenuItem, androidx.core.view.ActionProvider?);
+    method @Deprecated public static android.view.MenuItem! setActionView(android.view.MenuItem!, android.view.View!);
+    method @Deprecated public static android.view.MenuItem! setActionView(android.view.MenuItem!, int);
+    method public static void setAlphabeticShortcut(android.view.MenuItem, char, int);
+    method public static void setContentDescription(android.view.MenuItem, CharSequence?);
+    method public static void setIconTintList(android.view.MenuItem, android.content.res.ColorStateList?);
+    method public static void setIconTintMode(android.view.MenuItem, android.graphics.PorterDuff.Mode?);
+    method public static void setNumericShortcut(android.view.MenuItem, char, int);
+    method @Deprecated public static android.view.MenuItem! setOnActionExpandListener(android.view.MenuItem!, androidx.core.view.MenuItemCompat.OnActionExpandListener!);
+    method public static void setShortcut(android.view.MenuItem, char, char, int, int);
+    method @Deprecated public static void setShowAsAction(android.view.MenuItem!, int);
+    method public static void setTooltipText(android.view.MenuItem, CharSequence?);
+    field @Deprecated public static final int SHOW_AS_ACTION_ALWAYS = 2; // 0x2
+    field @Deprecated public static final int SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = 8; // 0x8
+    field @Deprecated public static final int SHOW_AS_ACTION_IF_ROOM = 1; // 0x1
+    field @Deprecated public static final int SHOW_AS_ACTION_NEVER = 0; // 0x0
+    field @Deprecated public static final int SHOW_AS_ACTION_WITH_TEXT = 4; // 0x4
+  }
+
+  @Deprecated public static interface MenuItemCompat.OnActionExpandListener {
+    method @Deprecated public boolean onMenuItemActionCollapse(android.view.MenuItem!);
+    method @Deprecated public boolean onMenuItemActionExpand(android.view.MenuItem!);
+  }
+
+  public interface MenuProvider {
+    method public void onCreateMenu(android.view.Menu, android.view.MenuInflater);
+    method public default void onMenuClosed(android.view.Menu);
+    method public boolean onMenuItemSelected(android.view.MenuItem);
+    method public default void onPrepareMenu(android.view.Menu);
+  }
+
+  public final class MotionEventCompat {
+    method @Deprecated public static int findPointerIndex(android.view.MotionEvent!, int);
+    method @Deprecated public static int getActionIndex(android.view.MotionEvent!);
+    method @Deprecated public static int getActionMasked(android.view.MotionEvent!);
+    method @Deprecated public static float getAxisValue(android.view.MotionEvent!, int);
+    method @Deprecated public static float getAxisValue(android.view.MotionEvent!, int, int);
+    method @Deprecated public static int getButtonState(android.view.MotionEvent!);
+    method @Deprecated public static int getPointerCount(android.view.MotionEvent!);
+    method @Deprecated public static int getPointerId(android.view.MotionEvent!, int);
+    method @Deprecated public static int getSource(android.view.MotionEvent!);
+    method @Deprecated public static float getX(android.view.MotionEvent!, int);
+    method @Deprecated public static float getY(android.view.MotionEvent!, int);
+    method public static boolean isFromSource(android.view.MotionEvent, int);
+    field @Deprecated public static final int ACTION_HOVER_ENTER = 9; // 0x9
+    field @Deprecated public static final int ACTION_HOVER_EXIT = 10; // 0xa
+    field @Deprecated public static final int ACTION_HOVER_MOVE = 7; // 0x7
+    field @Deprecated public static final int ACTION_MASK = 255; // 0xff
+    field @Deprecated public static final int ACTION_POINTER_DOWN = 5; // 0x5
+    field @Deprecated public static final int ACTION_POINTER_INDEX_MASK = 65280; // 0xff00
+    field @Deprecated public static final int ACTION_POINTER_INDEX_SHIFT = 8; // 0x8
+    field @Deprecated public static final int ACTION_POINTER_UP = 6; // 0x6
+    field @Deprecated public static final int ACTION_SCROLL = 8; // 0x8
+    field @Deprecated public static final int AXIS_BRAKE = 23; // 0x17
+    field @Deprecated public static final int AXIS_DISTANCE = 24; // 0x18
+    field @Deprecated public static final int AXIS_GAS = 22; // 0x16
+    field @Deprecated public static final int AXIS_GENERIC_1 = 32; // 0x20
+    field @Deprecated public static final int AXIS_GENERIC_10 = 41; // 0x29
+    field @Deprecated public static final int AXIS_GENERIC_11 = 42; // 0x2a
+    field @Deprecated public static final int AXIS_GENERIC_12 = 43; // 0x2b
+    field @Deprecated public static final int AXIS_GENERIC_13 = 44; // 0x2c
+    field @Deprecated public static final int AXIS_GENERIC_14 = 45; // 0x2d
+    field @Deprecated public static final int AXIS_GENERIC_15 = 46; // 0x2e
+    field @Deprecated public static final int AXIS_GENERIC_16 = 47; // 0x2f
+    field @Deprecated public static final int AXIS_GENERIC_2 = 33; // 0x21
+    field @Deprecated public static final int AXIS_GENERIC_3 = 34; // 0x22
+    field @Deprecated public static final int AXIS_GENERIC_4 = 35; // 0x23
+    field @Deprecated public static final int AXIS_GENERIC_5 = 36; // 0x24
+    field @Deprecated public static final int AXIS_GENERIC_6 = 37; // 0x25
+    field @Deprecated public static final int AXIS_GENERIC_7 = 38; // 0x26
+    field @Deprecated public static final int AXIS_GENERIC_8 = 39; // 0x27
+    field @Deprecated public static final int AXIS_GENERIC_9 = 40; // 0x28
+    field @Deprecated public static final int AXIS_HAT_X = 15; // 0xf
+    field @Deprecated public static final int AXIS_HAT_Y = 16; // 0x10
+    field @Deprecated public static final int AXIS_HSCROLL = 10; // 0xa
+    field @Deprecated public static final int AXIS_LTRIGGER = 17; // 0x11
+    field @Deprecated public static final int AXIS_ORIENTATION = 8; // 0x8
+    field @Deprecated public static final int AXIS_PRESSURE = 2; // 0x2
+    field public static final int AXIS_RELATIVE_X = 27; // 0x1b
+    field public static final int AXIS_RELATIVE_Y = 28; // 0x1c
+    field @Deprecated public static final int AXIS_RTRIGGER = 18; // 0x12
+    field @Deprecated public static final int AXIS_RUDDER = 20; // 0x14
+    field @Deprecated public static final int AXIS_RX = 12; // 0xc
+    field @Deprecated public static final int AXIS_RY = 13; // 0xd
+    field @Deprecated public static final int AXIS_RZ = 14; // 0xe
+    field public static final int AXIS_SCROLL = 26; // 0x1a
+    field @Deprecated public static final int AXIS_SIZE = 3; // 0x3
+    field @Deprecated public static final int AXIS_THROTTLE = 19; // 0x13
+    field @Deprecated public static final int AXIS_TILT = 25; // 0x19
+    field @Deprecated public static final int AXIS_TOOL_MAJOR = 6; // 0x6
+    field @Deprecated public static final int AXIS_TOOL_MINOR = 7; // 0x7
+    field @Deprecated public static final int AXIS_TOUCH_MAJOR = 4; // 0x4
+    field @Deprecated public static final int AXIS_TOUCH_MINOR = 5; // 0x5
+    field @Deprecated public static final int AXIS_VSCROLL = 9; // 0x9
+    field @Deprecated public static final int AXIS_WHEEL = 21; // 0x15
+    field @Deprecated public static final int AXIS_X = 0; // 0x0
+    field @Deprecated public static final int AXIS_Y = 1; // 0x1
+    field @Deprecated public static final int AXIS_Z = 11; // 0xb
+    field @Deprecated public static final int BUTTON_PRIMARY = 1; // 0x1
+  }
+
+  public interface NestedScrollingChild {
+    method public boolean dispatchNestedFling(float, float, boolean);
+    method public boolean dispatchNestedPreFling(float, float);
+    method public boolean dispatchNestedPreScroll(int, int, int[]?, int[]?);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]?);
+    method public boolean hasNestedScrollingParent();
+    method public boolean isNestedScrollingEnabled();
+    method public void setNestedScrollingEnabled(boolean);
+    method public boolean startNestedScroll(@androidx.core.view.ViewCompat.ScrollAxis int);
+    method public void stopNestedScroll();
+  }
+
+  public interface NestedScrollingChild2 extends androidx.core.view.NestedScrollingChild {
+    method public boolean dispatchNestedPreScroll(int, int, int[]?, int[]?, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]?, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public boolean hasNestedScrollingParent(@androidx.core.view.ViewCompat.NestedScrollType int);
+    method public boolean startNestedScroll(@androidx.core.view.ViewCompat.ScrollAxis int, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public void stopNestedScroll(@androidx.core.view.ViewCompat.NestedScrollType int);
+  }
+
+  public interface NestedScrollingChild3 extends androidx.core.view.NestedScrollingChild2 {
+    method public void dispatchNestedScroll(int, int, int, int, int[]?, @androidx.core.view.ViewCompat.NestedScrollType int, int[]);
+  }
+
+  public class NestedScrollingChildHelper {
+    ctor public NestedScrollingChildHelper(android.view.View);
+    method public boolean dispatchNestedFling(float, float, boolean);
+    method public boolean dispatchNestedPreFling(float, float);
+    method public boolean dispatchNestedPreScroll(int, int, int[]?, int[]?);
+    method public boolean dispatchNestedPreScroll(int, int, int[]?, int[]?, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]?);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]?, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public void dispatchNestedScroll(int, int, int, int, int[]?, @androidx.core.view.ViewCompat.NestedScrollType int, int[]?);
+    method public boolean hasNestedScrollingParent();
+    method public boolean hasNestedScrollingParent(@androidx.core.view.ViewCompat.NestedScrollType int);
+    method public boolean isNestedScrollingEnabled();
+    method public void onDetachedFromWindow();
+    method public void onStopNestedScroll(android.view.View);
+    method public void setNestedScrollingEnabled(boolean);
+    method public boolean startNestedScroll(@androidx.core.view.ViewCompat.ScrollAxis int);
+    method public boolean startNestedScroll(@androidx.core.view.ViewCompat.ScrollAxis int, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public void stopNestedScroll();
+    method public void stopNestedScroll(@androidx.core.view.ViewCompat.NestedScrollType int);
+  }
+
+  public interface NestedScrollingParent {
+    method @androidx.core.view.ViewCompat.ScrollAxis public int getNestedScrollAxes();
+    method public boolean onNestedFling(android.view.View, float, float, boolean);
+    method public boolean onNestedPreFling(android.view.View, float, float);
+    method public void onNestedPreScroll(android.view.View, int, int, int[]);
+    method public void onNestedScroll(android.view.View, int, int, int, int);
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, @androidx.core.view.ViewCompat.ScrollAxis int);
+    method public boolean onStartNestedScroll(android.view.View, android.view.View, @androidx.core.view.ViewCompat.ScrollAxis int);
+    method public void onStopNestedScroll(android.view.View);
+  }
+
+  public interface NestedScrollingParent2 extends androidx.core.view.NestedScrollingParent {
+    method public void onNestedPreScroll(android.view.View, int, int, int[], @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public void onNestedScroll(android.view.View, int, int, int, int, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, @androidx.core.view.ViewCompat.ScrollAxis int, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public boolean onStartNestedScroll(android.view.View, android.view.View, @androidx.core.view.ViewCompat.ScrollAxis int, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public void onStopNestedScroll(android.view.View, @androidx.core.view.ViewCompat.NestedScrollType int);
+  }
+
+  public interface NestedScrollingParent3 extends androidx.core.view.NestedScrollingParent2 {
+    method public void onNestedScroll(android.view.View, int, int, int, int, @androidx.core.view.ViewCompat.NestedScrollType int, int[]);
+  }
+
+  public class NestedScrollingParentHelper {
+    ctor public NestedScrollingParentHelper(android.view.ViewGroup);
+    method @androidx.core.view.ViewCompat.ScrollAxis public int getNestedScrollAxes();
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, @androidx.core.view.ViewCompat.ScrollAxis int);
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, @androidx.core.view.ViewCompat.ScrollAxis int, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public void onStopNestedScroll(android.view.View);
+    method public void onStopNestedScroll(android.view.View, @androidx.core.view.ViewCompat.NestedScrollType int);
+  }
+
+  public interface OnApplyWindowInsetsListener {
+    method public androidx.core.view.WindowInsetsCompat onApplyWindowInsets(android.view.View, androidx.core.view.WindowInsetsCompat);
+  }
+
+  public interface OnReceiveContentListener {
+    method public androidx.core.view.ContentInfoCompat? onReceiveContent(android.view.View, androidx.core.view.ContentInfoCompat);
+  }
+
+  public interface OnReceiveContentViewBehavior {
+    method public androidx.core.view.ContentInfoCompat? onReceiveContent(androidx.core.view.ContentInfoCompat);
+  }
+
+  public final class OneShotPreDrawListener implements android.view.View.OnAttachStateChangeListener android.view.ViewTreeObserver.OnPreDrawListener {
+    method public static androidx.core.view.OneShotPreDrawListener add(android.view.View, Runnable);
+    method public boolean onPreDraw();
+    method public void onViewAttachedToWindow(android.view.View);
+    method public void onViewDetachedFromWindow(android.view.View);
+    method public void removeListener();
+  }
+
+  public final class PointerIconCompat {
+    method public static androidx.core.view.PointerIconCompat create(android.graphics.Bitmap, float, float);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public Object? getPointerIcon();
+    method public static androidx.core.view.PointerIconCompat getSystemIcon(android.content.Context, int);
+    method public static androidx.core.view.PointerIconCompat load(android.content.res.Resources, int);
+    field public static final int TYPE_ALIAS = 1010; // 0x3f2
+    field public static final int TYPE_ALL_SCROLL = 1013; // 0x3f5
+    field public static final int TYPE_ARROW = 1000; // 0x3e8
+    field public static final int TYPE_CELL = 1006; // 0x3ee
+    field public static final int TYPE_CONTEXT_MENU = 1001; // 0x3e9
+    field public static final int TYPE_COPY = 1011; // 0x3f3
+    field public static final int TYPE_CROSSHAIR = 1007; // 0x3ef
+    field public static final int TYPE_DEFAULT = 1000; // 0x3e8
+    field public static final int TYPE_GRAB = 1020; // 0x3fc
+    field public static final int TYPE_GRABBING = 1021; // 0x3fd
+    field public static final int TYPE_HAND = 1002; // 0x3ea
+    field public static final int TYPE_HELP = 1003; // 0x3eb
+    field public static final int TYPE_HORIZONTAL_DOUBLE_ARROW = 1014; // 0x3f6
+    field public static final int TYPE_NO_DROP = 1012; // 0x3f4
+    field public static final int TYPE_NULL = 0; // 0x0
+    field public static final int TYPE_TEXT = 1008; // 0x3f0
+    field public static final int TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW = 1017; // 0x3f9
+    field public static final int TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW = 1016; // 0x3f8
+    field public static final int TYPE_VERTICAL_DOUBLE_ARROW = 1015; // 0x3f7
+    field public static final int TYPE_VERTICAL_TEXT = 1009; // 0x3f1
+    field public static final int TYPE_WAIT = 1004; // 0x3ec
+    field public static final int TYPE_ZOOM_IN = 1018; // 0x3fa
+    field public static final int TYPE_ZOOM_OUT = 1019; // 0x3fb
+  }
+
+  public final class ScaleGestureDetectorCompat {
+    method public static boolean isQuickScaleEnabled(android.view.ScaleGestureDetector);
+    method @Deprecated public static boolean isQuickScaleEnabled(Object!);
+    method public static void setQuickScaleEnabled(android.view.ScaleGestureDetector, boolean);
+    method @Deprecated public static void setQuickScaleEnabled(Object!, boolean);
+  }
+
+  public interface ScrollingView {
+    method public int computeHorizontalScrollExtent();
+    method public int computeHorizontalScrollOffset();
+    method public int computeHorizontalScrollRange();
+    method public int computeVerticalScrollExtent();
+    method public int computeVerticalScrollOffset();
+    method public int computeVerticalScrollRange();
+  }
+
+  public final class SoftwareKeyboardControllerCompat {
+    ctor public SoftwareKeyboardControllerCompat(android.view.View);
+    method public void hide();
+    method public void show();
+  }
+
+  public interface TintableBackgroundView {
+    method public android.content.res.ColorStateList? getSupportBackgroundTintList();
+    method public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();
+    method public void setSupportBackgroundTintList(android.content.res.ColorStateList?);
+    method public void setSupportBackgroundTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  public final class VelocityTrackerCompat {
+    method public static float getAxisVelocity(android.view.VelocityTracker, @androidx.core.view.VelocityTrackerCompat.VelocityTrackableMotionEventAxis int);
+    method public static float getAxisVelocity(android.view.VelocityTracker, @androidx.core.view.VelocityTrackerCompat.VelocityTrackableMotionEventAxis int, int);
+    method @Deprecated public static float getXVelocity(android.view.VelocityTracker!, int);
+    method @Deprecated public static float getYVelocity(android.view.VelocityTracker!, int);
+    method public static boolean isAxisSupported(android.view.VelocityTracker, @androidx.core.view.VelocityTrackerCompat.VelocityTrackableMotionEventAxis int);
+  }
+
+  @IntDef({android.view.MotionEvent.AXIS_X, android.view.MotionEvent.AXIS_Y, android.view.MotionEvent.AXIS_SCROLL}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface VelocityTrackerCompat.VelocityTrackableMotionEventAxis {
+  }
+
+  public class ViewCompat {
+    ctor @Deprecated protected ViewCompat();
+    method public static int addAccessibilityAction(android.view.View, CharSequence, androidx.core.view.accessibility.AccessibilityViewCommand);
+    method public static void addKeyboardNavigationClusters(android.view.View, java.util.Collection<android.view.View!>, int);
+    method public static void addOnUnhandledKeyEventListener(android.view.View, androidx.core.view.ViewCompat.OnUnhandledKeyEventListenerCompat);
+    method public static androidx.core.view.ViewPropertyAnimatorCompat animate(android.view.View);
+    method @Deprecated public static boolean canScrollHorizontally(android.view.View!, int);
+    method @Deprecated public static boolean canScrollVertically(android.view.View!, int);
+    method public static void cancelDragAndDrop(android.view.View);
+    method @Deprecated public static int combineMeasuredStates(int, int);
+    method public static androidx.core.view.WindowInsetsCompat computeSystemWindowInsets(android.view.View, androidx.core.view.WindowInsetsCompat, android.graphics.Rect);
+    method public static androidx.core.view.WindowInsetsCompat dispatchApplyWindowInsets(android.view.View, androidx.core.view.WindowInsetsCompat);
+    method public static void dispatchFinishTemporaryDetach(android.view.View);
+    method public static boolean dispatchNestedFling(android.view.View, float, float, boolean);
+    method public static boolean dispatchNestedPreFling(android.view.View, float, float);
+    method public static boolean dispatchNestedPreScroll(android.view.View, int, int, int[]?, int[]?);
+    method public static boolean dispatchNestedPreScroll(android.view.View, int, int, int[]?, int[]?, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public static boolean dispatchNestedScroll(android.view.View, int, int, int, int, int[]?);
+    method public static boolean dispatchNestedScroll(android.view.View, int, int, int, int, int[]?, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public static void dispatchNestedScroll(android.view.View, int, int, int, int, int[]?, @androidx.core.view.ViewCompat.NestedScrollType int, int[]);
+    method public static void dispatchStartTemporaryDetach(android.view.View);
+    method public static void enableAccessibleClickableSpanSupport(android.view.View);
+    method public static int generateViewId();
+    method public static androidx.core.view.AccessibilityDelegateCompat? getAccessibilityDelegate(android.view.View);
+    method public static int getAccessibilityLiveRegion(android.view.View);
+    method public static androidx.core.view.accessibility.AccessibilityNodeProviderCompat? getAccessibilityNodeProvider(android.view.View);
+    method @UiThread public static CharSequence? getAccessibilityPaneTitle(android.view.View);
+    method @Deprecated public static float getAlpha(android.view.View!);
+    method public static androidx.core.view.autofill.AutofillIdCompat? getAutofillId(android.view.View);
+    method public static android.content.res.ColorStateList? getBackgroundTintList(android.view.View);
+    method public static android.graphics.PorterDuff.Mode? getBackgroundTintMode(android.view.View);
+    method public static android.graphics.Rect? getClipBounds(android.view.View);
+    method public static androidx.core.view.contentcapture.ContentCaptureSessionCompat? getContentCaptureSession(android.view.View);
+    method public static android.view.Display? getDisplay(android.view.View);
+    method public static float getElevation(android.view.View);
+    method public static boolean getFitsSystemWindows(android.view.View);
+    method public static int getImportantForAccessibility(android.view.View);
+    method public static int getImportantForAutofill(android.view.View);
+    method public static int getImportantForContentCapture(android.view.View);
+    method public static int getLabelFor(android.view.View);
+    method @Deprecated public static int getLayerType(android.view.View!);
+    method public static int getLayoutDirection(android.view.View);
+    method @Deprecated public static android.graphics.Matrix? getMatrix(android.view.View!);
+    method @Deprecated public static int getMeasuredHeightAndState(android.view.View!);
+    method @Deprecated public static int getMeasuredState(android.view.View!);
+    method @Deprecated public static int getMeasuredWidthAndState(android.view.View!);
+    method public static int getMinimumHeight(android.view.View);
+    method public static int getMinimumWidth(android.view.View);
+    method public static int getNextClusterForwardId(android.view.View);
+    method public static String![]? getOnReceiveContentMimeTypes(android.view.View);
+    method @Deprecated public static int getOverScrollMode(android.view.View!);
+    method @Px public static int getPaddingEnd(android.view.View);
+    method @Px public static int getPaddingStart(android.view.View);
+    method public static android.view.ViewParent? getParentForAccessibility(android.view.View);
+    method @Deprecated public static float getPivotX(android.view.View!);
+    method @Deprecated public static float getPivotY(android.view.View!);
+    method public static androidx.core.view.WindowInsetsCompat? getRootWindowInsets(android.view.View);
+    method @Deprecated public static float getRotation(android.view.View!);
+    method @Deprecated public static float getRotationX(android.view.View!);
+    method @Deprecated public static float getRotationY(android.view.View!);
+    method @Deprecated public static float getScaleX(android.view.View!);
+    method @Deprecated public static float getScaleY(android.view.View!);
+    method public static int getScrollIndicators(android.view.View);
+    method @UiThread public static CharSequence? getStateDescription(android.view.View);
+    method public static java.util.List<android.graphics.Rect!> getSystemGestureExclusionRects(android.view.View);
+    method public static String? getTransitionName(android.view.View);
+    method @Deprecated public static float getTranslationX(android.view.View!);
+    method @Deprecated public static float getTranslationY(android.view.View!);
+    method public static float getTranslationZ(android.view.View);
+    method @Deprecated public static androidx.core.view.WindowInsetsControllerCompat? getWindowInsetsController(android.view.View);
+    method @Deprecated public static int getWindowSystemUiVisibility(android.view.View);
+    method @Deprecated public static float getX(android.view.View!);
+    method @Deprecated public static float getY(android.view.View!);
+    method public static float getZ(android.view.View);
+    method public static boolean hasAccessibilityDelegate(android.view.View);
+    method public static boolean hasExplicitFocusable(android.view.View);
+    method public static boolean hasNestedScrollingParent(android.view.View);
+    method public static boolean hasNestedScrollingParent(android.view.View, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public static boolean hasOnClickListeners(android.view.View);
+    method public static boolean hasOverlappingRendering(android.view.View);
+    method public static boolean hasTransientState(android.view.View);
+    method @UiThread public static boolean isAccessibilityHeading(android.view.View);
+    method public static boolean isAttachedToWindow(android.view.View);
+    method public static boolean isFocusedByDefault(android.view.View);
+    method public static boolean isImportantForAccessibility(android.view.View);
+    method public static boolean isImportantForAutofill(android.view.View);
+    method public static boolean isImportantForContentCapture(android.view.View);
+    method public static boolean isInLayout(android.view.View);
+    method public static boolean isKeyboardNavigationCluster(android.view.View);
+    method public static boolean isLaidOut(android.view.View);
+    method public static boolean isLayoutDirectionResolved(android.view.View);
+    method public static boolean isNestedScrollingEnabled(android.view.View);
+    method @Deprecated public static boolean isOpaque(android.view.View!);
+    method public static boolean isPaddingRelative(android.view.View);
+    method @UiThread public static boolean isScreenReaderFocusable(android.view.View);
+    method @Deprecated public static void jumpDrawablesToCurrentState(android.view.View!);
+    method public static android.view.View? keyboardNavigationClusterSearch(android.view.View, android.view.View?, @androidx.core.view.ViewCompat.FocusDirection int);
+    method public static void offsetLeftAndRight(android.view.View, int);
+    method public static void offsetTopAndBottom(android.view.View, int);
+    method public static androidx.core.view.WindowInsetsCompat onApplyWindowInsets(android.view.View, androidx.core.view.WindowInsetsCompat);
+    method @Deprecated public static void onInitializeAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
+    method public static void onInitializeAccessibilityNodeInfo(android.view.View, androidx.core.view.accessibility.AccessibilityNodeInfoCompat);
+    method @Deprecated public static void onPopulateAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
+    method public static boolean performAccessibilityAction(android.view.View, int, android.os.Bundle?);
+    method public static boolean performHapticFeedback(android.view.View, int);
+    method public static boolean performHapticFeedback(android.view.View, int, int);
+    method public static androidx.core.view.ContentInfoCompat? performReceiveContent(android.view.View, androidx.core.view.ContentInfoCompat);
+    method public static void postInvalidateOnAnimation(android.view.View);
+    method public static void postInvalidateOnAnimation(android.view.View, int, int, int, int);
+    method public static void postOnAnimation(android.view.View, Runnable);
+    method public static void postOnAnimationDelayed(android.view.View, Runnable, long);
+    method public static void removeAccessibilityAction(android.view.View, int);
+    method public static void removeOnUnhandledKeyEventListener(android.view.View, androidx.core.view.ViewCompat.OnUnhandledKeyEventListenerCompat);
+    method public static void replaceAccessibilityAction(android.view.View, androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat, CharSequence?, androidx.core.view.accessibility.AccessibilityViewCommand?);
+    method public static void requestApplyInsets(android.view.View);
+    method public static <T extends android.view.View> T requireViewById(android.view.View, @IdRes int);
+    method @Deprecated public static int resolveSizeAndState(int, int, int);
+    method public static boolean restoreDefaultFocus(android.view.View);
+    method public static void saveAttributeDataForStyleable(android.view.View, android.content.Context, int[], android.util.AttributeSet?, android.content.res.TypedArray, int, int);
+    method public static void setAccessibilityDelegate(android.view.View, androidx.core.view.AccessibilityDelegateCompat?);
+    method @UiThread public static void setAccessibilityHeading(android.view.View, boolean);
+    method public static void setAccessibilityLiveRegion(android.view.View, int);
+    method @UiThread public static void setAccessibilityPaneTitle(android.view.View, CharSequence?);
+    method @Deprecated public static void setActivated(android.view.View!, boolean);
+    method @Deprecated public static void setAlpha(android.view.View!, @FloatRange(from=0.0, to=1.0) float);
+    method public static void setAutofillHints(android.view.View, java.lang.String!...);
+    method public static void setAutofillId(android.view.View, androidx.core.view.autofill.AutofillIdCompat?);
+    method public static void setBackground(android.view.View, android.graphics.drawable.Drawable?);
+    method public static void setBackgroundTintList(android.view.View, android.content.res.ColorStateList?);
+    method public static void setBackgroundTintMode(android.view.View, android.graphics.PorterDuff.Mode?);
+    method @Deprecated public static void setChildrenDrawingOrderEnabled(android.view.ViewGroup!, boolean);
+    method public static void setClipBounds(android.view.View, android.graphics.Rect?);
+    method public static void setContentCaptureSession(android.view.View, androidx.core.view.contentcapture.ContentCaptureSessionCompat?);
+    method public static void setElevation(android.view.View, float);
+    method @Deprecated public static void setFitsSystemWindows(android.view.View!, boolean);
+    method public static void setFocusedByDefault(android.view.View, boolean);
+    method public static void setHasTransientState(android.view.View, boolean);
+    method @UiThread public static void setImportantForAccessibility(android.view.View, int);
+    method public static void setImportantForAutofill(android.view.View, int);
+    method public static void setImportantForContentCapture(android.view.View, int);
+    method public static void setKeyboardNavigationCluster(android.view.View, boolean);
+    method public static void setLabelFor(android.view.View, @IdRes int);
+    method public static void setLayerPaint(android.view.View, android.graphics.Paint?);
+    method @Deprecated public static void setLayerType(android.view.View!, int, android.graphics.Paint!);
+    method public static void setLayoutDirection(android.view.View, int);
+    method public static void setNestedScrollingEnabled(android.view.View, boolean);
+    method public static void setNextClusterForwardId(android.view.View, int);
+    method public static void setOnApplyWindowInsetsListener(android.view.View, androidx.core.view.OnApplyWindowInsetsListener?);
+    method public static void setOnReceiveContentListener(android.view.View, String![]?, androidx.core.view.OnReceiveContentListener?);
+    method @Deprecated public static void setOverScrollMode(android.view.View!, int);
+    method public static void setPaddingRelative(android.view.View, @Px int, @Px int, @Px int, @Px int);
+    method @Deprecated public static void setPivotX(android.view.View!, float);
+    method @Deprecated public static void setPivotY(android.view.View!, float);
+    method public static void setPointerIcon(android.view.View, androidx.core.view.PointerIconCompat?);
+    method @Deprecated public static void setRotation(android.view.View!, float);
+    method @Deprecated public static void setRotationX(android.view.View!, float);
+    method @Deprecated public static void setRotationY(android.view.View!, float);
+    method @Deprecated public static void setSaveFromParentEnabled(android.view.View!, boolean);
+    method @Deprecated public static void setScaleX(android.view.View!, float);
+    method @Deprecated public static void setScaleY(android.view.View!, float);
+    method @UiThread public static void setScreenReaderFocusable(android.view.View, boolean);
+    method public static void setScrollIndicators(android.view.View, @androidx.core.view.ViewCompat.ScrollIndicators int);
+    method public static void setScrollIndicators(android.view.View, @androidx.core.view.ViewCompat.ScrollIndicators int, @androidx.core.view.ViewCompat.ScrollIndicators int);
+    method @UiThread public static void setStateDescription(android.view.View, CharSequence?);
+    method public static void setSystemGestureExclusionRects(android.view.View, java.util.List<android.graphics.Rect!>);
+    method public static void setTooltipText(android.view.View, CharSequence?);
+    method public static void setTransitionName(android.view.View, String?);
+    method @Deprecated public static void setTranslationX(android.view.View!, float);
+    method @Deprecated public static void setTranslationY(android.view.View!, float);
+    method public static void setTranslationZ(android.view.View, float);
+    method public static void setWindowInsetsAnimationCallback(android.view.View, androidx.core.view.WindowInsetsAnimationCompat.Callback?);
+    method @Deprecated public static void setX(android.view.View!, float);
+    method @Deprecated public static void setY(android.view.View!, float);
+    method public static void setZ(android.view.View, float);
+    method public static boolean startDragAndDrop(android.view.View, android.content.ClipData?, android.view.View.DragShadowBuilder, Object?, int);
+    method public static boolean startNestedScroll(android.view.View, @androidx.core.view.ViewCompat.ScrollAxis int);
+    method public static boolean startNestedScroll(android.view.View, @androidx.core.view.ViewCompat.ScrollAxis int, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public static void stopNestedScroll(android.view.View);
+    method public static void stopNestedScroll(android.view.View, @androidx.core.view.ViewCompat.NestedScrollType int);
+    method public static void updateDragShadow(android.view.View, android.view.View.DragShadowBuilder);
+    field public static final int ACCESSIBILITY_LIVE_REGION_ASSERTIVE = 2; // 0x2
+    field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
+    field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_AUTO = 0; // 0x0
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 2; // 0x2
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 4; // 0x4
+    field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
+    field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_AUTO = 0; // 0x0
+    field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO = 2; // 0x2
+    field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS = 8; // 0x8
+    field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES = 1; // 0x1
+    field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS = 4; // 0x4
+    field @Deprecated public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
+    field @Deprecated public static final int LAYER_TYPE_NONE = 0; // 0x0
+    field @Deprecated public static final int LAYER_TYPE_SOFTWARE = 1; // 0x1
+    field public static final int LAYOUT_DIRECTION_INHERIT = 2; // 0x2
+    field public static final int LAYOUT_DIRECTION_LOCALE = 3; // 0x3
+    field public static final int LAYOUT_DIRECTION_LTR = 0; // 0x0
+    field public static final int LAYOUT_DIRECTION_RTL = 1; // 0x1
+    field @Deprecated public static final int MEASURED_HEIGHT_STATE_SHIFT = 16; // 0x10
+    field @Deprecated public static final int MEASURED_SIZE_MASK = 16777215; // 0xffffff
+    field @Deprecated public static final int MEASURED_STATE_MASK = -16777216; // 0xff000000
+    field @Deprecated public static final int MEASURED_STATE_TOO_SMALL = 16777216; // 0x1000000
+    field @Deprecated public static final int OVER_SCROLL_ALWAYS = 0; // 0x0
+    field @Deprecated public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1; // 0x1
+    field @Deprecated public static final int OVER_SCROLL_NEVER = 2; // 0x2
+    field public static final int SCROLL_AXIS_HORIZONTAL = 1; // 0x1
+    field public static final int SCROLL_AXIS_NONE = 0; // 0x0
+    field public static final int SCROLL_AXIS_VERTICAL = 2; // 0x2
+    field public static final int SCROLL_INDICATOR_BOTTOM = 2; // 0x2
+    field public static final int SCROLL_INDICATOR_END = 32; // 0x20
+    field public static final int SCROLL_INDICATOR_LEFT = 4; // 0x4
+    field public static final int SCROLL_INDICATOR_RIGHT = 8; // 0x8
+    field public static final int SCROLL_INDICATOR_START = 16; // 0x10
+    field public static final int SCROLL_INDICATOR_TOP = 1; // 0x1
+    field public static final int TYPE_NON_TOUCH = 1; // 0x1
+    field public static final int TYPE_TOUCH = 0; // 0x0
+  }
+
+  @IntDef({android.view.View.FOCUS_LEFT, android.view.View.FOCUS_UP, android.view.View.FOCUS_RIGHT, android.view.View.FOCUS_DOWN, android.view.View.FOCUS_FORWARD, android.view.View.FOCUS_BACKWARD}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ViewCompat.FocusDirection {
+  }
+
+  @IntDef({android.view.View.FOCUS_LEFT, android.view.View.FOCUS_UP, android.view.View.FOCUS_RIGHT, android.view.View.FOCUS_DOWN}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ViewCompat.FocusRealDirection {
+  }
+
+  @IntDef({android.view.View.FOCUS_FORWARD, android.view.View.FOCUS_BACKWARD}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ViewCompat.FocusRelativeDirection {
+  }
+
+  @IntDef({androidx.core.view.ViewCompat.TYPE_TOUCH, androidx.core.view.ViewCompat.TYPE_NON_TOUCH}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ViewCompat.NestedScrollType {
+  }
+
+  public static interface ViewCompat.OnUnhandledKeyEventListenerCompat {
+    method public boolean onUnhandledKeyEvent(android.view.View, android.view.KeyEvent);
+  }
+
+  @IntDef(value={androidx.core.view.ViewCompat.SCROLL_AXIS_NONE, androidx.core.view.ViewCompat.SCROLL_AXIS_HORIZONTAL, androidx.core.view.ViewCompat.SCROLL_AXIS_VERTICAL}, flag=true) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ViewCompat.ScrollAxis {
+  }
+
+  @IntDef(flag=true, value={androidx.core.view.ViewCompat.SCROLL_INDICATOR_TOP, androidx.core.view.ViewCompat.SCROLL_INDICATOR_BOTTOM, androidx.core.view.ViewCompat.SCROLL_INDICATOR_LEFT, androidx.core.view.ViewCompat.SCROLL_INDICATOR_RIGHT, androidx.core.view.ViewCompat.SCROLL_INDICATOR_START, androidx.core.view.ViewCompat.SCROLL_INDICATOR_END}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ViewCompat.ScrollIndicators {
+  }
+
+  public final class ViewConfigurationCompat {
+    method public static float getScaledHorizontalScrollFactor(android.view.ViewConfiguration, android.content.Context);
+    method public static int getScaledHoverSlop(android.view.ViewConfiguration);
+    method public static int getScaledMaximumFlingVelocity(android.content.Context, android.view.ViewConfiguration, int, int, int);
+    method public static int getScaledMinimumFlingVelocity(android.content.Context, android.view.ViewConfiguration, int, int, int);
+    method @Deprecated public static int getScaledPagingTouchSlop(android.view.ViewConfiguration!);
+    method public static float getScaledVerticalScrollFactor(android.view.ViewConfiguration, android.content.Context);
+    method @Deprecated public static boolean hasPermanentMenuKey(android.view.ViewConfiguration!);
+    method public static boolean shouldShowMenuShortcutsWhenKeyboardPresent(android.view.ViewConfiguration, android.content.Context);
+  }
+
+  public final class ViewGroupCompat {
+    method public static int getLayoutMode(android.view.ViewGroup);
+    method @androidx.core.view.ViewCompat.ScrollAxis public static int getNestedScrollAxes(android.view.ViewGroup);
+    method public static boolean isTransitionGroup(android.view.ViewGroup);
+    method @Deprecated public static boolean onRequestSendAccessibilityEvent(android.view.ViewGroup!, android.view.View!, android.view.accessibility.AccessibilityEvent!);
+    method public static void setLayoutMode(android.view.ViewGroup, int);
+    method @Deprecated public static void setMotionEventSplittingEnabled(android.view.ViewGroup!, boolean);
+    method public static void setTransitionGroup(android.view.ViewGroup, boolean);
+    field public static final int LAYOUT_MODE_CLIP_BOUNDS = 0; // 0x0
+    field public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1; // 0x1
+  }
+
+  public final class ViewParentCompat {
+    method public static void notifySubtreeAccessibilityStateChanged(android.view.ViewParent, android.view.View, android.view.View, int);
+    method public static boolean onNestedFling(android.view.ViewParent, android.view.View, float, float, boolean);
+    method public static boolean onNestedPreFling(android.view.ViewParent, android.view.View, float, float);
+    method public static void onNestedPreScroll(android.view.ViewParent, android.view.View, int, int, int[]);
+    method public static void onNestedPreScroll(android.view.ViewParent, android.view.View, int, int, int[], int);
+    method public static void onNestedScroll(android.view.ViewParent, android.view.View, int, int, int, int);
+    method public static void onNestedScroll(android.view.ViewParent, android.view.View, int, int, int, int, int);
+    method public static void onNestedScroll(android.view.ViewParent, android.view.View, int, int, int, int, int, int[]);
+    method public static void onNestedScrollAccepted(android.view.ViewParent, android.view.View, android.view.View, int);
+    method public static void onNestedScrollAccepted(android.view.ViewParent, android.view.View, android.view.View, int, int);
+    method public static boolean onStartNestedScroll(android.view.ViewParent, android.view.View, android.view.View, int);
+    method public static boolean onStartNestedScroll(android.view.ViewParent, android.view.View, android.view.View, int, int);
+    method public static void onStopNestedScroll(android.view.ViewParent, android.view.View);
+    method public static void onStopNestedScroll(android.view.ViewParent, android.view.View, int);
+    method @Deprecated public static boolean requestSendAccessibilityEvent(android.view.ViewParent!, android.view.View!, android.view.accessibility.AccessibilityEvent!);
+  }
+
+  public final class ViewPropertyAnimatorCompat {
+    method public androidx.core.view.ViewPropertyAnimatorCompat alpha(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat alphaBy(float);
+    method public void cancel();
+    method public long getDuration();
+    method public android.view.animation.Interpolator? getInterpolator();
+    method public long getStartDelay();
+    method public androidx.core.view.ViewPropertyAnimatorCompat rotation(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat rotationBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat rotationX(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat rotationXBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat rotationY(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat rotationYBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat scaleX(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat scaleXBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat scaleY(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat scaleYBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat setDuration(long);
+    method public androidx.core.view.ViewPropertyAnimatorCompat setInterpolator(android.view.animation.Interpolator?);
+    method public androidx.core.view.ViewPropertyAnimatorCompat setListener(androidx.core.view.ViewPropertyAnimatorListener?);
+    method public androidx.core.view.ViewPropertyAnimatorCompat setStartDelay(long);
+    method public androidx.core.view.ViewPropertyAnimatorCompat setUpdateListener(androidx.core.view.ViewPropertyAnimatorUpdateListener?);
+    method public void start();
+    method public androidx.core.view.ViewPropertyAnimatorCompat translationX(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat translationXBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat translationY(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat translationYBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat translationZ(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat translationZBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat withEndAction(Runnable);
+    method public androidx.core.view.ViewPropertyAnimatorCompat withLayer();
+    method public androidx.core.view.ViewPropertyAnimatorCompat withStartAction(Runnable);
+    method public androidx.core.view.ViewPropertyAnimatorCompat x(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat xBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat y(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat yBy(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat z(float);
+    method public androidx.core.view.ViewPropertyAnimatorCompat zBy(float);
+  }
+
+  public interface ViewPropertyAnimatorListener {
+    method public void onAnimationCancel(android.view.View);
+    method public void onAnimationEnd(android.view.View);
+    method public void onAnimationStart(android.view.View);
+  }
+
+  public class ViewPropertyAnimatorListenerAdapter implements androidx.core.view.ViewPropertyAnimatorListener {
+    ctor public ViewPropertyAnimatorListenerAdapter();
+    method public void onAnimationCancel(android.view.View);
+    method public void onAnimationEnd(android.view.View);
+    method public void onAnimationStart(android.view.View);
+  }
+
+  public interface ViewPropertyAnimatorUpdateListener {
+    method public void onAnimationUpdate(android.view.View);
+  }
+
+  public class ViewStructureCompat {
+    method public void setClassName(String);
+    method public void setContentDescription(CharSequence);
+    method public void setDimens(int, int, int, int, int, int);
+    method public void setText(CharSequence);
+    method @RequiresApi(23) public android.view.ViewStructure toViewStructure();
+    method @RequiresApi(23) public static androidx.core.view.ViewStructureCompat toViewStructureCompat(android.view.ViewStructure);
+  }
+
+  public final class WindowCompat {
+    method public static androidx.core.view.WindowInsetsControllerCompat getInsetsController(android.view.Window, android.view.View);
+    method public static <T extends android.view.View> T requireViewById(android.view.Window, @IdRes int);
+    method public static void setDecorFitsSystemWindows(android.view.Window, boolean);
+    field public static final int FEATURE_ACTION_BAR = 8; // 0x8
+    field public static final int FEATURE_ACTION_BAR_OVERLAY = 9; // 0x9
+    field public static final int FEATURE_ACTION_MODE_OVERLAY = 10; // 0xa
+  }
+
+  public final class WindowInsetsAnimationCompat {
+    ctor public WindowInsetsAnimationCompat(@androidx.core.view.WindowInsetsCompat.Type.InsetsType int, android.view.animation.Interpolator?, long);
+    method @FloatRange(from=0.0f, to=1.0f) public float getAlpha();
+    method public long getDurationMillis();
+    method @FloatRange(from=0.0f, to=1.0f) public float getFraction();
+    method public float getInterpolatedFraction();
+    method public android.view.animation.Interpolator? getInterpolator();
+    method @androidx.core.view.WindowInsetsCompat.Type.InsetsType public int getTypeMask();
+    method public void setAlpha(@FloatRange(from=0.0f, to=1.0f) float);
+    method public void setFraction(@FloatRange(from=0.0f, to=1.0f) float);
+  }
+
+  public static final class WindowInsetsAnimationCompat.BoundsCompat {
+    ctor public WindowInsetsAnimationCompat.BoundsCompat(androidx.core.graphics.Insets, androidx.core.graphics.Insets);
+    method public androidx.core.graphics.Insets getLowerBound();
+    method public androidx.core.graphics.Insets getUpperBound();
+    method public androidx.core.view.WindowInsetsAnimationCompat.BoundsCompat inset(androidx.core.graphics.Insets);
+    method @RequiresApi(30) public android.view.WindowInsetsAnimation.Bounds toBounds();
+    method @RequiresApi(30) public static androidx.core.view.WindowInsetsAnimationCompat.BoundsCompat toBoundsCompat(android.view.WindowInsetsAnimation.Bounds);
+  }
+
+  public abstract static class WindowInsetsAnimationCompat.Callback {
+    ctor public WindowInsetsAnimationCompat.Callback(@androidx.core.view.WindowInsetsAnimationCompat.Callback.DispatchMode int);
+    method @androidx.core.view.WindowInsetsAnimationCompat.Callback.DispatchMode public final int getDispatchMode();
+    method public void onEnd(androidx.core.view.WindowInsetsAnimationCompat);
+    method public void onPrepare(androidx.core.view.WindowInsetsAnimationCompat);
+    method public abstract androidx.core.view.WindowInsetsCompat onProgress(androidx.core.view.WindowInsetsCompat, java.util.List<androidx.core.view.WindowInsetsAnimationCompat!>);
+    method public androidx.core.view.WindowInsetsAnimationCompat.BoundsCompat onStart(androidx.core.view.WindowInsetsAnimationCompat, androidx.core.view.WindowInsetsAnimationCompat.BoundsCompat);
+    field public static final int DISPATCH_MODE_CONTINUE_ON_SUBTREE = 1; // 0x1
+    field public static final int DISPATCH_MODE_STOP = 0; // 0x0
+  }
+
+  @IntDef({androidx.core.view.WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_STOP, androidx.core.view.WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface WindowInsetsAnimationCompat.Callback.DispatchMode {
+  }
+
+  public interface WindowInsetsAnimationControlListenerCompat {
+    method public void onCancelled(androidx.core.view.WindowInsetsAnimationControllerCompat?);
+    method public void onFinished(androidx.core.view.WindowInsetsAnimationControllerCompat);
+    method public void onReady(androidx.core.view.WindowInsetsAnimationControllerCompat, @androidx.core.view.WindowInsetsCompat.Type.InsetsType int);
+  }
+
+  public final class WindowInsetsAnimationControllerCompat {
+    method public void finish(boolean);
+    method public float getCurrentAlpha();
+    method @FloatRange(from=0.0f, to=1.0f) public float getCurrentFraction();
+    method public androidx.core.graphics.Insets getCurrentInsets();
+    method public androidx.core.graphics.Insets getHiddenStateInsets();
+    method public androidx.core.graphics.Insets getShownStateInsets();
+    method @androidx.core.view.WindowInsetsCompat.Type.InsetsType public int getTypes();
+    method public boolean isCancelled();
+    method public boolean isFinished();
+    method public boolean isReady();
+    method public void setInsetsAndAlpha(androidx.core.graphics.Insets?, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
+  }
+
+  public class WindowInsetsCompat {
+    ctor public WindowInsetsCompat(androidx.core.view.WindowInsetsCompat?);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat consumeDisplayCutout();
+    method @Deprecated public androidx.core.view.WindowInsetsCompat consumeStableInsets();
+    method @Deprecated public androidx.core.view.WindowInsetsCompat consumeSystemWindowInsets();
+    method public androidx.core.view.DisplayCutoutCompat? getDisplayCutout();
+    method public androidx.core.graphics.Insets getInsets(@androidx.core.view.WindowInsetsCompat.Type.InsetsType int);
+    method public androidx.core.graphics.Insets getInsetsIgnoringVisibility(@androidx.core.view.WindowInsetsCompat.Type.InsetsType int);
+    method @Deprecated public androidx.core.graphics.Insets getMandatorySystemGestureInsets();
+    method @Deprecated public int getStableInsetBottom();
+    method @Deprecated public int getStableInsetLeft();
+    method @Deprecated public int getStableInsetRight();
+    method @Deprecated public int getStableInsetTop();
+    method @Deprecated public androidx.core.graphics.Insets getStableInsets();
+    method @Deprecated public androidx.core.graphics.Insets getSystemGestureInsets();
+    method @Deprecated public int getSystemWindowInsetBottom();
+    method @Deprecated public int getSystemWindowInsetLeft();
+    method @Deprecated public int getSystemWindowInsetRight();
+    method @Deprecated public int getSystemWindowInsetTop();
+    method @Deprecated public androidx.core.graphics.Insets getSystemWindowInsets();
+    method @Deprecated public androidx.core.graphics.Insets getTappableElementInsets();
+    method public boolean hasInsets();
+    method @Deprecated public boolean hasStableInsets();
+    method @Deprecated public boolean hasSystemWindowInsets();
+    method public androidx.core.view.WindowInsetsCompat inset(androidx.core.graphics.Insets);
+    method public androidx.core.view.WindowInsetsCompat inset(@IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
+    method public boolean isConsumed();
+    method public boolean isRound();
+    method public boolean isVisible(@androidx.core.view.WindowInsetsCompat.Type.InsetsType int);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat replaceSystemWindowInsets(android.graphics.Rect);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat replaceSystemWindowInsets(int, int, int, int);
+    method @RequiresApi(20) public android.view.WindowInsets? toWindowInsets();
+    method @RequiresApi(20) public static androidx.core.view.WindowInsetsCompat toWindowInsetsCompat(android.view.WindowInsets);
+    method @RequiresApi(20) public static androidx.core.view.WindowInsetsCompat toWindowInsetsCompat(android.view.WindowInsets, android.view.View?);
+    field public static final androidx.core.view.WindowInsetsCompat CONSUMED;
+  }
+
+  public static final class WindowInsetsCompat.Builder {
+    ctor public WindowInsetsCompat.Builder();
+    ctor public WindowInsetsCompat.Builder(androidx.core.view.WindowInsetsCompat);
+    method public androidx.core.view.WindowInsetsCompat build();
+    method public androidx.core.view.WindowInsetsCompat.Builder setDisplayCutout(androidx.core.view.DisplayCutoutCompat?);
+    method public androidx.core.view.WindowInsetsCompat.Builder setInsets(@androidx.core.view.WindowInsetsCompat.Type.InsetsType int, androidx.core.graphics.Insets);
+    method public androidx.core.view.WindowInsetsCompat.Builder setInsetsIgnoringVisibility(@androidx.core.view.WindowInsetsCompat.Type.InsetsType int, androidx.core.graphics.Insets);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setMandatorySystemGestureInsets(androidx.core.graphics.Insets);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setStableInsets(androidx.core.graphics.Insets);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setSystemGestureInsets(androidx.core.graphics.Insets);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setSystemWindowInsets(androidx.core.graphics.Insets);
+    method @Deprecated public androidx.core.view.WindowInsetsCompat.Builder setTappableElementInsets(androidx.core.graphics.Insets);
+    method public androidx.core.view.WindowInsetsCompat.Builder setVisible(@androidx.core.view.WindowInsetsCompat.Type.InsetsType int, boolean);
+  }
+
+  public static final class WindowInsetsCompat.Type {
+    method @androidx.core.view.WindowInsetsCompat.Type.InsetsType public static int captionBar();
+    method @androidx.core.view.WindowInsetsCompat.Type.InsetsType public static int displayCutout();
+    method @androidx.core.view.WindowInsetsCompat.Type.InsetsType public static int ime();
+    method @androidx.core.view.WindowInsetsCompat.Type.InsetsType public static int mandatorySystemGestures();
+    method @androidx.core.view.WindowInsetsCompat.Type.InsetsType public static int navigationBars();
+    method @androidx.core.view.WindowInsetsCompat.Type.InsetsType public static int statusBars();
+    method @androidx.core.view.WindowInsetsCompat.Type.InsetsType public static int systemBars();
+    method @androidx.core.view.WindowInsetsCompat.Type.InsetsType public static int systemGestures();
+    method @androidx.core.view.WindowInsetsCompat.Type.InsetsType public static int tappableElement();
+  }
+
+  @IntDef(flag=true, value={0x1, 0x2, 0x4, 0x8, 0x100, 0x10, 0x20, 0x40, 0x80}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface WindowInsetsCompat.Type.InsetsType {
+  }
+
+  public final class WindowInsetsControllerCompat {
+    ctor public WindowInsetsControllerCompat(android.view.Window, android.view.View);
+    method public void addOnControllableInsetsChangedListener(androidx.core.view.WindowInsetsControllerCompat.OnControllableInsetsChangedListener);
+    method public void controlWindowInsetsAnimation(@androidx.core.view.WindowInsetsCompat.Type.InsetsType int, long, android.view.animation.Interpolator?, android.os.CancellationSignal?, androidx.core.view.WindowInsetsAnimationControlListenerCompat);
+    method public int getSystemBarsBehavior();
+    method public void hide(@androidx.core.view.WindowInsetsCompat.Type.InsetsType int);
+    method public boolean isAppearanceLightNavigationBars();
+    method public boolean isAppearanceLightStatusBars();
+    method public void removeOnControllableInsetsChangedListener(androidx.core.view.WindowInsetsControllerCompat.OnControllableInsetsChangedListener);
+    method public void setAppearanceLightNavigationBars(boolean);
+    method public void setAppearanceLightStatusBars(boolean);
+    method public void setSystemBarsBehavior(int);
+    method public void show(@androidx.core.view.WindowInsetsCompat.Type.InsetsType int);
+    method @Deprecated @RequiresApi(30) public static androidx.core.view.WindowInsetsControllerCompat toWindowInsetsControllerCompat(android.view.WindowInsetsController);
+    field public static final int BEHAVIOR_DEFAULT = 1; // 0x1
+    field @Deprecated public static final int BEHAVIOR_SHOW_BARS_BY_SWIPE = 1; // 0x1
+    field @Deprecated public static final int BEHAVIOR_SHOW_BARS_BY_TOUCH = 0; // 0x0
+    field public static final int BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE = 2; // 0x2
+  }
+
+  public static interface WindowInsetsControllerCompat.OnControllableInsetsChangedListener {
+    method public void onControllableInsetsChanged(androidx.core.view.WindowInsetsControllerCompat, @androidx.core.view.WindowInsetsCompat.Type.InsetsType int);
+  }
+
+}
+
+package androidx.core.view.accessibility {
+
+  public final class AccessibilityClickableSpanCompat extends android.text.style.ClickableSpan {
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public AccessibilityClickableSpanCompat(int, androidx.core.view.accessibility.AccessibilityNodeInfoCompat, int);
+    method public void onClick(android.view.View);
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final String SPAN_ID = "ACCESSIBILITY_CLICKABLE_SPAN_ID";
+  }
+
+  public final class AccessibilityEventCompat {
+    method @Deprecated public static void appendRecord(android.view.accessibility.AccessibilityEvent!, androidx.core.view.accessibility.AccessibilityRecordCompat!);
+    method @Deprecated public static androidx.core.view.accessibility.AccessibilityRecordCompat! asRecord(android.view.accessibility.AccessibilityEvent!);
+    method public static int getAction(android.view.accessibility.AccessibilityEvent);
+    method @androidx.core.view.accessibility.AccessibilityEventCompat.ContentChangeType public static int getContentChangeTypes(android.view.accessibility.AccessibilityEvent);
+    method public static int getMovementGranularity(android.view.accessibility.AccessibilityEvent);
+    method @Deprecated public static androidx.core.view.accessibility.AccessibilityRecordCompat! getRecord(android.view.accessibility.AccessibilityEvent!, int);
+    method @Deprecated public static int getRecordCount(android.view.accessibility.AccessibilityEvent!);
+    method public static boolean isAccessibilityDataSensitive(android.view.accessibility.AccessibilityEvent);
+    method public static void setAccessibilityDataSensitive(android.view.accessibility.AccessibilityEvent, boolean);
+    method public static void setAction(android.view.accessibility.AccessibilityEvent, int);
+    method public static void setContentChangeTypes(android.view.accessibility.AccessibilityEvent, @androidx.core.view.accessibility.AccessibilityEventCompat.ContentChangeType int);
+    method public static void setMovementGranularity(android.view.accessibility.AccessibilityEvent, int);
+    field public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 4; // 0x4
+    field public static final int CONTENT_CHANGE_TYPE_CONTENT_INVALID = 1024; // 0x400
+    field public static final int CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 512; // 0x200
+    field public static final int CONTENT_CHANGE_TYPE_DRAG_DROPPED = 256; // 0x100
+    field public static final int CONTENT_CHANGE_TYPE_DRAG_STARTED = 128; // 0x80
+    field public static final int CONTENT_CHANGE_TYPE_ENABLED = 4096; // 0x1000
+    field public static final int CONTENT_CHANGE_TYPE_ERROR = 2048; // 0x800
+    field public static final int CONTENT_CHANGE_TYPE_PANE_APPEARED = 16; // 0x10
+    field public static final int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 32; // 0x20
+    field public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 8; // 0x8
+    field public static final int CONTENT_CHANGE_TYPE_STATE_DESCRIPTION = 64; // 0x40
+    field public static final int CONTENT_CHANGE_TYPE_SUBTREE = 1; // 0x1
+    field public static final int CONTENT_CHANGE_TYPE_TEXT = 2; // 0x2
+    field public static final int CONTENT_CHANGE_TYPE_UNDEFINED = 0; // 0x0
+    field public static final int TYPES_ALL_MASK = -1; // 0xffffffff
+    field public static final int TYPE_ANNOUNCEMENT = 16384; // 0x4000
+    field public static final int TYPE_ASSIST_READING_CONTEXT = 16777216; // 0x1000000
+    field public static final int TYPE_GESTURE_DETECTION_END = 524288; // 0x80000
+    field public static final int TYPE_GESTURE_DETECTION_START = 262144; // 0x40000
+    field @Deprecated public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END = 1024; // 0x400
+    field @Deprecated public static final int TYPE_TOUCH_EXPLORATION_GESTURE_START = 512; // 0x200
+    field public static final int TYPE_TOUCH_INTERACTION_END = 2097152; // 0x200000
+    field public static final int TYPE_TOUCH_INTERACTION_START = 1048576; // 0x100000
+    field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUSED = 32768; // 0x8000
+    field public static final int TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 65536; // 0x10000
+    field public static final int TYPE_VIEW_CONTEXT_CLICKED = 8388608; // 0x800000
+    field @Deprecated public static final int TYPE_VIEW_HOVER_ENTER = 128; // 0x80
+    field @Deprecated public static final int TYPE_VIEW_HOVER_EXIT = 256; // 0x100
+    field @Deprecated public static final int TYPE_VIEW_SCROLLED = 4096; // 0x1000
+    field public static final int TYPE_VIEW_TARGETED_BY_SCROLL = 67108864; // 0x4000000
+    field @Deprecated public static final int TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192; // 0x2000
+    field public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 131072; // 0x20000
+    field public static final int TYPE_WINDOWS_CHANGED = 4194304; // 0x400000
+    field @Deprecated public static final int TYPE_WINDOW_CONTENT_CHANGED = 2048; // 0x800
+  }
+
+  @IntDef(flag=true, value={androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_SUBTREE, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_TEXT, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_UNDEFINED, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_DRAG_STARTED, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_DRAG_DROPPED, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_DRAG_CANCELLED, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_CONTENT_INVALID, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_ERROR, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_ENABLED}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface AccessibilityEventCompat.ContentChangeType {
+  }
+
+  public final class AccessibilityManagerCompat {
+    method @Deprecated public static boolean addAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager!, androidx.core.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener!);
+    method public static boolean addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager, androidx.core.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener);
+    method @Deprecated public static java.util.List<android.accessibilityservice.AccessibilityServiceInfo!>! getEnabledAccessibilityServiceList(android.view.accessibility.AccessibilityManager!, int);
+    method @Deprecated public static java.util.List<android.accessibilityservice.AccessibilityServiceInfo!>! getInstalledAccessibilityServiceList(android.view.accessibility.AccessibilityManager!);
+    method public static boolean isRequestFromAccessibilityTool(android.view.accessibility.AccessibilityManager);
+    method @Deprecated public static boolean isTouchExplorationEnabled(android.view.accessibility.AccessibilityManager!);
+    method @Deprecated public static boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager!, androidx.core.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener!);
+    method public static boolean removeTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager, androidx.core.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener);
+  }
+
+  @Deprecated public static interface AccessibilityManagerCompat.AccessibilityStateChangeListener {
+    method @Deprecated public void onAccessibilityStateChanged(boolean);
+  }
+
+  @Deprecated public abstract static class AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat implements androidx.core.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener {
+    ctor @Deprecated public AccessibilityManagerCompat.AccessibilityStateChangeListenerCompat();
+  }
+
+  public static interface AccessibilityManagerCompat.TouchExplorationStateChangeListener {
+    method public void onTouchExplorationStateChanged(boolean);
+  }
+
+  public class AccessibilityNodeInfoCompat {
+    ctor @Deprecated public AccessibilityNodeInfoCompat(Object!);
+    method public void addAction(androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat!);
+    method public void addAction(int);
+    method public void addChild(android.view.View!);
+    method public void addChild(android.view.View!, int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void addSpansToExtras(CharSequence!, android.view.View!);
+    method public boolean canOpenPopup();
+    method public java.util.List<androidx.core.view.accessibility.AccessibilityNodeInfoCompat!>! findAccessibilityNodeInfosByText(String!);
+    method public java.util.List<androidx.core.view.accessibility.AccessibilityNodeInfoCompat!>! findAccessibilityNodeInfosByViewId(String!);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! findFocus(int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! focusSearch(int);
+    method public java.util.List<androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat!>! getActionList();
+    method @Deprecated public int getActions();
+    method public java.util.List<java.lang.String!> getAvailableExtraData();
+    method @Deprecated public void getBoundsInParent(android.graphics.Rect!);
+    method public void getBoundsInScreen(android.graphics.Rect!);
+    method public void getBoundsInWindow(android.graphics.Rect);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getChild(int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? getChild(int, int);
+    method public int getChildCount();
+    method public CharSequence! getClassName();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static android.text.style.ClickableSpan![]! getClickableSpans(CharSequence!);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat! getCollectionInfo();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat! getCollectionItemInfo();
+    method public CharSequence? getContainerTitle();
+    method public CharSequence! getContentDescription();
+    method public int getDrawingOrder();
+    method public CharSequence! getError();
+    method public android.view.accessibility.AccessibilityNodeInfo.ExtraRenderingInfo? getExtraRenderingInfo();
+    method public android.os.Bundle! getExtras();
+    method public CharSequence? getHintText();
+    method @Deprecated public Object! getInfo();
+    method public int getInputType();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getLabelFor();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getLabeledBy();
+    method public int getLiveRegion();
+    method public int getMaxTextLength();
+    method public long getMinDurationBetweenContentChangesMillis();
+    method public int getMovementGranularities();
+    method public CharSequence! getPackageName();
+    method public CharSequence? getPaneTitle();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getParent();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? getParent(int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat! getRangeInfo();
+    method public CharSequence? getRoleDescription();
+    method public CharSequence? getStateDescription();
+    method public CharSequence! getText();
+    method public int getTextSelectionEnd();
+    method public int getTextSelectionStart();
+    method public CharSequence? getTooltipText();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.TouchDelegateInfoCompat? getTouchDelegateInfo();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getTraversalAfter();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getTraversalBefore();
+    method public String? getUniqueId();
+    method public String! getViewIdResourceName();
+    method public androidx.core.view.accessibility.AccessibilityWindowInfoCompat! getWindow();
+    method public int getWindowId();
+    method public boolean hasRequestInitialAccessibilityFocus();
+    method public boolean isAccessibilityDataSensitive();
+    method public boolean isAccessibilityFocused();
+    method public boolean isCheckable();
+    method public boolean isChecked();
+    method public boolean isClickable();
+    method public boolean isContentInvalid();
+    method public boolean isContextClickable();
+    method public boolean isDismissable();
+    method public boolean isEditable();
+    method public boolean isEnabled();
+    method public boolean isFocusable();
+    method public boolean isFocused();
+    method public boolean isGranularScrollingSupported();
+    method public boolean isHeading();
+    method public boolean isImportantForAccessibility();
+    method public boolean isLongClickable();
+    method public boolean isMultiLine();
+    method public boolean isPassword();
+    method public boolean isScreenReaderFocusable();
+    method public boolean isScrollable();
+    method public boolean isSelected();
+    method public boolean isShowingHintText();
+    method public boolean isTextEntryKey();
+    method public boolean isTextSelectable();
+    method public boolean isVisibleToUser();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! obtain();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! obtain(android.view.View!);
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! obtain(android.view.View!, int);
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! obtain(androidx.core.view.accessibility.AccessibilityNodeInfoCompat!);
+    method public boolean performAction(int);
+    method public boolean performAction(int, android.os.Bundle!);
+    method @Deprecated public void recycle();
+    method public boolean refresh();
+    method public boolean removeAction(androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat!);
+    method public boolean removeChild(android.view.View!);
+    method public boolean removeChild(android.view.View!, int);
+    method public void setAccessibilityDataSensitive(boolean);
+    method public void setAccessibilityFocused(boolean);
+    method public void setAvailableExtraData(java.util.List<java.lang.String!>);
+    method @Deprecated public void setBoundsInParent(android.graphics.Rect!);
+    method public void setBoundsInScreen(android.graphics.Rect!);
+    method public void setBoundsInWindow(android.graphics.Rect);
+    method public void setCanOpenPopup(boolean);
+    method public void setCheckable(boolean);
+    method public void setChecked(boolean);
+    method public void setClassName(CharSequence!);
+    method public void setClickable(boolean);
+    method public void setCollectionInfo(Object!);
+    method public void setCollectionItemInfo(Object!);
+    method public void setContainerTitle(CharSequence?);
+    method public void setContentDescription(CharSequence!);
+    method public void setContentInvalid(boolean);
+    method public void setContextClickable(boolean);
+    method public void setDismissable(boolean);
+    method public void setDrawingOrder(int);
+    method public void setEditable(boolean);
+    method public void setEnabled(boolean);
+    method public void setError(CharSequence!);
+    method public void setFocusable(boolean);
+    method public void setFocused(boolean);
+    method public void setGranularScrollingSupported(boolean);
+    method public void setHeading(boolean);
+    method public void setHintText(CharSequence?);
+    method public void setImportantForAccessibility(boolean);
+    method public void setInputType(int);
+    method public void setLabelFor(android.view.View!);
+    method public void setLabelFor(android.view.View!, int);
+    method public void setLabeledBy(android.view.View!);
+    method public void setLabeledBy(android.view.View!, int);
+    method public void setLiveRegion(int);
+    method public void setLongClickable(boolean);
+    method public void setMaxTextLength(int);
+    method public void setMinDurationBetweenContentChangesMillis(long);
+    method public void setMovementGranularities(int);
+    method public void setMultiLine(boolean);
+    method public void setPackageName(CharSequence!);
+    method public void setPaneTitle(CharSequence?);
+    method public void setParent(android.view.View!);
+    method public void setParent(android.view.View!, int);
+    method public void setPassword(boolean);
+    method public void setQueryFromAppProcessEnabled(android.view.View, boolean);
+    method public void setRangeInfo(androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat!);
+    method public void setRequestInitialAccessibilityFocus(boolean);
+    method public void setRoleDescription(CharSequence?);
+    method public void setScreenReaderFocusable(boolean);
+    method public void setScrollable(boolean);
+    method public void setSelected(boolean);
+    method public void setShowingHintText(boolean);
+    method public void setSource(android.view.View!);
+    method public void setSource(android.view.View!, int);
+    method public void setStateDescription(CharSequence?);
+    method public void setText(CharSequence!);
+    method public void setTextEntryKey(boolean);
+    method public void setTextSelectable(boolean);
+    method public void setTextSelection(int, int);
+    method public void setTooltipText(CharSequence?);
+    method public void setTouchDelegateInfo(androidx.core.view.accessibility.AccessibilityNodeInfoCompat.TouchDelegateInfoCompat);
+    method public void setTraversalAfter(android.view.View!);
+    method public void setTraversalAfter(android.view.View!, int);
+    method public void setTraversalBefore(android.view.View!);
+    method public void setTraversalBefore(android.view.View!, int);
+    method public void setUniqueId(String?);
+    method public void setViewIdResourceName(String!);
+    method public void setVisibleToUser(boolean);
+    method public android.view.accessibility.AccessibilityNodeInfo! unwrap();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat! wrap(android.view.accessibility.AccessibilityNodeInfo);
+    field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
+    field public static final String ACTION_ARGUMENT_COLUMN_INT = "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
+    field public static final String ACTION_ARGUMENT_DIRECTION_INT = "androidx.core.view.accessibility.action.ARGUMENT_DIRECTION_INT";
+    field public static final String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
+    field public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
+    field public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
+    field public static final String ACTION_ARGUMENT_MOVE_WINDOW_X = "ACTION_ARGUMENT_MOVE_WINDOW_X";
+    field public static final String ACTION_ARGUMENT_MOVE_WINDOW_Y = "ACTION_ARGUMENT_MOVE_WINDOW_Y";
+    field public static final String ACTION_ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT = "android.view.accessibility.action.ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT";
+    field public static final String ACTION_ARGUMENT_PROGRESS_VALUE = "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";
+    field public static final String ACTION_ARGUMENT_ROW_INT = "android.view.accessibility.action.ARGUMENT_ROW_INT";
+    field public static final String ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT = "androidx.core.view.accessibility.action.ARGUMENT_SCROLL_AMOUNT_FLOAT";
+    field public static final String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT";
+    field public static final String ACTION_ARGUMENT_SELECTION_START_INT = "ACTION_ARGUMENT_SELECTION_START_INT";
+    field public static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
+    field public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 128; // 0x80
+    field public static final int ACTION_CLEAR_FOCUS = 2; // 0x2
+    field public static final int ACTION_CLEAR_SELECTION = 8; // 0x8
+    field public static final int ACTION_CLICK = 16; // 0x10
+    field public static final int ACTION_COLLAPSE = 524288; // 0x80000
+    field public static final int ACTION_COPY = 16384; // 0x4000
+    field public static final int ACTION_CUT = 65536; // 0x10000
+    field public static final int ACTION_DISMISS = 1048576; // 0x100000
+    field public static final int ACTION_EXPAND = 262144; // 0x40000
+    field public static final int ACTION_FOCUS = 1; // 0x1
+    field public static final int ACTION_LONG_CLICK = 32; // 0x20
+    field public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 256; // 0x100
+    field public static final int ACTION_NEXT_HTML_ELEMENT = 1024; // 0x400
+    field public static final int ACTION_PASTE = 32768; // 0x8000
+    field public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 512; // 0x200
+    field public static final int ACTION_PREVIOUS_HTML_ELEMENT = 2048; // 0x800
+    field public static final int ACTION_SCROLL_BACKWARD = 8192; // 0x2000
+    field public static final int ACTION_SCROLL_FORWARD = 4096; // 0x1000
+    field public static final int ACTION_SELECT = 4; // 0x4
+    field public static final int ACTION_SET_SELECTION = 131072; // 0x20000
+    field public static final int ACTION_SET_TEXT = 2097152; // 0x200000
+    field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH = "android.core.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
+    field public static final int EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH = 20000; // 0x4e20
+    field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX = "android.core.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX";
+    field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY = "android.core.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";
+    field public static final int FLAG_PREFETCH_ANCESTORS = 1; // 0x1
+    field public static final int FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST = 16; // 0x10
+    field public static final int FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST = 8; // 0x8
+    field public static final int FLAG_PREFETCH_DESCENDANTS_HYBRID = 4; // 0x4
+    field public static final int FLAG_PREFETCH_SIBLINGS = 2; // 0x2
+    field public static final int FLAG_PREFETCH_UNINTERRUPTIBLE = 32; // 0x20
+    field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2
+    field public static final int FOCUS_INPUT = 1; // 0x1
+    field public static final int MAX_NUMBER_OF_PREFETCHED_NODES = 50; // 0x32
+    field public static final int MOVEMENT_GRANULARITY_CHARACTER = 1; // 0x1
+    field public static final int MOVEMENT_GRANULARITY_LINE = 4; // 0x4
+    field public static final int MOVEMENT_GRANULARITY_PAGE = 16; // 0x10
+    field public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 8; // 0x8
+    field public static final int MOVEMENT_GRANULARITY_WORD = 2; // 0x2
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int mParentVirtualDescendantId;
+  }
+
+  public static class AccessibilityNodeInfoCompat.AccessibilityActionCompat {
+    ctor public AccessibilityNodeInfoCompat.AccessibilityActionCompat(int, CharSequence!);
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public AccessibilityNodeInfoCompat.AccessibilityActionCompat(int, CharSequence!, androidx.core.view.accessibility.AccessibilityViewCommand!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! createReplacementAction(CharSequence!, androidx.core.view.accessibility.AccessibilityViewCommand!);
+    method public int getId();
+    method public CharSequence! getLabel();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean perform(android.view.View!, android.os.Bundle!);
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_ACCESSIBILITY_FOCUS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CLEAR_ACCESSIBILITY_FOCUS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CLEAR_FOCUS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CLEAR_SELECTION;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CLICK;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_COLLAPSE;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CONTEXT_CLICK;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_COPY;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_CUT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_DISMISS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_DRAG_CANCEL;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_DRAG_DROP;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_DRAG_START;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_EXPAND;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_FOCUS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_HIDE_TOOLTIP;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_IME_ENTER;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_LONG_CLICK;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_MOVE_WINDOW;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_NEXT_AT_MOVEMENT_GRANULARITY;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_NEXT_HTML_ELEMENT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PAGE_DOWN;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PAGE_LEFT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PAGE_RIGHT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PAGE_UP;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_PASTE;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_PRESS_AND_HOLD;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_PREVIOUS_HTML_ELEMENT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_BACKWARD;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_DOWN;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_FORWARD;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SCROLL_IN_DIRECTION;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_LEFT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_RIGHT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_TO_POSITION;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SCROLL_UP;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SELECT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SET_PROGRESS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SET_SELECTION;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SET_TEXT;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SHOW_ON_SCREEN;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat ACTION_SHOW_TEXT_SUGGESTIONS;
+    field public static final androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat! ACTION_SHOW_TOOLTIP;
+    field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) protected final androidx.core.view.accessibility.AccessibilityViewCommand! mCommand;
+  }
+
+  public static class AccessibilityNodeInfoCompat.CollectionInfoCompat {
+    method public int getColumnCount();
+    method public int getRowCount();
+    method public int getSelectionMode();
+    method public boolean isHierarchical();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat! obtain(int, int, boolean);
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat! obtain(int, int, boolean, int);
+    field public static final int SELECTION_MODE_MULTIPLE = 2; // 0x2
+    field public static final int SELECTION_MODE_NONE = 0; // 0x0
+    field public static final int SELECTION_MODE_SINGLE = 1; // 0x1
+  }
+
+  public static class AccessibilityNodeInfoCompat.CollectionItemInfoCompat {
+    method public int getColumnIndex();
+    method public int getColumnSpan();
+    method public String? getColumnTitle();
+    method public int getRowIndex();
+    method public int getRowSpan();
+    method public String? getRowTitle();
+    method @Deprecated public boolean isHeading();
+    method public boolean isSelected();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat! obtain(int, int, int, int, boolean);
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat! obtain(int, int, int, int, boolean, boolean);
+  }
+
+  public static final class AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder {
+    ctor public AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat build();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setColumnIndex(int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setColumnSpan(int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setColumnTitle(String?);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setHeading(boolean);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setRowIndex(int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setRowSpan(int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setRowTitle(String?);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setSelected(boolean);
+  }
+
+  public static class AccessibilityNodeInfoCompat.RangeInfoCompat {
+    ctor public AccessibilityNodeInfoCompat.RangeInfoCompat(int, float, float, float);
+    method public float getCurrent();
+    method public float getMax();
+    method public float getMin();
+    method public int getType();
+    method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat! obtain(int, float, float, float);
+    field public static final int RANGE_TYPE_FLOAT = 1; // 0x1
+    field public static final int RANGE_TYPE_INT = 0; // 0x0
+    field public static final int RANGE_TYPE_PERCENT = 2; // 0x2
+  }
+
+  public static final class AccessibilityNodeInfoCompat.TouchDelegateInfoCompat {
+    ctor public AccessibilityNodeInfoCompat.TouchDelegateInfoCompat(java.util.Map<android.graphics.Region!,android.view.View!>);
+    method public android.graphics.Region? getRegionAt(@IntRange(from=0) int);
+    method @IntRange(from=0) public int getRegionCount();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? getTargetForRegion(android.graphics.Region);
+  }
+
+  public class AccessibilityNodeProviderCompat {
+    ctor public AccessibilityNodeProviderCompat();
+    ctor public AccessibilityNodeProviderCompat(Object?);
+    method public void addExtraDataToAccessibilityNodeInfo(int, androidx.core.view.accessibility.AccessibilityNodeInfoCompat, String, android.os.Bundle?);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? createAccessibilityNodeInfo(int);
+    method public java.util.List<androidx.core.view.accessibility.AccessibilityNodeInfoCompat!>? findAccessibilityNodeInfosByText(String, int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? findFocus(int);
+    method public Object? getProvider();
+    method public boolean performAction(int, int, android.os.Bundle?);
+    field public static final int HOST_VIEW_ID = -1; // 0xffffffff
+  }
+
+  public class AccessibilityRecordCompat {
+    ctor @Deprecated public AccessibilityRecordCompat(Object!);
+    method @Deprecated public boolean equals(Object?);
+    method @Deprecated public int getAddedCount();
+    method @Deprecated public CharSequence! getBeforeText();
+    method @Deprecated public CharSequence! getClassName();
+    method @Deprecated public CharSequence! getContentDescription();
+    method @Deprecated public int getCurrentItemIndex();
+    method @Deprecated public int getFromIndex();
+    method @Deprecated public Object! getImpl();
+    method @Deprecated public int getItemCount();
+    method @Deprecated public int getMaxScrollX();
+    method public static int getMaxScrollX(android.view.accessibility.AccessibilityRecord);
+    method @Deprecated public int getMaxScrollY();
+    method public static int getMaxScrollY(android.view.accessibility.AccessibilityRecord);
+    method @Deprecated public android.os.Parcelable! getParcelableData();
+    method @Deprecated public int getRemovedCount();
+    method @Deprecated public int getScrollX();
+    method @Deprecated public int getScrollY();
+    method @Deprecated public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getSource();
+    method @Deprecated public java.util.List<java.lang.CharSequence!>! getText();
+    method @Deprecated public int getToIndex();
+    method @Deprecated public int getWindowId();
+    method @Deprecated public int hashCode();
+    method @Deprecated public boolean isChecked();
+    method @Deprecated public boolean isEnabled();
+    method @Deprecated public boolean isFullScreen();
+    method @Deprecated public boolean isPassword();
+    method @Deprecated public boolean isScrollable();
+    method @Deprecated public static androidx.core.view.accessibility.AccessibilityRecordCompat! obtain();
+    method @Deprecated public static androidx.core.view.accessibility.AccessibilityRecordCompat! obtain(androidx.core.view.accessibility.AccessibilityRecordCompat!);
+    method @Deprecated public void recycle();
+    method @Deprecated public void setAddedCount(int);
+    method @Deprecated public void setBeforeText(CharSequence!);
+    method @Deprecated public void setChecked(boolean);
+    method @Deprecated public void setClassName(CharSequence!);
+    method @Deprecated public void setContentDescription(CharSequence!);
+    method @Deprecated public void setCurrentItemIndex(int);
+    method @Deprecated public void setEnabled(boolean);
+    method @Deprecated public void setFromIndex(int);
+    method @Deprecated public void setFullScreen(boolean);
+    method @Deprecated public void setItemCount(int);
+    method public static void setMaxScrollX(android.view.accessibility.AccessibilityRecord, int);
+    method @Deprecated public void setMaxScrollX(int);
+    method public static void setMaxScrollY(android.view.accessibility.AccessibilityRecord, int);
+    method @Deprecated public void setMaxScrollY(int);
+    method @Deprecated public void setParcelableData(android.os.Parcelable!);
+    method @Deprecated public void setPassword(boolean);
+    method @Deprecated public void setRemovedCount(int);
+    method @Deprecated public void setScrollX(int);
+    method @Deprecated public void setScrollY(int);
+    method @Deprecated public void setScrollable(boolean);
+    method public static void setSource(android.view.accessibility.AccessibilityRecord, android.view.View?, int);
+    method @Deprecated public void setSource(android.view.View!);
+    method @Deprecated public void setSource(android.view.View!, int);
+    method @Deprecated public void setToIndex(int);
+  }
+
+  public interface AccessibilityViewCommand {
+    method public boolean perform(android.view.View, androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments?);
+  }
+
+  public abstract static class AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.CommandArguments();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setBundle(android.os.Bundle?);
+  }
+
+  public static final class AccessibilityViewCommand.MoveAtGranularityArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.MoveAtGranularityArguments();
+    method public boolean getExtendSelection();
+    method public int getGranularity();
+  }
+
+  public static final class AccessibilityViewCommand.MoveHtmlArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.MoveHtmlArguments();
+    method public String? getHTMLElement();
+  }
+
+  public static final class AccessibilityViewCommand.MoveWindowArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.MoveWindowArguments();
+    method public int getX();
+    method public int getY();
+  }
+
+  public static final class AccessibilityViewCommand.ScrollToPositionArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.ScrollToPositionArguments();
+    method public int getColumn();
+    method public int getRow();
+  }
+
+  public static final class AccessibilityViewCommand.SetProgressArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.SetProgressArguments();
+    method public float getProgress();
+  }
+
+  public static final class AccessibilityViewCommand.SetSelectionArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.SetSelectionArguments();
+    method public int getEnd();
+    method public int getStart();
+  }
+
+  public static final class AccessibilityViewCommand.SetTextArguments extends androidx.core.view.accessibility.AccessibilityViewCommand.CommandArguments {
+    ctor public AccessibilityViewCommand.SetTextArguments();
+    method public CharSequence? getText();
+  }
+
+  public class AccessibilityWindowInfoCompat {
+    ctor public AccessibilityWindowInfoCompat();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? getAnchor();
+    method public void getBoundsInScreen(android.graphics.Rect);
+    method public androidx.core.view.accessibility.AccessibilityWindowInfoCompat? getChild(int);
+    method public int getChildCount();
+    method public int getDisplayId();
+    method public int getId();
+    method public int getLayer();
+    method public androidx.core.os.LocaleListCompat getLocales();
+    method public androidx.core.view.accessibility.AccessibilityWindowInfoCompat? getParent();
+    method public void getRegionInScreen(android.graphics.Region);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? getRoot();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? getRoot(int);
+    method public CharSequence? getTitle();
+    method public long getTransitionTimeMillis();
+    method public int getType();
+    method public boolean isAccessibilityFocused();
+    method public boolean isActive();
+    method public boolean isFocused();
+    method public boolean isInPictureInPictureMode();
+    method public static androidx.core.view.accessibility.AccessibilityWindowInfoCompat? obtain();
+    method public static androidx.core.view.accessibility.AccessibilityWindowInfoCompat? obtain(androidx.core.view.accessibility.AccessibilityWindowInfoCompat?);
+    method @Deprecated public void recycle();
+    method public android.view.accessibility.AccessibilityWindowInfo? unwrap();
+    field public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; // 0x4
+    field public static final int TYPE_APPLICATION = 1; // 0x1
+    field public static final int TYPE_INPUT_METHOD = 2; // 0x2
+    field public static final int TYPE_MAGNIFICATION_OVERLAY = 6; // 0x6
+    field public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5; // 0x5
+    field public static final int TYPE_SYSTEM = 3; // 0x3
+  }
+
+}
+
+package androidx.core.view.animation {
+
+  public final class PathInterpolatorCompat {
+    method public static android.view.animation.Interpolator create(android.graphics.Path);
+    method public static android.view.animation.Interpolator create(float, float);
+    method public static android.view.animation.Interpolator create(float, float, float, float);
+  }
+
+}
+
+package androidx.core.view.autofill {
+
+  public class AutofillIdCompat {
+    method @RequiresApi(26) public android.view.autofill.AutofillId toAutofillId();
+    method @RequiresApi(26) public static androidx.core.view.autofill.AutofillIdCompat toAutofillIdCompat(android.view.autofill.AutofillId);
+  }
+
+}
+
+package androidx.core.view.contentcapture {
+
+  public class ContentCaptureSessionCompat {
+    method public android.view.autofill.AutofillId? newAutofillId(long);
+    method public androidx.core.view.ViewStructureCompat? newVirtualViewStructure(android.view.autofill.AutofillId, long);
+    method public void notifyViewTextChanged(android.view.autofill.AutofillId, CharSequence?);
+    method public void notifyViewsAppeared(java.util.List<android.view.ViewStructure!>);
+    method public void notifyViewsDisappeared(long[]);
+    method @RequiresApi(29) public android.view.contentcapture.ContentCaptureSession toContentCaptureSession();
+    method @RequiresApi(29) public static androidx.core.view.contentcapture.ContentCaptureSessionCompat toContentCaptureSessionCompat(android.view.contentcapture.ContentCaptureSession, android.view.View);
+  }
+
+}
+
+package androidx.core.view.inputmethod {
+
+  public final class EditorInfoCompat {
+    ctor @Deprecated public EditorInfoCompat();
+    method public static String![] getContentMimeTypes(android.view.inputmethod.EditorInfo);
+    method public static CharSequence? getInitialSelectedText(android.view.inputmethod.EditorInfo, int);
+    method public static CharSequence? getInitialTextAfterCursor(android.view.inputmethod.EditorInfo, int, int);
+    method public static CharSequence? getInitialTextBeforeCursor(android.view.inputmethod.EditorInfo, int, int);
+    method public static void setContentMimeTypes(android.view.inputmethod.EditorInfo, String![]?);
+    method public static void setInitialSurroundingSubText(android.view.inputmethod.EditorInfo, CharSequence, int);
+    method public static void setInitialSurroundingText(android.view.inputmethod.EditorInfo, CharSequence);
+    field public static final int IME_FLAG_FORCE_ASCII = -2147483648; // 0x80000000
+    field public static final int IME_FLAG_NO_PERSONALIZED_LEARNING = 16777216; // 0x1000000
+  }
+
+  public final class InputConnectionCompat {
+    ctor @Deprecated public InputConnectionCompat();
+    method public static boolean commitContent(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo, androidx.core.view.inputmethod.InputContentInfoCompat, int, android.os.Bundle?);
+    method @Deprecated public static android.view.inputmethod.InputConnection createWrapper(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo, androidx.core.view.inputmethod.InputConnectionCompat.OnCommitContentListener);
+    method public static android.view.inputmethod.InputConnection createWrapper(android.view.View, android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo);
+    field public static final int INPUT_CONTENT_GRANT_READ_URI_PERMISSION = 1; // 0x1
+  }
+
+  public static interface InputConnectionCompat.OnCommitContentListener {
+    method public boolean onCommitContent(androidx.core.view.inputmethod.InputContentInfoCompat, int, android.os.Bundle?);
+  }
+
+  public final class InputContentInfoCompat {
+    ctor public InputContentInfoCompat(android.net.Uri, android.content.ClipDescription, android.net.Uri?);
+    method public android.net.Uri getContentUri();
+    method public android.content.ClipDescription getDescription();
+    method public android.net.Uri? getLinkUri();
+    method public void releasePermission();
+    method public void requestPermission();
+    method public Object? unwrap();
+    method public static androidx.core.view.inputmethod.InputContentInfoCompat? wrap(Object?);
+  }
+
+}
+
+package androidx.core.widget {
+
+  public abstract class AutoScrollHelper implements android.view.View.OnTouchListener {
+    ctor public AutoScrollHelper(android.view.View);
+    method public abstract boolean canTargetScrollHorizontally(int);
+    method public abstract boolean canTargetScrollVertically(int);
+    method public boolean isEnabled();
+    method public boolean isExclusive();
+    method public boolean onTouch(android.view.View!, android.view.MotionEvent!);
+    method public abstract void scrollTargetBy(int, int);
+    method public androidx.core.widget.AutoScrollHelper setActivationDelay(int);
+    method public androidx.core.widget.AutoScrollHelper setEdgeType(int);
+    method public androidx.core.widget.AutoScrollHelper! setEnabled(boolean);
+    method public androidx.core.widget.AutoScrollHelper! setExclusive(boolean);
+    method public androidx.core.widget.AutoScrollHelper setMaximumEdges(float, float);
+    method public androidx.core.widget.AutoScrollHelper setMaximumVelocity(float, float);
+    method public androidx.core.widget.AutoScrollHelper setMinimumVelocity(float, float);
+    method public androidx.core.widget.AutoScrollHelper setRampDownDuration(int);
+    method public androidx.core.widget.AutoScrollHelper setRampUpDuration(int);
+    method public androidx.core.widget.AutoScrollHelper setRelativeEdges(float, float);
+    method public androidx.core.widget.AutoScrollHelper setRelativeVelocity(float, float);
+    field public static final int EDGE_TYPE_INSIDE = 0; // 0x0
+    field public static final int EDGE_TYPE_INSIDE_EXTEND = 1; // 0x1
+    field public static final int EDGE_TYPE_OUTSIDE = 2; // 0x2
+    field public static final float NO_MAX = 3.4028235E38f;
+    field public static final float NO_MIN = 0.0f;
+    field public static final float RELATIVE_UNSPECIFIED = 0.0f;
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface AutoSizeableTextView {
+    method public int getAutoSizeMaxTextSize();
+    method public int getAutoSizeMinTextSize();
+    method public int getAutoSizeStepGranularity();
+    method public int[]! getAutoSizeTextAvailableSizes();
+    method @androidx.core.widget.TextViewCompat.AutoSizeTextType public int getAutoSizeTextType();
+    method public void setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int) throws java.lang.IllegalArgumentException;
+    method public void setAutoSizeTextTypeUniformWithPresetSizes(int[], int) throws java.lang.IllegalArgumentException;
+    method public void setAutoSizeTextTypeWithDefaults(@androidx.core.widget.TextViewCompat.AutoSizeTextType int);
+    field @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final boolean PLATFORM_SUPPORTS_AUTOSIZE;
+  }
+
+  public final class CheckedTextViewCompat {
+    method public static android.graphics.drawable.Drawable? getCheckMarkDrawable(android.widget.CheckedTextView);
+    method public static android.content.res.ColorStateList? getCheckMarkTintList(android.widget.CheckedTextView);
+    method public static android.graphics.PorterDuff.Mode? getCheckMarkTintMode(android.widget.CheckedTextView);
+    method public static void setCheckMarkTintList(android.widget.CheckedTextView, android.content.res.ColorStateList?);
+    method public static void setCheckMarkTintMode(android.widget.CheckedTextView, android.graphics.PorterDuff.Mode?);
+  }
+
+  public final class CompoundButtonCompat {
+    method public static android.graphics.drawable.Drawable? getButtonDrawable(android.widget.CompoundButton);
+    method public static android.content.res.ColorStateList? getButtonTintList(android.widget.CompoundButton);
+    method public static android.graphics.PorterDuff.Mode? getButtonTintMode(android.widget.CompoundButton);
+    method public static void setButtonTintList(android.widget.CompoundButton, android.content.res.ColorStateList?);
+    method public static void setButtonTintMode(android.widget.CompoundButton, android.graphics.PorterDuff.Mode?);
+  }
+
+  public class ContentLoadingProgressBar extends android.widget.ProgressBar {
+    ctor public ContentLoadingProgressBar(android.content.Context);
+    ctor public ContentLoadingProgressBar(android.content.Context, android.util.AttributeSet?);
+    method public void hide();
+    method public void onAttachedToWindow();
+    method public void onDetachedFromWindow();
+    method public void show();
+  }
+
+  public final class EdgeEffectCompat {
+    ctor @Deprecated public EdgeEffectCompat(android.content.Context!);
+    method public static android.widget.EdgeEffect create(android.content.Context, android.util.AttributeSet?);
+    method @Deprecated public boolean draw(android.graphics.Canvas!);
+    method @Deprecated public void finish();
+    method public static float getDistance(android.widget.EdgeEffect);
+    method @Deprecated public boolean isFinished();
+    method @Deprecated public boolean onAbsorb(int);
+    method public static void onPull(android.widget.EdgeEffect, float, float);
+    method @Deprecated public boolean onPull(float);
+    method @Deprecated public boolean onPull(float, float);
+    method public static float onPullDistance(android.widget.EdgeEffect, float, float);
+    method @Deprecated public boolean onRelease();
+    method @Deprecated public void setSize(int, int);
+  }
+
+  public class ImageViewCompat {
+    method public static android.content.res.ColorStateList? getImageTintList(android.widget.ImageView);
+    method public static android.graphics.PorterDuff.Mode? getImageTintMode(android.widget.ImageView);
+    method public static void setImageTintList(android.widget.ImageView, android.content.res.ColorStateList?);
+    method public static void setImageTintMode(android.widget.ImageView, android.graphics.PorterDuff.Mode?);
+  }
+
+  public final class ListPopupWindowCompat {
+    method public static android.view.View.OnTouchListener? createDragToOpenListener(android.widget.ListPopupWindow, android.view.View);
+    method @Deprecated public static android.view.View.OnTouchListener! createDragToOpenListener(Object!, android.view.View!);
+  }
+
+  public class ListViewAutoScrollHelper extends androidx.core.widget.AutoScrollHelper {
+    ctor public ListViewAutoScrollHelper(android.widget.ListView);
+    method public boolean canTargetScrollHorizontally(int);
+    method public boolean canTargetScrollVertically(int);
+    method public void scrollTargetBy(int, int);
+  }
+
+  public final class ListViewCompat {
+    method public static boolean canScrollList(android.widget.ListView, int);
+    method public static void scrollListBy(android.widget.ListView, int);
+  }
+
+  public class NestedScrollView extends android.widget.FrameLayout implements androidx.core.view.NestedScrollingChild3 androidx.core.view.NestedScrollingParent3 androidx.core.view.ScrollingView {
+    ctor public NestedScrollView(android.content.Context);
+    ctor public NestedScrollView(android.content.Context, android.util.AttributeSet?);
+    ctor public NestedScrollView(android.content.Context, android.util.AttributeSet?, int);
+    method public boolean arrowScroll(int);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeHorizontalScrollExtent();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeHorizontalScrollOffset();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeHorizontalScrollRange();
+    method protected int computeScrollDeltaToGetChildRectOnScreen(android.graphics.Rect!);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeVerticalScrollExtent();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeVerticalScrollOffset();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int computeVerticalScrollRange();
+    method public boolean dispatchNestedPreScroll(int, int, int[]?, int[]?, int);
+    method public boolean dispatchNestedScroll(int, int, int, int, int[]?, int);
+    method public void dispatchNestedScroll(int, int, int, int, int[]?, int, int[]);
+    method public boolean executeKeyEvent(android.view.KeyEvent);
+    method public void fling(int);
+    method public boolean fullScroll(int);
+    method public int getMaxScrollAmount();
+    method public boolean hasNestedScrollingParent(int);
+    method public boolean isFillViewport();
+    method public boolean isSmoothScrollingEnabled();
+    method public void onAttachedToWindow();
+    method public void onNestedPreScroll(android.view.View, int, int, int[], int);
+    method public void onNestedScroll(android.view.View, int, int, int, int, int);
+    method public void onNestedScroll(android.view.View, int, int, int, int, int, int[]);
+    method public void onNestedScrollAccepted(android.view.View, android.view.View, int, int);
+    method public boolean onStartNestedScroll(android.view.View, android.view.View, int, int);
+    method public void onStopNestedScroll(android.view.View, int);
+    method public boolean pageScroll(int);
+    method public void setFillViewport(boolean);
+    method public void setOnScrollChangeListener(androidx.core.widget.NestedScrollView.OnScrollChangeListener?);
+    method public void setSmoothScrollingEnabled(boolean);
+    method public final void smoothScrollBy(int, int);
+    method public final void smoothScrollBy(int, int, int);
+    method public final void smoothScrollTo(int, int);
+    method public final void smoothScrollTo(int, int, int);
+    method public boolean startNestedScroll(int, int);
+    method public void stopNestedScroll(int);
+  }
+
+  public static interface NestedScrollView.OnScrollChangeListener {
+    method public void onScrollChange(androidx.core.widget.NestedScrollView, int, int, int, int);
+  }
+
+  public final class PopupMenuCompat {
+    method public static android.view.View.OnTouchListener? getDragToOpenListener(Object);
+  }
+
+  public final class PopupWindowCompat {
+    method public static boolean getOverlapAnchor(android.widget.PopupWindow);
+    method public static int getWindowLayoutType(android.widget.PopupWindow);
+    method public static void setOverlapAnchor(android.widget.PopupWindow, boolean);
+    method public static void setWindowLayoutType(android.widget.PopupWindow, int);
+    method public static void showAsDropDown(android.widget.PopupWindow, android.view.View, int, int, int);
+  }
+
+  @Deprecated public final class ScrollerCompat {
+    method @Deprecated public void abortAnimation();
+    method @Deprecated public boolean computeScrollOffset();
+    method @Deprecated public static androidx.core.widget.ScrollerCompat! create(android.content.Context!);
+    method @Deprecated public static androidx.core.widget.ScrollerCompat! create(android.content.Context!, android.view.animation.Interpolator!);
+    method @Deprecated public void fling(int, int, int, int, int, int, int, int);
+    method @Deprecated public void fling(int, int, int, int, int, int, int, int, int, int);
+    method @Deprecated public float getCurrVelocity();
+    method @Deprecated public int getCurrX();
+    method @Deprecated public int getCurrY();
+    method @Deprecated public int getFinalX();
+    method @Deprecated public int getFinalY();
+    method @Deprecated public boolean isFinished();
+    method @Deprecated public boolean isOverScrolled();
+    method @Deprecated public void notifyHorizontalEdgeReached(int, int, int);
+    method @Deprecated public void notifyVerticalEdgeReached(int, int, int);
+    method @Deprecated public boolean springBack(int, int, int, int, int, int);
+    method @Deprecated public void startScroll(int, int, int, int);
+    method @Deprecated public void startScroll(int, int, int, int, int);
+  }
+
+  public final class TextViewCompat {
+    method public static int getAutoSizeMaxTextSize(android.widget.TextView);
+    method public static int getAutoSizeMinTextSize(android.widget.TextView);
+    method public static int getAutoSizeStepGranularity(android.widget.TextView);
+    method public static int[] getAutoSizeTextAvailableSizes(android.widget.TextView);
+    method public static int getAutoSizeTextType(android.widget.TextView);
+    method public static android.content.res.ColorStateList? getCompoundDrawableTintList(android.widget.TextView);
+    method public static android.graphics.PorterDuff.Mode? getCompoundDrawableTintMode(android.widget.TextView);
+    method public static android.graphics.drawable.Drawable![] getCompoundDrawablesRelative(android.widget.TextView);
+    method public static int getFirstBaselineToTopHeight(android.widget.TextView);
+    method public static int getLastBaselineToBottomHeight(android.widget.TextView);
+    method public static int getMaxLines(android.widget.TextView);
+    method public static int getMinLines(android.widget.TextView);
+    method public static androidx.core.text.PrecomputedTextCompat.Params getTextMetricsParams(android.widget.TextView);
+    method public static void setAutoSizeTextTypeUniformWithConfiguration(android.widget.TextView, int, int, int, int) throws java.lang.IllegalArgumentException;
+    method public static void setAutoSizeTextTypeUniformWithPresetSizes(android.widget.TextView, int[], int) throws java.lang.IllegalArgumentException;
+    method public static void setAutoSizeTextTypeWithDefaults(android.widget.TextView, int);
+    method public static void setCompoundDrawableTintList(android.widget.TextView, android.content.res.ColorStateList?);
+    method public static void setCompoundDrawableTintMode(android.widget.TextView, android.graphics.PorterDuff.Mode?);
+    method public static void setCompoundDrawablesRelative(android.widget.TextView, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?);
+    method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?, android.graphics.drawable.Drawable?);
+    method public static void setCompoundDrawablesRelativeWithIntrinsicBounds(android.widget.TextView, @DrawableRes int, @DrawableRes int, @DrawableRes int, @DrawableRes int);
+    method public static void setCustomSelectionActionModeCallback(android.widget.TextView, android.view.ActionMode.Callback);
+    method public static void setFirstBaselineToTopHeight(android.widget.TextView, @IntRange(from=0) @Px int);
+    method public static void setLastBaselineToBottomHeight(android.widget.TextView, @IntRange(from=0) @Px int);
+    method public static void setLineHeight(android.widget.TextView, @IntRange(from=0) @Px int);
+    method public static void setLineHeight(android.widget.TextView, int, @FloatRange(from=0) float);
+    method public static void setPrecomputedText(android.widget.TextView, androidx.core.text.PrecomputedTextCompat);
+    method public static void setTextAppearance(android.widget.TextView, @StyleRes int);
+    method public static void setTextMetricsParams(android.widget.TextView, androidx.core.text.PrecomputedTextCompat.Params);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static android.view.ActionMode.Callback? unwrapCustomSelectionActionModeCallback(android.view.ActionMode.Callback?);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static android.view.ActionMode.Callback? wrapCustomSelectionActionModeCallback(android.widget.TextView, android.view.ActionMode.Callback?);
+    field public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; // 0x0
+    field public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1; // 0x1
+  }
+
+  @IntDef({androidx.core.widget.TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE, androidx.core.widget.TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface TextViewCompat.AutoSizeTextType {
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final class TextViewOnReceiveContentListener implements androidx.core.view.OnReceiveContentListener {
+    ctor public TextViewOnReceiveContentListener();
+    method public androidx.core.view.ContentInfoCompat? onReceiveContent(android.view.View, androidx.core.view.ContentInfoCompat);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface TintableCheckedTextView {
+    method public android.content.res.ColorStateList? getSupportCheckMarkTintList();
+    method public android.graphics.PorterDuff.Mode? getSupportCheckMarkTintMode();
+    method public void setSupportCheckMarkTintList(android.content.res.ColorStateList?);
+    method public void setSupportCheckMarkTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  public interface TintableCompoundButton {
+    method public android.content.res.ColorStateList? getSupportButtonTintList();
+    method public android.graphics.PorterDuff.Mode? getSupportButtonTintMode();
+    method public void setSupportButtonTintList(android.content.res.ColorStateList?);
+    method public void setSupportButtonTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  public interface TintableCompoundDrawablesView {
+    method public android.content.res.ColorStateList? getSupportCompoundDrawablesTintList();
+    method public android.graphics.PorterDuff.Mode? getSupportCompoundDrawablesTintMode();
+    method public void setSupportCompoundDrawablesTintList(android.content.res.ColorStateList?);
+    method public void setSupportCompoundDrawablesTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public interface TintableImageSourceView {
+    method public android.content.res.ColorStateList? getSupportImageTintList();
+    method public android.graphics.PorterDuff.Mode? getSupportImageTintMode();
+    method public void setSupportImageTintList(android.content.res.ColorStateList?);
+    method public void setSupportImageTintMode(android.graphics.PorterDuff.Mode?);
+  }
+
+}
+
diff --git a/core/core/api/restricted_current.ignore b/core/core/api/restricted_current.ignore
index d11fec9..a3517f0 100644
--- a/core/core/api/restricted_current.ignore
+++ b/core/core/api/restricted_current.ignore
@@ -1,7 +1,5 @@
 // Baseline format: 1.0
-InvalidNullConversion: androidx.core.app.PendingIntentCompat#getActivity(android.content.Context, int, android.content.Intent, int, android.os.Bundle, boolean):
-    Attempted to change method return from @NonNull to @Nullable: incompatible change for method androidx.core.app.PendingIntentCompat.getActivity(android.content.Context,int,android.content.Intent,int,android.os.Bundle,boolean)
-InvalidNullConversion: androidx.core.app.PendingIntentCompat#getActivity(android.content.Context, int, android.content.Intent, int, boolean):
-    Attempted to change method return from @NonNull to @Nullable: incompatible change for method androidx.core.app.PendingIntentCompat.getActivity(android.content.Context,int,android.content.Intent,int,boolean)
-InvalidNullConversion: androidx.core.app.PendingIntentCompat#getService(android.content.Context, int, android.content.Intent, int, boolean):
-    Attempted to change method return from @NonNull to @Nullable: incompatible change for method androidx.core.app.PendingIntentCompat.getService(android.content.Context,int,android.content.Intent,int,boolean)
+AddedMethod: androidx.core.util.TypedValueCompat#getUnitFromComplexDimension(int):
+    Added method androidx.core.util.TypedValueCompat.getUnitFromComplexDimension(int)
+AddedMethod: androidx.core.widget.TextViewCompat#setLineHeight(android.widget.TextView, int, float):
+    Added method androidx.core.widget.TextViewCompat.setLineHeight(android.widget.TextView,int,float)
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index 2fb7c56..7be35d5 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -2211,9 +2211,9 @@
     method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.Q) public static boolean isAtLeastQ();
     method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.R) public static boolean isAtLeastR();
     method @Deprecated @ChecksSdkIntAtLeast(api=31, codename="S") public static boolean isAtLeastS();
-    method @Deprecated @SuppressCompatibility @ChecksSdkIntAtLeast(api=32, codename="Sv2") @androidx.core.os.BuildCompat.PrereleaseSdkCheck public static boolean isAtLeastSv2();
-    method @Deprecated @SuppressCompatibility @ChecksSdkIntAtLeast(api=33, codename="Tiramisu") @androidx.core.os.BuildCompat.PrereleaseSdkCheck public static boolean isAtLeastT();
-    method @Deprecated @SuppressCompatibility @ChecksSdkIntAtLeast(api=34, codename="UpsideDownCake") @androidx.core.os.BuildCompat.PrereleaseSdkCheck public static boolean isAtLeastU();
+    method @Deprecated @ChecksSdkIntAtLeast(api=32, codename="Sv2") public static boolean isAtLeastSv2();
+    method @Deprecated @ChecksSdkIntAtLeast(api=33, codename="Tiramisu") public static boolean isAtLeastT();
+    method @Deprecated @ChecksSdkIntAtLeast(api=34, codename="UpsideDownCake") public static boolean isAtLeastU();
     method @SuppressCompatibility @ChecksSdkIntAtLeast(codename="VanillaIceCream") @androidx.core.os.BuildCompat.PrereleaseSdkCheck public static boolean isAtLeastV();
     field @ChecksSdkIntAtLeast(extension=android.os.ext.SdkExtensions.AD_SERVICES) public static final int AD_SERVICES_EXTENSION_INT;
     field public static final androidx.core.os.BuildCompat INSTANCE;
@@ -3865,13 +3865,18 @@
     method public static int getMovementGranularity(android.view.accessibility.AccessibilityEvent);
     method @Deprecated public static androidx.core.view.accessibility.AccessibilityRecordCompat! getRecord(android.view.accessibility.AccessibilityEvent!, int);
     method @Deprecated public static int getRecordCount(android.view.accessibility.AccessibilityEvent!);
+    method public static boolean isAccessibilityDataSensitive(android.view.accessibility.AccessibilityEvent);
+    method public static void setAccessibilityDataSensitive(android.view.accessibility.AccessibilityEvent, boolean);
     method public static void setAction(android.view.accessibility.AccessibilityEvent, int);
     method public static void setContentChangeTypes(android.view.accessibility.AccessibilityEvent, @androidx.core.view.accessibility.AccessibilityEventCompat.ContentChangeType int);
     method public static void setMovementGranularity(android.view.accessibility.AccessibilityEvent, int);
     field public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 4; // 0x4
+    field public static final int CONTENT_CHANGE_TYPE_CONTENT_INVALID = 1024; // 0x400
     field public static final int CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 512; // 0x200
     field public static final int CONTENT_CHANGE_TYPE_DRAG_DROPPED = 256; // 0x100
     field public static final int CONTENT_CHANGE_TYPE_DRAG_STARTED = 128; // 0x80
+    field public static final int CONTENT_CHANGE_TYPE_ENABLED = 4096; // 0x1000
+    field public static final int CONTENT_CHANGE_TYPE_ERROR = 2048; // 0x800
     field public static final int CONTENT_CHANGE_TYPE_PANE_APPEARED = 16; // 0x10
     field public static final int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 32; // 0x20
     field public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 8; // 0x8
@@ -3901,7 +3906,7 @@
     field @Deprecated public static final int TYPE_WINDOW_CONTENT_CHANGED = 2048; // 0x800
   }
 
-  @IntDef(flag=true, value={androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_SUBTREE, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_TEXT, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_UNDEFINED, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_DRAG_STARTED, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_DRAG_DROPPED, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_DRAG_CANCELLED}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface AccessibilityEventCompat.ContentChangeType {
+  @IntDef(flag=true, value={androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_SUBTREE, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_TEXT, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_UNDEFINED, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_DRAG_STARTED, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_DRAG_DROPPED, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_DRAG_CANCELLED, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_CONTENT_INVALID, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_ERROR, androidx.core.view.accessibility.AccessibilityEventCompat.CONTENT_CHANGE_TYPE_ENABLED}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface AccessibilityEventCompat.ContentChangeType {
   }
 
   public final class AccessibilityManagerCompat {
@@ -3909,6 +3914,7 @@
     method public static boolean addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager, androidx.core.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener);
     method @Deprecated public static java.util.List<android.accessibilityservice.AccessibilityServiceInfo!>! getEnabledAccessibilityServiceList(android.view.accessibility.AccessibilityManager!, int);
     method @Deprecated public static java.util.List<android.accessibilityservice.AccessibilityServiceInfo!>! getInstalledAccessibilityServiceList(android.view.accessibility.AccessibilityManager!);
+    method public static boolean isRequestFromAccessibilityTool(android.view.accessibility.AccessibilityManager);
     method @Deprecated public static boolean isTouchExplorationEnabled(android.view.accessibility.AccessibilityManager!);
     method @Deprecated public static boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager!, androidx.core.view.accessibility.AccessibilityManagerCompat.AccessibilityStateChangeListener!);
     method public static boolean removeTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager, androidx.core.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener);
@@ -3943,12 +3949,15 @@
     method public java.util.List<java.lang.String!> getAvailableExtraData();
     method @Deprecated public void getBoundsInParent(android.graphics.Rect!);
     method public void getBoundsInScreen(android.graphics.Rect!);
+    method public void getBoundsInWindow(android.graphics.Rect);
     method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getChild(int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? getChild(int, int);
     method public int getChildCount();
     method public CharSequence! getClassName();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static android.text.style.ClickableSpan![]! getClickableSpans(CharSequence!);
     method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat! getCollectionInfo();
     method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat! getCollectionItemInfo();
+    method public CharSequence? getContainerTitle();
     method public CharSequence! getContentDescription();
     method public int getDrawingOrder();
     method public CharSequence! getError();
@@ -3966,6 +3975,7 @@
     method public CharSequence! getPackageName();
     method public CharSequence? getPaneTitle();
     method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat! getParent();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? getParent(int);
     method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat! getRangeInfo();
     method public CharSequence? getRoleDescription();
     method public CharSequence? getStateDescription();
@@ -3981,6 +3991,7 @@
     method public androidx.core.view.accessibility.AccessibilityWindowInfoCompat! getWindow();
     method public int getWindowId();
     method public boolean hasRequestInitialAccessibilityFocus();
+    method public boolean isAccessibilityDataSensitive();
     method public boolean isAccessibilityFocused();
     method public boolean isCheckable();
     method public boolean isChecked();
@@ -4016,10 +4027,12 @@
     method public boolean removeAction(androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat!);
     method public boolean removeChild(android.view.View!);
     method public boolean removeChild(android.view.View!, int);
+    method public void setAccessibilityDataSensitive(boolean);
     method public void setAccessibilityFocused(boolean);
     method public void setAvailableExtraData(java.util.List<java.lang.String!>);
     method @Deprecated public void setBoundsInParent(android.graphics.Rect!);
     method public void setBoundsInScreen(android.graphics.Rect!);
+    method public void setBoundsInWindow(android.graphics.Rect);
     method public void setCanOpenPopup(boolean);
     method public void setCheckable(boolean);
     method public void setChecked(boolean);
@@ -4027,6 +4040,7 @@
     method public void setClickable(boolean);
     method public void setCollectionInfo(Object!);
     method public void setCollectionItemInfo(Object!);
+    method public void setContainerTitle(CharSequence?);
     method public void setContentDescription(CharSequence!);
     method public void setContentInvalid(boolean);
     method public void setContextClickable(boolean);
@@ -4057,6 +4071,7 @@
     method public void setParent(android.view.View!);
     method public void setParent(android.view.View!, int);
     method public void setPassword(boolean);
+    method public void setQueryFromAppProcessEnabled(android.view.View, boolean);
     method public void setRangeInfo(androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat!);
     method public void setRequestInitialAccessibilityFocus(boolean);
     method public void setRoleDescription(CharSequence?);
@@ -4122,8 +4137,15 @@
     field public static final int EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH = 20000; // 0x4e20
     field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX = "android.core.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX";
     field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY = "android.core.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";
+    field public static final int FLAG_PREFETCH_ANCESTORS = 1; // 0x1
+    field public static final int FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST = 16; // 0x10
+    field public static final int FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST = 8; // 0x8
+    field public static final int FLAG_PREFETCH_DESCENDANTS_HYBRID = 4; // 0x4
+    field public static final int FLAG_PREFETCH_SIBLINGS = 2; // 0x2
+    field public static final int FLAG_PREFETCH_UNINTERRUPTIBLE = 32; // 0x20
     field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2
     field public static final int FOCUS_INPUT = 1; // 0x1
+    field public static final int MAX_NUMBER_OF_PREFETCHED_NODES = 50; // 0x32
     field public static final int MOVEMENT_GRANULARITY_CHARACTER = 1; // 0x1
     field public static final int MOVEMENT_GRANULARITY_LINE = 4; // 0x4
     field public static final int MOVEMENT_GRANULARITY_PAGE = 16; // 0x10
@@ -4201,15 +4223,31 @@
   public static class AccessibilityNodeInfoCompat.CollectionItemInfoCompat {
     method public int getColumnIndex();
     method public int getColumnSpan();
+    method public String? getColumnTitle();
     method public int getRowIndex();
     method public int getRowSpan();
+    method public String? getRowTitle();
     method @Deprecated public boolean isHeading();
     method public boolean isSelected();
     method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat! obtain(int, int, int, int, boolean);
     method public static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat! obtain(int, int, int, int, boolean, boolean);
   }
 
+  public static final class AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder {
+    ctor public AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat build();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setColumnIndex(int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setColumnSpan(int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setColumnTitle(String?);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setHeading(boolean);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setRowIndex(int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setRowSpan(int);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setRowTitle(String?);
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder setSelected(boolean);
+  }
+
   public static class AccessibilityNodeInfoCompat.RangeInfoCompat {
+    ctor public AccessibilityNodeInfoCompat.RangeInfoCompat(int, float, float, float);
     method public float getCurrent();
     method public float getMax();
     method public float getMin();
@@ -4354,10 +4392,13 @@
     method public int getDisplayId();
     method public int getId();
     method public int getLayer();
+    method public androidx.core.os.LocaleListCompat getLocales();
     method public androidx.core.view.accessibility.AccessibilityWindowInfoCompat? getParent();
     method public void getRegionInScreen(android.graphics.Region);
     method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? getRoot();
+    method public androidx.core.view.accessibility.AccessibilityNodeInfoCompat? getRoot(int);
     method public CharSequence? getTitle();
+    method public long getTransitionTimeMillis();
     method public int getType();
     method public boolean isAccessibilityFocused();
     method public boolean isActive();
@@ -4370,6 +4411,7 @@
     field public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; // 0x4
     field public static final int TYPE_APPLICATION = 1; // 0x1
     field public static final int TYPE_INPUT_METHOD = 2; // 0x2
+    field public static final int TYPE_MAGNIFICATION_OVERLAY = 6; // 0x6
     field public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5; // 0x5
     field public static final int TYPE_SYSTEM = 3; // 0x3
   }
diff --git a/core/core/build.gradle b/core/core/build.gradle
index 149662a..967e12e 100644
--- a/core/core/build.gradle
+++ b/core/core/build.gradle
@@ -57,6 +57,7 @@
     androidTestImplementation(project(":internal-testutils-mockito"))
 
     testImplementation(libs.junit)
+    testImplementation(libs.testExtJunit)
     testImplementation(libs.testCore)
     testImplementation(libs.testRunner)
     testImplementation(libs.truth)
diff --git a/core/core/src/androidTest/java/androidx/core/math/MathUtilsTest.java b/core/core/src/androidTest/java/androidx/core/math/MathUtilsTest.java
index 856acd4..a717d3d 100644
--- a/core/core/src/androidTest/java/androidx/core/math/MathUtilsTest.java
+++ b/core/core/src/androidTest/java/androidx/core/math/MathUtilsTest.java
@@ -17,7 +17,6 @@
 package androidx.core.math;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThrows;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
@@ -30,78 +29,6 @@
 public class MathUtilsTest {
 
     @Test
-    public void testAddExact() {
-        assertEquals(2, MathUtils.addExact(1, 1));
-        assertEquals(2L, MathUtils.addExact(1L, 1L));
-        assertEquals(0, MathUtils.addExact(1, -1));
-        assertEquals(0L, MathUtils.addExact(1L, -1L));
-        assertThrows(ArithmeticException.class, () -> MathUtils.addExact(Integer.MAX_VALUE, 1));
-        assertThrows(ArithmeticException.class, () -> MathUtils.addExact(Long.MAX_VALUE, 1L));
-        assertThrows(ArithmeticException.class, () -> MathUtils.addExact(Integer.MIN_VALUE, -1));
-        assertThrows(ArithmeticException.class, () -> MathUtils.addExact(Long.MIN_VALUE, -1L));
-    }
-
-    @Test
-    public void testSubtractExact() {
-        assertEquals(0, MathUtils.subtractExact(1, 1));
-        assertEquals(0L, MathUtils.subtractExact(1L, 1L));
-        assertEquals(2, MathUtils.subtractExact(1, -1));
-        assertEquals(2L, MathUtils.subtractExact(1L, -1L));
-        assertThrows(ArithmeticException.class,
-                () -> MathUtils.subtractExact(Integer.MAX_VALUE, -1));
-        assertThrows(ArithmeticException.class, () -> MathUtils.subtractExact(Long.MAX_VALUE, -1L));
-        assertThrows(ArithmeticException.class,
-                () -> MathUtils.subtractExact(Integer.MIN_VALUE, 1));
-        assertThrows(ArithmeticException.class, () -> MathUtils.subtractExact(Long.MIN_VALUE, 1L));
-    }
-
-    @Test
-    public void testMultiplyExact() {
-        assertEquals(4, MathUtils.multiplyExact(2, 2));
-        assertEquals(4L, MathUtils.multiplyExact(2L, 2L));
-        assertEquals(0, MathUtils.multiplyExact(2, 0));
-        assertEquals(0L, MathUtils.multiplyExact(2L, 0L));
-        assertEquals(-4, MathUtils.multiplyExact(2, -2));
-        assertEquals(-4L, MathUtils.multiplyExact(2L, -2L));
-        assertThrows(ArithmeticException.class,
-                () -> MathUtils.multiplyExact(Integer.MAX_VALUE, 2));
-        assertThrows(ArithmeticException.class, () -> MathUtils.multiplyExact(Long.MAX_VALUE, 2L));
-        assertThrows(ArithmeticException.class,
-                () -> MathUtils.multiplyExact(Integer.MIN_VALUE, 2));
-        assertThrows(ArithmeticException.class, () -> MathUtils.multiplyExact(Long.MIN_VALUE, 2L));
-    }
-
-    @Test
-    public void testIncrementExact() {
-        assertEquals(1, MathUtils.incrementExact(0));
-        assertEquals(1L, MathUtils.incrementExact(0L));
-        assertThrows(ArithmeticException.class, () -> MathUtils.incrementExact(Integer.MAX_VALUE));
-        assertThrows(ArithmeticException.class, () -> MathUtils.incrementExact(Long.MAX_VALUE));
-    }
-
-    @Test
-    public void testDecrementExact() {
-        assertEquals(-1, MathUtils.decrementExact(0));
-        assertEquals(-1L, MathUtils.decrementExact(0L));
-        assertThrows(ArithmeticException.class, () -> MathUtils.decrementExact(Integer.MIN_VALUE));
-        assertThrows(ArithmeticException.class, () -> MathUtils.decrementExact(Long.MIN_VALUE));
-    }
-
-    @Test
-    public void testNegateExact() {
-        assertEquals(Integer.MIN_VALUE + 1, MathUtils.negateExact(Integer.MAX_VALUE));
-        assertEquals(Long.MIN_VALUE + 1, MathUtils.negateExact(Long.MAX_VALUE));
-        assertThrows(ArithmeticException.class, () -> MathUtils.negateExact(Integer.MIN_VALUE));
-        assertThrows(ArithmeticException.class, () -> MathUtils.negateExact(Long.MIN_VALUE));
-    }
-
-    @Test
-    public void testToIntExact() {
-        assertEquals(1, MathUtils.toIntExact(1L));
-        assertThrows(ArithmeticException.class, () -> MathUtils.toIntExact(Long.MAX_VALUE));
-    }
-
-    @Test
     public void testClamp() {
         // Int
         assertEquals(0, MathUtils.clamp(-4, 0, 7));
diff --git a/core/core/src/androidTest/java/androidx/core/service/quicksettings/TileServiceCompatTest.java b/core/core/src/androidTest/java/androidx/core/service/quicksettings/TileServiceCompatTest.java
index a78f703..836591a 100644
--- a/core/core/src/androidTest/java/androidx/core/service/quicksettings/TileServiceCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/service/quicksettings/TileServiceCompatTest.java
@@ -59,7 +59,7 @@
         Bundle options = new Bundle();
         PendingIntentActivityWrapper wrapper = new PendingIntentActivityWrapper(mContext,
                 requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT, options, /* isMutable = */
-                true);
+                false);
         TileServiceCompat.setTileServiceWrapper(tileServiceWrapper);
 
         TileServiceCompat.startActivityAndCollapse(tileService, wrapper);
diff --git a/core/core/src/androidTest/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompatTest.java b/core/core/src/androidTest/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompatTest.java
index e0cc255..d6fd006 100644
--- a/core/core/src/androidTest/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompatTest.java
@@ -16,11 +16,12 @@
 
 package androidx.core.view.accessibility;
 
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.core.IsEqual.equalTo;
+import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Build;
 import android.view.View;
@@ -46,23 +47,104 @@
 @RunWith(AndroidJUnit4.class)
 public class AccessibilityNodeInfoCompatTest {
     @Test
-    public void testSetCollectionInfoIsNullable() throws Exception {
+    public void testSetCollectionInfoIsNullable() {
         AccessibilityNodeInfoCompat accessibilityNodeInfoCompat = obtainedWrappedNodeCompat();
         accessibilityNodeInfoCompat.setCollectionInfo(null);
     }
 
     @Test
-    public void testSetCollectionItemInfoIsNullable() throws Exception {
+    public void testSetCollectionItemInfoIsNullable() {
         AccessibilityNodeInfoCompat accessibilityNodeInfoCompat = obtainedWrappedNodeCompat();
         accessibilityNodeInfoCompat.setCollectionItemInfo(null);
     }
 
     @Test
+    public void testSetCollectionItemInfoCompatBuilder_withDefaultValues() {
+        AccessibilityNodeInfoCompat.CollectionItemInfoCompat collectionItemInfoCompat =
+                new AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder().build();
+
+        assertThat(collectionItemInfoCompat.getColumnIndex()).isEqualTo(0);
+        assertThat(collectionItemInfoCompat.getColumnSpan()).isEqualTo(0);
+        assertThat(collectionItemInfoCompat.getColumnTitle()).isNull();
+
+        assertThat(collectionItemInfoCompat.getRowIndex()).isEqualTo(0);
+        assertThat(collectionItemInfoCompat.getRowSpan()).isEqualTo(0);
+        assertThat(collectionItemInfoCompat.getRowTitle()).isNull();
+        assertThat(collectionItemInfoCompat.isSelected()).isFalse();
+        assertThat(collectionItemInfoCompat.isHeading()).isFalse();
+    }
+
+    @Test
+    public void testSetCollectionInfoCompatBuilder_withRealValues() {
+        AccessibilityNodeInfoCompat.CollectionItemInfoCompat collectionItemInfoCompat =
+                new AccessibilityNodeInfoCompat.CollectionItemInfoCompat.Builder()
+                        .setColumnIndex(2)
+                        .setColumnSpan(1)
+                        .setColumnTitle("Column title")
+                        .setRowIndex(1)
+                        .setRowSpan(2)
+                        .setRowTitle("Row title")
+                        .setSelected(true)
+                        .setHeading(true)
+                        .build();
+
+        if (Build.VERSION.SDK_INT >= 33) {
+            assertThat(collectionItemInfoCompat.getColumnTitle()).isEqualTo("Column title");
+            assertThat(collectionItemInfoCompat.getRowTitle()).isEqualTo("Row title");
+        }
+
+        if (Build.VERSION.SDK_INT >= 21) {
+            assertThat(collectionItemInfoCompat.isSelected()).isTrue();
+        }
+
+        assertThat(collectionItemInfoCompat.getColumnIndex()).isEqualTo(2);
+        assertThat(collectionItemInfoCompat.getColumnSpan()).isEqualTo(1);
+        assertThat(collectionItemInfoCompat.getRowIndex()).isEqualTo(1);
+        assertThat(collectionItemInfoCompat.getRowSpan()).isEqualTo(2);
+        assertThat(collectionItemInfoCompat.isHeading()).isTrue();
+    }
+
+    @SdkSuppress(minSdkVersion = 19)
+    @Test
+    public void testRangeInfoCompatConstructor_always_returnsRangeInfoCompat() {
+        AccessibilityNodeInfoCompat.RangeInfoCompat rangeInfoCompat =
+                new AccessibilityNodeInfoCompat.RangeInfoCompat(
+                        AccessibilityNodeInfoCompat.RangeInfoCompat.RANGE_TYPE_INT, 0, 100, 50);
+        assertThat(rangeInfoCompat.getType()).isEqualTo(
+                AccessibilityNodeInfoCompat.RangeInfoCompat.RANGE_TYPE_INT);
+        assertThat(rangeInfoCompat.getMin()).isEqualTo(0f);
+        assertThat(rangeInfoCompat.getMax()).isEqualTo(100f);
+        assertThat(rangeInfoCompat.getCurrent()).isEqualTo(50f);
+    }
+
+    @SdkSuppress(minSdkVersion = 33)
+    @Test
+    public void testGetChild_withPrefetchingStrategy_returnsChild() {
+        AccessibilityNodeInfo accessibilityNodeInfo = mock(AccessibilityNodeInfo.class);
+        AccessibilityNodeInfoCompat nodeCompat = AccessibilityNodeInfoCompat.wrap(
+                accessibilityNodeInfo);
+        nodeCompat.getChild(0, AccessibilityNodeInfoCompat.FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST);
+        verify(accessibilityNodeInfo).getChild(0,
+                AccessibilityNodeInfoCompat.FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST);
+    }
+
+    @SdkSuppress(minSdkVersion = 33)
+    @Test
+    public void testGetChild_withPrefetchingStrategy_returnsParent() {
+        AccessibilityNodeInfo accessibilityNodeInfo = mock(AccessibilityNodeInfo.class);
+        AccessibilityNodeInfoCompat nodeCompat = AccessibilityNodeInfoCompat.wrap(
+                accessibilityNodeInfo);
+        nodeCompat.getParent(AccessibilityNodeInfoCompat.FLAG_PREFETCH_ANCESTORS);
+        verify(accessibilityNodeInfo).getParent(
+                AccessibilityNodeInfoCompat.FLAG_PREFETCH_ANCESTORS);
+    }
+
+    @Test
     public void testGetSetHintText() {
         final CharSequence hintText = (Build.VERSION.SDK_INT >= 19) ? "hint text" : null;
         AccessibilityNodeInfoCompat nodeCompat = obtainedWrappedNodeCompat();
         nodeCompat.setHintText(hintText);
-        assertThat(nodeCompat.getHintText(), equalTo(hintText));
+        assertThat(nodeCompat.getHintText()).isEqualTo(hintText);
     }
 
     @Test
@@ -70,7 +152,7 @@
         final CharSequence paneTitle = (Build.VERSION.SDK_INT >= 19) ? "pane title" : null;
         AccessibilityNodeInfoCompat nodeCompat = obtainedWrappedNodeCompat();
         nodeCompat.setPaneTitle(paneTitle);
-        assertThat(nodeCompat.getPaneTitle(), equalTo(paneTitle));
+        assertThat(nodeCompat.getPaneTitle()).isEqualTo(paneTitle);
     }
 
     @Test
@@ -78,7 +160,7 @@
         final CharSequence tooltipText = (Build.VERSION.SDK_INT >= 19) ? "tooltip" : null;
         AccessibilityNodeInfoCompat nodeCompat = obtainedWrappedNodeCompat();
         nodeCompat.setTooltipText(tooltipText);
-        assertThat(nodeCompat.getTooltipText(), equalTo(tooltipText));
+        assertThat(nodeCompat.getTooltipText()).isEqualTo(tooltipText);
     }
 
     @SdkSuppress(minSdkVersion = 19)
@@ -86,9 +168,9 @@
     public void testGetSetShowingHintText() {
         AccessibilityNodeInfoCompat nodeCompat = obtainedWrappedNodeCompat();
         nodeCompat.setShowingHintText(true);
-        assertThat(nodeCompat.isShowingHintText(), is(true));
+        assertThat(nodeCompat.isShowingHintText()).isTrue();
         nodeCompat.setShowingHintText(false);
-        assertThat(nodeCompat.isShowingHintText(), is(false));
+        assertThat(nodeCompat.isShowingHintText()).isFalse();
     }
 
     @SdkSuppress(minSdkVersion = 19)
@@ -96,9 +178,9 @@
     public void testGetSetScreenReaderFocusable() {
         AccessibilityNodeInfoCompat nodeCompat = obtainedWrappedNodeCompat();
         nodeCompat.setScreenReaderFocusable(true);
-        assertThat(nodeCompat.isScreenReaderFocusable(), is(true));
+        assertThat(nodeCompat.isScreenReaderFocusable()).isTrue();
         nodeCompat.setScreenReaderFocusable(false);
-        assertThat(nodeCompat.isScreenReaderFocusable(), is(false));
+        assertThat(nodeCompat.isScreenReaderFocusable()).isFalse();
     }
 
     @SdkSuppress(minSdkVersion = 19)
@@ -106,24 +188,54 @@
     public void testGetSetMinDurationBetweenContentChanges() {
         AccessibilityNodeInfoCompat nodeCompat = obtainedWrappedNodeCompat();
         nodeCompat.setMinDurationBetweenContentChangesMillis(200L);
-        assertThat(nodeCompat.getMinDurationBetweenContentChangesMillis(), equalTo(200L));
+        assertThat(nodeCompat.getMinDurationBetweenContentChangesMillis()).isEqualTo(200L);
     }
 
     @SdkSuppress(minSdkVersion = 19)
     @Test
-    public void testGetHasRequestInitialAccessibilityFocus() {
+    public void testGetSetRequestInitialAccessibilityFocus() {
         AccessibilityNodeInfoCompat nodeCompat = obtainedWrappedNodeCompat();
         nodeCompat.setRequestInitialAccessibilityFocus(true);
-        assertThat(nodeCompat.hasRequestInitialAccessibilityFocus(), is(true));
+        assertThat(nodeCompat.hasRequestInitialAccessibilityFocus()).isTrue();
+
+    }
+
+    @Test
+    public void testGetSetContainerTitle() {
+        final CharSequence containerTitle = (Build.VERSION.SDK_INT >= 19) ? "title" : null;
+        AccessibilityNodeInfoCompat nodeCompat = obtainedWrappedNodeCompat();
+        nodeCompat.setContainerTitle(containerTitle);
+        assertThat(nodeCompat.getContainerTitle()).isEqualTo(containerTitle);
+    }
+
+    @SdkSuppress(minSdkVersion = 19)
+    @Test
+    public void testGetBoundsInWindow() {
+        AccessibilityNodeInfoCompat nodeCompat = obtainedWrappedNodeCompat();
+        Rect bounds = new Rect(0, 0, 50, 50);
+        nodeCompat.setBoundsInWindow(bounds);
+        Rect outBounds = new Rect();
+        nodeCompat.getBoundsInWindow(outBounds);
+        assertThat(bounds).isEqualTo(outBounds);
+    }
+
+    @SdkSuppress(minSdkVersion = 34)
+    @Test
+    public void testSetQueryFromAppProcessEnabled() {
+        AccessibilityNodeInfo accessibilityNodeInfo = mock(AccessibilityNodeInfo.class);
+        AccessibilityNodeInfoCompat nodeCompat = AccessibilityNodeInfoCompat.wrap(
+                accessibilityNodeInfo);
+        nodeCompat.setQueryFromAppProcessEnabled(null, true);
+        verify(accessibilityNodeInfo).setQueryFromAppProcessEnabled(null, true);
     }
 
     @SdkSuppress(minSdkVersion = 19)
     @Test
     public void testisGranularScrollingSupported() {
         AccessibilityNodeInfoCompat nodeCompat = obtainedWrappedNodeCompat();
-        assertThat(nodeCompat.isGranularScrollingSupported(), is(false));
+        assertThat(nodeCompat.isGranularScrollingSupported()).isFalse();
         nodeCompat.setGranularScrollingSupported(true);
-        assertThat(nodeCompat.isGranularScrollingSupported(), is(true));
+        assertThat(nodeCompat.isGranularScrollingSupported()).isTrue();
     }
 
     @SdkSuppress(minSdkVersion = 19)
@@ -131,13 +243,13 @@
     public void testGetSetHeading() {
         AccessibilityNodeInfoCompat nodeCompat = obtainedWrappedNodeCompat();
         nodeCompat.setHeading(true);
-        assertThat(nodeCompat.isHeading(), is(true));
+        assertThat(nodeCompat.isHeading()).isTrue();
         nodeCompat.setHeading(false);
-        assertThat(nodeCompat.isHeading(), is(false));
+        assertThat(nodeCompat.isHeading()).isFalse();
         AccessibilityNodeInfoCompat.CollectionItemInfoCompat collectionItemInfo =
                 AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(0, 1, 0, 1, true);
         nodeCompat.setCollectionItemInfo(collectionItemInfo);
-        assertThat(nodeCompat.isHeading(), is(true));
+        assertThat(nodeCompat.isHeading()).isTrue();
     }
 
     @SdkSuppress(minSdkVersion = 19)
@@ -145,9 +257,20 @@
     public void testGetSetTextEntryKey() {
         AccessibilityNodeInfoCompat nodeCompat = obtainedWrappedNodeCompat();
         nodeCompat.setTextEntryKey(true);
-        assertThat(nodeCompat.isTextEntryKey(), is(true));
+        assertThat(nodeCompat.isTextEntryKey()).isTrue();
         nodeCompat.setTextEntryKey(false);
-        assertThat(nodeCompat.isTextEntryKey(), is(false));
+        assertThat(nodeCompat.isTextEntryKey()).isFalse();
+    }
+
+    @SdkSuppress(minSdkVersion = 19)
+    @Test
+    public void testGetSetAccessibilityDataSensitive() {
+        AccessibilityNodeInfoCompat accessibilityNodeInfoCompat = obtainedWrappedNodeCompat();
+
+        accessibilityNodeInfoCompat.setAccessibilityDataSensitive(true);
+        assertThat(accessibilityNodeInfoCompat.isAccessibilityDataSensitive()).isTrue();
+        accessibilityNodeInfoCompat.setAccessibilityDataSensitive(false);
+        assertThat(accessibilityNodeInfoCompat.isAccessibilityDataSensitive()).isFalse();
     }
 
     @Test
@@ -155,7 +278,7 @@
         final String uniqueId = (Build.VERSION.SDK_INT >= 19) ? "localUId" : null;
         AccessibilityNodeInfoCompat nodeCompat = obtainedWrappedNodeCompat();
         nodeCompat.setUniqueId(uniqueId);
-        assertThat(nodeCompat.getUniqueId(), equalTo(uniqueId));
+        assertThat(nodeCompat.getUniqueId()).isEqualTo(uniqueId);
     }
 
     @SdkSuppress(minSdkVersion = 19)
@@ -164,44 +287,44 @@
         try {
             AccessibilityActionCompat actionCompat;
             actionCompat = AccessibilityActionCompat.ACTION_SHOW_ON_SCREEN;
-            assertThat(actionCompat.getId(),
-                    is(getExpectedActionId(android.R.id.accessibilityActionShowOnScreen)));
+            assertThat(actionCompat.getId())
+                    .isEqualTo(getExpectedActionId(android.R.id.accessibilityActionShowOnScreen));
             actionCompat = AccessibilityActionCompat.ACTION_SCROLL_TO_POSITION;
-            assertThat(actionCompat.getId(),
-                    is(getExpectedActionId(android.R.id.accessibilityActionScrollToPosition)));
+            assertThat(actionCompat.getId()).isEqualTo(
+                    getExpectedActionId(android.R.id.accessibilityActionScrollToPosition));
             actionCompat = AccessibilityActionCompat.ACTION_SCROLL_UP;
-            assertThat(actionCompat.getId(),
-                    is(getExpectedActionId(android.R.id.accessibilityActionScrollUp)));
+            assertThat(actionCompat.getId())
+                    .isEqualTo(getExpectedActionId(android.R.id.accessibilityActionScrollUp));
             actionCompat = AccessibilityActionCompat.ACTION_SCROLL_LEFT;
-            assertThat(actionCompat.getId(),
-                    is(getExpectedActionId(android.R.id.accessibilityActionScrollLeft)));
+            assertThat(actionCompat.getId())
+                    .isEqualTo(getExpectedActionId(android.R.id.accessibilityActionScrollLeft));
             actionCompat = AccessibilityActionCompat.ACTION_SCROLL_DOWN;
-            assertThat(actionCompat.getId(),
-                    is(getExpectedActionId(android.R.id.accessibilityActionScrollDown)));
+            assertThat(actionCompat.getId())
+                    .isEqualTo(getExpectedActionId(android.R.id.accessibilityActionScrollDown));
             actionCompat = AccessibilityActionCompat.ACTION_SCROLL_RIGHT;
-            assertThat(actionCompat.getId(),
-                    is(getExpectedActionId(android.R.id.accessibilityActionScrollRight)));
+            assertThat(actionCompat.getId())
+                    .isEqualTo(getExpectedActionId(android.R.id.accessibilityActionScrollRight));
             actionCompat = AccessibilityActionCompat.ACTION_CONTEXT_CLICK;
-            assertThat(actionCompat.getId(),
-                    is(getExpectedActionId(android.R.id.accessibilityActionContextClick)));
+            assertThat(actionCompat.getId())
+                    .isEqualTo(getExpectedActionId(android.R.id.accessibilityActionContextClick));
             actionCompat = AccessibilityActionCompat.ACTION_SET_PROGRESS;
-            assertThat(actionCompat.getId(),
-                    is(getExpectedActionId(android.R.id.accessibilityActionSetProgress)));
+            assertThat(actionCompat.getId())
+                    .isEqualTo(getExpectedActionId(android.R.id.accessibilityActionSetProgress));
             actionCompat = AccessibilityActionCompat.ACTION_MOVE_WINDOW;
-            assertThat(actionCompat.getId(),
-                    is(getExpectedActionId(android.R.id.accessibilityActionMoveWindow)));
+            assertThat(actionCompat.getId())
+                    .isEqualTo(getExpectedActionId(android.R.id.accessibilityActionMoveWindow));
             actionCompat = AccessibilityActionCompat.ACTION_SHOW_TOOLTIP;
-            assertThat(actionCompat.getId(),
-                    is(getExpectedActionId(android.R.id.accessibilityActionShowTooltip)));
+            assertThat(actionCompat.getId())
+                    .isEqualTo(getExpectedActionId(android.R.id.accessibilityActionShowTooltip));
             actionCompat = AccessibilityActionCompat.ACTION_HIDE_TOOLTIP;
-            assertThat(actionCompat.getId(),
-                    is(getExpectedActionId(android.R.id.accessibilityActionHideTooltip)));
+            assertThat(actionCompat.getId())
+                    .isEqualTo(getExpectedActionId(android.R.id.accessibilityActionHideTooltip));
             actionCompat = AccessibilityActionCompat.ACTION_PRESS_AND_HOLD;
-            assertThat(actionCompat.getId(),
-                    is(getExpectedActionId(android.R.id.accessibilityActionPressAndHold)));
+            assertThat(actionCompat.getId())
+                    .isEqualTo(getExpectedActionId(android.R.id.accessibilityActionPressAndHold));
             actionCompat = AccessibilityActionCompat.ACTION_IME_ENTER;
-            assertThat(actionCompat.getId(),
-                    is(getExpectedActionId(android.R.id.accessibilityActionImeEnter)));
+            assertThat(actionCompat.getId())
+                    .isEqualTo(getExpectedActionId(android.R.id.accessibilityActionImeEnter));
         } catch (NullPointerException e) {
             Assert.fail("Expected no NullPointerException, but got: " + e.getMessage());
         }
@@ -213,10 +336,11 @@
         AccessibilityActionCompat actionCompat;
         actionCompat = AccessibilityActionCompat.ACTION_SHOW_ON_SCREEN;
         final String showOnScreen = "AccessibilityActionCompat: ACTION_SHOW_ON_SCREEN";
-        assertThat(actionCompat.toString(), is(showOnScreen));
+        assertThat(actionCompat.toString()).isEqualTo(showOnScreen);
         final String customAction = "CustomAction";
         actionCompat = new AccessibilityActionCompat(123123123, customAction);
-        assertThat(actionCompat.toString(), is("AccessibilityActionCompat: " + customAction));
+        assertThat(actionCompat.toString()).isEqualTo("AccessibilityActionCompat: "
+                + customAction);
     }
 
     @Test
@@ -231,15 +355,15 @@
         final TouchDelegateInfoCompat touchDelegateInfoResult =
                 accessibilityNodeInfoCompat.getTouchDelegateInfo();
         if (Build.VERSION.SDK_INT >= 29) {
-            assertThat(touchDelegateInfoResult.getRegionCount(), is(1));
-            assertThat(touchDelegateInfoResult.getRegionAt(0), is(region));
+            assertThat(touchDelegateInfoResult.getRegionCount()).isEqualTo(1);
+            assertThat(touchDelegateInfoResult.getRegionAt(0)).isEqualTo(region);
             // getTargetForRegion return null, since we are not a11y service
-            assertThat(touchDelegateInfoResult.getTargetForRegion(region), is(nullValue()));
+            assertThat(touchDelegateInfoResult.getTargetForRegion(region)).isNull();
         } else {
-            assertThat(touchDelegateInfoResult, is(nullValue()));
-            assertThat(delegateInfo.getRegionCount(), is(0));
-            assertThat(delegateInfo.getRegionAt(0), is(nullValue()));
-            assertThat(delegateInfo.getTargetForRegion(region), is(nullValue()));
+            assertThat(touchDelegateInfoResult).isNull();
+            assertThat(delegateInfo.getRegionCount()).isEqualTo(0);
+            assertThat(delegateInfo.getRegionAt(0)).isNull();
+            assertThat(delegateInfo.getTargetForRegion(region)).isNull();
         }
     }
 
@@ -252,8 +376,8 @@
         // Wrapped AccessibilityAction
         AccessibilityActionCompat wrappedAction = new AccessibilityActionCompat(
                         AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
-        assertThat(staticAction.equals(wrappedAction), equalTo(true));
-        assertThat(staticAction.hashCode() == wrappedAction.hashCode(), equalTo(true));
+        assertThat(staticAction.equals(wrappedAction)).isTrue();
+        assertThat(staticAction.hashCode() == wrappedAction.hashCode()).isTrue();
     }
 
     @SdkSuppress(minSdkVersion = 21)
@@ -264,8 +388,8 @@
         // AccessibilityActionCompat defined by id and label
         AccessibilityActionCompat wrappedIdAndLabelAction = new AccessibilityActionCompat(
                 AccessibilityNodeInfoCompat.ACTION_LONG_CLICK, "label", null);
-        assertThat(staticAction.equals(wrappedIdAndLabelAction), equalTo(true));
-        assertThat(staticAction.hashCode() == wrappedIdAndLabelAction.hashCode(), equalTo(true));
+        assertThat(staticAction.equals(wrappedIdAndLabelAction)).isTrue();
+        assertThat(staticAction.hashCode() == wrappedIdAndLabelAction.hashCode()).isTrue();
     }
 
     @SdkSuppress(minSdkVersion = 21)
@@ -275,23 +399,23 @@
                 AccessibilityActionCompat.ACTION_LONG_CLICK;
         AccessibilityActionCompat staticClickAction = AccessibilityActionCompat.ACTION_CLICK;
 
-        assertThat(staticLongClickAction.equals(staticClickAction), equalTo(false));
-        assertThat(staticLongClickAction.hashCode() == staticClickAction.hashCode(),
-                equalTo(false));
+        assertThat(staticLongClickAction.equals(staticClickAction)).isFalse();
+        assertThat(staticLongClickAction.hashCode() == staticClickAction.hashCode())
+                .isFalse();
 
         // AccessibilityActionCompat defined by id
         AccessibilityActionCompat wrappedIdLongClickAction = new AccessibilityActionCompat(
                 AccessibilityNodeInfoCompat.ACTION_LONG_CLICK, null, null);
-        assertThat(wrappedIdLongClickAction.equals(staticClickAction), equalTo(false));
-        assertThat(wrappedIdLongClickAction.hashCode() == staticClickAction.hashCode(),
-                equalTo(false));
+        assertThat(wrappedIdLongClickAction.equals(staticClickAction)).isFalse();
+        assertThat(wrappedIdLongClickAction.hashCode() == staticClickAction.hashCode())
+                .isFalse();
 
         // Wrapped AccessibilityAction
         AccessibilityActionCompat wrappedLongClickAction = new AccessibilityActionCompat(
                 AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
-        assertThat(wrappedLongClickAction.equals(staticClickAction), equalTo(false));
-        assertThat(wrappedLongClickAction.hashCode() == staticClickAction.hashCode(),
-                equalTo(false));
+        assertThat(wrappedLongClickAction.equals(staticClickAction)).isFalse();
+        assertThat(wrappedLongClickAction.hashCode() == staticClickAction.hashCode())
+                .isFalse();
     }
 
     private AccessibilityNodeInfoCompat obtainedWrappedNodeCompat() {
@@ -311,7 +435,7 @@
         final List<String> testData = Arrays.asList(new String[]{"A", "B"});
 
         accessibilityNodeInfoCompat.setAvailableExtraData(testData);
-        assertThat(accessibilityNodeInfoCompat.getAvailableExtraData(), equalTo(testData));
+        assertThat(accessibilityNodeInfoCompat.getAvailableExtraData()).isEqualTo(testData);
     }
 
     @SdkSuppress(minSdkVersion = 33)
@@ -320,8 +444,8 @@
     public void testGetExtraRenderingInfo() {
         AccessibilityNodeInfoCompat accessibilityNodeInfoCompat = obtainedWrappedNodeCompat();
         assertThat(
-                accessibilityNodeInfoCompat.getExtraRenderingInfo(),
-                equalTo(accessibilityNodeInfoCompat.unwrap().getExtraRenderingInfo()));
+                accessibilityNodeInfoCompat.getExtraRenderingInfo()).isEqualTo(
+                        accessibilityNodeInfoCompat.unwrap().getExtraRenderingInfo());
     }
 
     @SdkSuppress(minSdkVersion = 19)
@@ -330,9 +454,9 @@
     public void testSetGetTextSelectable() {
         AccessibilityNodeInfoCompat accessibilityNodeInfoCompat = obtainedWrappedNodeCompat();
         accessibilityNodeInfoCompat.setTextSelectable(false);
-        assertThat(accessibilityNodeInfoCompat.isTextSelectable(), equalTo(false));
+        assertThat(accessibilityNodeInfoCompat.isTextSelectable()).isFalse();
         accessibilityNodeInfoCompat.setTextSelectable(true);
-        assertThat(accessibilityNodeInfoCompat.isTextSelectable(), equalTo(true));
+        assertThat(accessibilityNodeInfoCompat.isTextSelectable()).isTrue();
     }
 
     @SmallTest
@@ -340,9 +464,9 @@
     public void testActionScrollInDirection() {
         AccessibilityActionCompat actionCompat =
                 AccessibilityActionCompat.ACTION_SCROLL_IN_DIRECTION;
-        assertThat(actionCompat.getId(),
-                is(getExpectedActionId(android.R.id.accessibilityActionScrollInDirection)));
-        assertThat(actionCompat.toString(), is("AccessibilityActionCompat: "
-                + "ACTION_SCROLL_IN_DIRECTION"));
+        assertThat(actionCompat.getId()).isEqualTo(getExpectedActionId(
+                android.R.id.accessibilityActionScrollInDirection));
+        assertThat(actionCompat.toString()).isEqualTo("AccessibilityActionCompat: "
+                + "ACTION_SCROLL_IN_DIRECTION");
     }
 }
diff --git a/core/core/src/androidTest/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompatTest.java b/core/core/src/androidTest/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompatTest.java
index 1788e22..a9a25b9 100644
--- a/core/core/src/androidTest/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompatTest.java
@@ -16,15 +16,19 @@
 
 package androidx.core.view.accessibility;
 
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.core.Is.is;
-import static org.hamcrest.core.IsEqual.equalTo;
-import static org.hamcrest.core.IsNot.not;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.annotation.TargetApi;
 import android.graphics.Region;
+import android.os.Build;
+import android.os.LocaleList;
 import android.view.accessibility.AccessibilityWindowInfo;
 
+import androidx.core.os.LocaleListCompat;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SdkSuppress;
 import androidx.test.filters.SmallTest;
@@ -32,6 +36,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Locale;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class AccessibilityWindowInfoCompatTest {
@@ -49,8 +55,8 @@
         AccessibilityWindowInfoCompat infoCompat = new AccessibilityWindowInfoCompat();
         AccessibilityWindowInfo info = new AccessibilityWindowInfo();
 
-        assertThat(infoCompat.unwrap(), is(not(equalTo(null))));
-        assertThat(infoCompat.unwrap(), equalTo(info));
+        assertThat(infoCompat.unwrap()).isNotNull();
+        assertThat(infoCompat.unwrap()).isEqualTo(info);
     }
 
     @SdkSuppress(minSdkVersion = 33)
@@ -60,16 +66,16 @@
         AccessibilityWindowInfo accessibilityWindowInfo = AccessibilityWindowInfo.obtain();
         AccessibilityWindowInfoCompat accessibilityWindowInfoCompat =
                 AccessibilityWindowInfoCompat.wrapNonNullInstance(accessibilityWindowInfo);
-        assertThat(accessibilityWindowInfoCompat.unwrap(), equalTo(accessibilityWindowInfo));
+        assertThat(accessibilityWindowInfoCompat.unwrap()).isEqualTo(accessibilityWindowInfo);
     }
 
-    @SdkSuppress(minSdkVersion = 33)
+    @SdkSuppress(minSdkVersion = 26)
     @SmallTest
     @Test
     public void testIsPictureInPictureMode() {
         AccessibilityWindowInfoCompat windowInfoCompat = obtainedWrappedWindowCompat();
-        assertThat(windowInfoCompat.isInPictureInPictureMode(), equalTo(
-                windowInfoCompat.unwrap().isInPictureInPictureMode()));
+        assertThat(windowInfoCompat.isInPictureInPictureMode()).isEqualTo(
+                windowInfoCompat.unwrap().isInPictureInPictureMode());
     }
 
     @SdkSuppress(minSdkVersion = 33)
@@ -77,8 +83,8 @@
     @Test
     public void testGetDisplayId() {
         AccessibilityWindowInfoCompat windowInfoCompat = obtainedWrappedWindowCompat();
-        assertThat(windowInfoCompat.getDisplayId(), equalTo(
-                windowInfoCompat.unwrap().getDisplayId()));
+        assertThat(windowInfoCompat.getDisplayId()).isEqualTo(
+                windowInfoCompat.unwrap().getDisplayId());
     }
 
     @SdkSuppress(minSdkVersion = 33)
@@ -90,6 +96,54 @@
         Region compatRegion = new Region();
         windowInfoCompat.unwrap().getRegionInScreen(region);
         windowInfoCompat.getRegionInScreen(region);
-        assertThat(region, equalTo(compatRegion));
+        assertThat(region).isEqualTo(compatRegion);
+    }
+    @SmallTest
+    @Test
+    public void testGetTransitionTimeMillis() {
+        boolean supportsPlatformTransitionMillis = Build.VERSION.SDK_INT >= 34;
+        AccessibilityWindowInfoCompat windowInfoCompat;
+        if (supportsPlatformTransitionMillis) {
+            AccessibilityWindowInfo mockInfo = mock(AccessibilityWindowInfo.class);
+            when(mockInfo.getTransitionTimeMillis()).thenReturn(100L);
+            windowInfoCompat =
+                    AccessibilityWindowInfoCompat.wrapNonNullInstance(mockInfo);
+        } else {
+            windowInfoCompat = obtainedWrappedWindowCompat();
+        }
+
+        long transitionMillis = supportsPlatformTransitionMillis ? 100L : 0;
+        assertThat(windowInfoCompat.getTransitionTimeMillis()).isEqualTo(transitionMillis);
+    }
+
+    @SmallTest
+    @Test
+    public void testGetLocales() {
+        boolean supportsPlatformLocales = Build.VERSION.SDK_INT >= 34;
+        AccessibilityWindowInfoCompat windowInfoCompat;
+        if (supportsPlatformLocales) {
+            AccessibilityWindowInfo mockInfo = mock(AccessibilityWindowInfo.class);
+            when(mockInfo.getLocales()).thenReturn(new LocaleList(Locale.ENGLISH));
+            windowInfoCompat = AccessibilityWindowInfoCompat.wrapNonNullInstance(mockInfo);
+        } else {
+            windowInfoCompat = obtainedWrappedWindowCompat();
+        }
+
+        LocaleListCompat localeListCompat = supportsPlatformLocales
+                ? LocaleListCompat.wrap(new LocaleList(Locale.ENGLISH))
+                : LocaleListCompat.getEmptyLocaleList();
+        assertThat(windowInfoCompat.getLocales()).isEqualTo(localeListCompat);
+    }
+
+    @SdkSuppress(minSdkVersion = 33)
+    @SmallTest
+    @Test
+    public void testGetRoot_withPrefetchingStrategy_returnsRoot() {
+        AccessibilityWindowInfo accessibilityWindowInfo = mock(AccessibilityWindowInfo.class);
+        AccessibilityWindowInfoCompat windowCompat =
+                AccessibilityWindowInfoCompat.wrapNonNullInstance(accessibilityWindowInfo);
+        windowCompat.getRoot(AccessibilityNodeInfoCompat.FLAG_PREFETCH_ANCESTORS);
+        verify(accessibilityWindowInfo).getRoot(
+                AccessibilityNodeInfoCompat.FLAG_PREFETCH_ANCESTORS);
     }
 }
diff --git a/core/core/src/main/java/androidx/core/content/ContextCompat.java b/core/core/src/main/java/androidx/core/content/ContextCompat.java
index bb53f32..d283059 100644
--- a/core/core/src/main/java/androidx/core/content/ContextCompat.java
+++ b/core/core/src/main/java/androidx/core/content/ContextCompat.java
@@ -806,10 +806,10 @@
      * @param context  Context to retrieve service from.
      * @param receiver The BroadcastReceiver to handle the broadcast.
      * @param filter   Selects the Intent broadcasts to be received.
-     * @param flags    Specify one of {@link #RECEIVER_EXPORTED}, if you wish for your receiver
-     *                 to be able to receiver broadcasts from other applications, or
-     *                 {@link #RECEIVER_NOT_EXPORTED} if you only want your receiver to be able
-     *                 to receive broadcasts from the system or your own app.
+     * @param flags    If this receiver is listening for broadcasts sent from the system or from
+     *                 other apps—even other apps that you own—use the {@link #RECEIVER_EXPORTED}
+     *                 flag. If instead this receiver is listening only for broadcasts sent by your
+     *                 app, use the {@link #RECEIVER_NOT_EXPORTED} flag.
      * @return The first sticky intent found that matches <var>filter</var>,
      * or null if there are none.
      * @see Context#registerReceiver(BroadcastReceiver, IntentFilter, int)
@@ -832,11 +832,11 @@
      *                            required.
      * @param scheduler           Handler identifying the thread will receive the Intent. If
      *                            null, the main thread of the process will be used.
-     * @param flags               Specify one of {@link #RECEIVER_EXPORTED}, if you wish for your
-     *                            receiver to be able to receiver broadcasts from other
-     *                            applications, or {@link #RECEIVER_NOT_EXPORTED} if you only want
-     *                            your receiver to be able to receive broadcasts from the system
-     *                            or your own app.
+     * @param flags               If this receiver is listening for broadcasts sent from the
+     *                            system or from other apps—even other apps that you own—use the
+     *                            {@link #RECEIVER_EXPORTED} flag. If instead this receiver is
+     *                            listening only for broadcasts sent by your app, use the
+     *                            {@link #RECEIVER_NOT_EXPORTED} flag.
      * @return The first sticky intent found that matches <var>filter</var>,
      * or null if there are none.
      * @see Context#registerReceiver(BroadcastReceiver, IntentFilter, String, Handler, int)
diff --git a/core/core/src/main/java/androidx/core/math/MathUtils.java b/core/core/src/main/java/androidx/core/math/MathUtils.java
index 8a42777..f6129cc 100644
--- a/core/core/src/main/java/androidx/core/math/MathUtils.java
+++ b/core/core/src/main/java/androidx/core/math/MathUtils.java
@@ -24,172 +24,225 @@
     private MathUtils() {}
 
     /**
-     * See {@link Math#addExact(int, int)}.
+     * Returns the sum of its arguments, throwing an exception if the result overflows an
+     * {@code int}.
+     *
+     * @param x the first value
+     * @param y the second value
+     * @return the result
+     * @throws ArithmeticException if the result overflows an int
      */
     public static int addExact(int x, int y) {
-        // copied from Math.java
-        int r = x + y;
-        // HD 2-12 Overflow iff both arguments have the opposite sign of the result
-        if (((x ^ r) & (y ^ r)) < 0) {
+        int sum = x + y;
+        // If x and y have the same sign, their sum should have the same sign as well
+        if ((x >= 0 == y >= 0) && (x >= 0 != sum >= 0)) {
             throw new ArithmeticException("integer overflow");
+        } else {
+            return sum;
         }
-        return r;
     }
 
     /**
-     * See {@link Math#addExact(long, long)}.
+     * Returns the sum of its arguments, throwing an exception if the result overflows a
+     * {@code long}.
+     *
+     * @param x the first value
+     * @param y the second value
+     * @return the result
+     * @throws ArithmeticException if the result overflows a long
      */
     public static long addExact(long x, long y) {
-        // copied from Math.java
-        long r = x + y;
-        // HD 2-12 Overflow iff both arguments have the opposite sign of the result
-        if (((x ^ r) & (y ^ r)) < 0) {
-            throw new ArithmeticException("long overflow");
+        long sum = x + y;
+        // If x and y have the same sign, their sum should have the same sign as well
+        if ((x >= 0 == y >= 0) && (x >= 0 != sum >= 0)) {
+            throw new ArithmeticException("integer overflow");
+        } else {
+            return sum;
         }
-        return r;
     }
 
-
     /**
-     * See {@link Math#subtractExact(int, int)}.
+     * Returns the difference of the arguments, throwing an exception if the result overflows an
+     * {@code int}.
+     *
+     * @param x the first value
+     * @param y the second value to subtract from the first
+     * @return the result
+     * @throws ArithmeticException if the result overflows an int
      */
     public static int subtractExact(int x, int y) {
-        // copied from Math.java
-        int r = x - y;
-        // HD 2-12 Overflow iff the arguments have different signs and
-        // the sign of the result is different than the sign of x
-        if (((x ^ y) & (x ^ r)) < 0) {
+        int difference = x - y;
+        // If only one of x or y is negative, the difference should have the same sign as x
+        if ((x < 0 != y < 0) && (x < 0 != difference < 0)) {
             throw new ArithmeticException("integer overflow");
         }
-        return r;
+        return difference;
     }
 
     /**
-     * See {@link Math#subtractExact(long, long)}.
+     * Returns the difference of the arguments, throwing an exception if the result overflows a
+     * {@code long}.
+     *
+     * @param x the first value
+     * @param y the second value to subtract from the first
+     * @return the result
+     * @throws ArithmeticException if the result overflows a long
      */
     public static long subtractExact(long x, long y) {
-        // copied from Math.java
-        long r = x - y;
-        // HD 2-12 Overflow iff the arguments have different signs and
-        // the sign of the result is different than the sign of x
-        if (((x ^ y) & (x ^ r)) < 0) {
-            throw new ArithmeticException("long overflow");
+        long difference = x - y;
+        // If only one of x or y is negative, the difference should have the same sign as x
+        if ((x < 0 != y < 0) && (x < 0 != difference < 0)) {
+            throw new ArithmeticException("integer overflow");
         }
-        return r;
+        return difference;
     }
 
     /**
-     * See {@link Math#multiplyExact(int, int)}.
+     * Returns the product of the arguments, throwing an exception if the result overflows an
+     * {@code int}.
+     *
+     * @param x the first value
+     * @param y the second value
+     * @return the result
+     * @throws ArithmeticException if the result overflows an int
      */
     public static int multiplyExact(int x, int y) {
-        // copied from Math.java
-        long r = (long) x * (long) y;
-        if ((int) r != r) {
+        int product = x * y;
+        // Dividing back by one of x or y should get the other back unless there was overflow
+        if (x != 0 && y != 0 && (product / x != y || product / y != x)) {
             throw new ArithmeticException("integer overflow");
         }
-        return (int) r;
+        return product;
     }
 
     /**
-     * See {@link Math#multiplyExact(long, long)}.
+     * Returns the product of the arguments, throwing an exception if the result overflows a
+     * {@code long}.
+     *
+     * @param x the first value
+     * @param y the second value
+     * @return the result
+     * @throws ArithmeticException if the result overflows a long
      */
     public static long multiplyExact(long x, long y) {
-        // copied from Math.java
-        long r = x * y;
-        long ax = Math.abs(x);
-        long ay = Math.abs(y);
-        if (((ax | ay) >>> 31 != 0)) {
-            // Some bits greater than 2^31 that might cause overflow
-            // Check the result using the divide operator
-            // and check for the special case of Long.MIN_VALUE * -1
-            if (((y != 0) && (r / y != x)) || (x == Long.MIN_VALUE && y == -1)) {
-                throw new ArithmeticException("long overflow");
-            }
+        long product = x * y;
+        // Dividing back by one of x or y should get the other back unless there was overflow
+        if (x != 0 && y != 0 && (product / x != y || product / y != x)) {
+            throw new ArithmeticException("integer overflow");
         }
-        return r;
+        return product;
     }
 
     /**
-     * See {@link Math#incrementExact(int)}.
+     * Returns the argument incremented by one, throwing an exception if the result overflows an
+     * {@code int}. The overflow only occurs for {@linkplain Integer#MAX_VALUE the maximum value}.
+     *
+     * @param a the value to increment
+     * @return the result
+     * @throws ArithmeticException if the result overflows an int
      */
     public static int incrementExact(int a) {
-        // copied from Math.java
         if (a == Integer.MAX_VALUE) {
             throw new ArithmeticException("integer overflow");
+        } else {
+            return a + 1;
         }
-
-        return a + 1;
     }
 
     /**
-     * See {@link Math#incrementExact(long)}.
+     * Returns the argument incremented by one, throwing an exception if the result overflows a
+     * {@code long}. The overflow only occurs for {@linkplain Long#MAX_VALUE the maximum value}.
+     *
+     * @param a the value to increment
+     * @return the result
+     * @throws ArithmeticException if the result overflows a long
      */
     public static long incrementExact(long a) {
-        // copied from Math.java
         if (a == Long.MAX_VALUE) {
-            throw new ArithmeticException("long overflow");
+            throw new ArithmeticException("integer overflow");
+        } else {
+            return a + 1;
         }
-
-        return a + 1L;
     }
 
     /**
-     * See {@link Math#decrementExact(int)}.
+     * Returns the argument decremented by one, throwing an exception if the result overflows an
+     * {@code int}. The overflow only occurs for {@linkplain Integer#MIN_VALUE the minimum value}.
+     *
+     * @param a the value to decrement
+     * @return the result
+     * @throws ArithmeticException if the result overflows an int
      */
     public static int decrementExact(int a) {
-        // copied from Math.java
         if (a == Integer.MIN_VALUE) {
             throw new ArithmeticException("integer overflow");
+        } else {
+            return a - 1;
         }
-
-        return a - 1;
     }
 
     /**
-     * See {@link Math#decrementExact(long)}.
+     * Returns the argument decremented by one, throwing an exception if the result overflows a
+     * {@code long}. The overflow only occurs for {@linkplain Long#MIN_VALUE the minimum value}.
+     *
+     * @param a the value to decrement
+     * @return the result
+     * @throws ArithmeticException if the result overflows a long
      */
     public static long decrementExact(long a) {
-        // copied from Math.java
         if (a == Long.MIN_VALUE) {
-            throw new ArithmeticException("long overflow");
+            throw new ArithmeticException("integer overflow");
+        } else {
+            return a - 1;
         }
-
-        return a - 1L;
     }
 
     /**
-     * See {@link Math#negateExact(int)}.
+     * Returns the negation of the argument, throwing an exception if the result overflows an
+     * {@code int}. The overflow only occurs for {@linkplain Integer#MIN_VALUE the minimum value}.
+     *
+     * @param a the value to negate
+     * @return the result
+     * @throws ArithmeticException if the result overflows an int
      */
     public static int negateExact(int a) {
-        // copied from Math.java
         if (a == Integer.MIN_VALUE) {
             throw new ArithmeticException("integer overflow");
+        } else {
+            return -a;
         }
-
-        return -a;
     }
 
     /**
-     * See {@link Math#negateExact(long)}.
+     * Returns the negation of the argument, throwing an exception if the result overflows a
+     * {@code long}. The overflow only occurs for {@linkplain Long#MIN_VALUE the minimum value}.
+     *
+     * @param a the value to negate
+     * @return the result
+     * @throws ArithmeticException if the result overflows a long
      */
     public static long negateExact(long a) {
-        // copied from Math.java
         if (a == Long.MIN_VALUE) {
-            throw new ArithmeticException("long overflow");
+            throw new ArithmeticException("integer overflow");
+        } else {
+            return -a;
         }
-
-        return -a;
     }
 
     /**
-     * See {@link Math#toIntExact(long)}.
+     * Returns the value of the {@code long} argument, throwing an exception if the value
+     * overflows an {@code int}.
+     *
+     * @param value the long value
+     * @return the argument as an int
+     * @throws ArithmeticException if the {@code argument} overflows an int
      */
     public static int toIntExact(long value) {
-        // copied from Math.java
-        if ((int) value != value) {
+        if (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE) {
             throw new ArithmeticException("integer overflow");
+        } else {
+            return (int) value;
         }
-        return (int) value;
     }
 
     /**
diff --git a/core/core/src/main/java/androidx/core/os/BuildCompat.kt b/core/core/src/main/java/androidx/core/os/BuildCompat.kt
index bc1ded2..a704a26 100644
--- a/core/core/src/main/java/androidx/core/os/BuildCompat.kt
+++ b/core/core/src/main/java/androidx/core/os/BuildCompat.kt
@@ -184,7 +184,6 @@
      *
      * @return `true` if Sv2 APIs are available for use, `false` otherwise
      */
-    @PrereleaseSdkCheck
     @JvmStatic
     @ChecksSdkIntAtLeast(api = 32, codename = "Sv2")
     @Deprecated(
@@ -208,7 +207,6 @@
      *
      * @return `true` if Tiramisu APIs are available for use, `false` otherwise
      */
-    @PrereleaseSdkCheck
     @JvmStatic
     @ChecksSdkIntAtLeast(api = 33, codename = "Tiramisu")
     @Deprecated(
@@ -232,7 +230,6 @@
      *
      * @return `true` if UpsideDownCake APIs are available for use, `false` otherwise
      */
-    @PrereleaseSdkCheck
     @JvmStatic
     @ChecksSdkIntAtLeast(api = 34, codename = "UpsideDownCake")
     @Deprecated(
@@ -267,10 +264,11 @@
     /**
      * Experimental feature set for pre-release SDK checks.
      *
-     * APIs annotated as part of this feature set should only be used when building against
-     * pre-release platform SDKs. They are safe to ship in production apps and alpha libraries,
-     * but they must not be shipped in beta or later libraries as they **will be
-     * removed** after their respective SDKs are finalized for release.
+     * Pre-release SDK checks **do not** guarantee correctness, as APIs may have been added or
+     * removed during the course of a pre-release SDK development cycle.
+     *
+     * Additionally, pre-release checks **may not** return `true` when run on a finalized version of
+     * the SDK associated with the codename.
      */
     @RequiresOptIn
     @Retention(AnnotationRetention.BINARY)
diff --git a/core/core/src/main/java/androidx/core/view/ViewCompat.java b/core/core/src/main/java/androidx/core/view/ViewCompat.java
index 9ca0a09..5fa5a59 100644
--- a/core/core/src/main/java/androidx/core/view/ViewCompat.java
+++ b/core/core/src/main/java/androidx/core/view/ViewCompat.java
@@ -56,6 +56,7 @@
 import android.view.WindowInsetsController;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityRecord;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeProvider;
 import android.view.autofill.AutofillId;
@@ -635,9 +636,12 @@
     /**
      * Called from {@link View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)}
      * giving a chance to this View to populate the accessibility event with its
-     * text content. While this method is free to modify event
-     * attributes other than text content, doing so should normally be performed in
-     * {@link View#onInitializeAccessibilityEvent(AccessibilityEvent)}.
+     * text content.
+     * <p>
+     * <b>Note:</b> This method should only be used with {@link AccessibilityRecord#getText()}.
+     * Avoid mutating other event state in this method. Instead, follow the practices described in
+     * {@link #dispatchPopulateAccessibilityEvent(AccessibilityEvent)}. In general, put UI
+     * metadata in the node for services to easily query, than in events.
      * <p>
      * Example: Adding formatted date string to an accessibility event in addition
      *          to the text added by the super implementation:
@@ -1472,6 +1476,11 @@
      * {@link AccessibilityDelegateCompat#performAccessibilityAction(View, int, Bundle)}
      * is responsible for handling this call.
      * </p>
+     * <p>
+     * <b>Note:</b> Avoid setting accessibility focus with
+     * {@link AccessibilityNodeInfoCompat.AccessibilityActionCompat#ACTION_ACCESSIBILITY_FOCUS}.
+     * This is intended to be controlled by screen readers. Apps changing focus can confuse
+     * screen readers, and the resulting behavior can vary by device and screen reader version.
      *
      * @param action The action to perform.
      * @param arguments Optional action arguments.
@@ -1703,10 +1712,22 @@
      *   {@link android.accessibilityservice.AccessibilityService}.
      *   This class is made immutable before being delivered to an AccessibilityService.
      * </p>
+     * <p>
+     * State refers to a frequently changing property of the View, such as an enabled/disabled
+     * state of a button or the audio level of a volume slider.
+     *
+     * <p>
+     * This should omit role or content. Role refers to the kind of user-interface element the
+     * View is, such as a Button or Checkbox. Content is the meaningful text and graphics that
+     * should be described by {@link View#setContentDescription(CharSequence)} or
+     * {@code android:contentDescription}. It is expected that a content description mostly
+     * remains constant, while a state description updates from time to time.
      *
      * @param stateDescription the state description of this node.
      *
      * @throws IllegalStateException If called from an AccessibilityService.
+     * @see View#setStateDescription(CharSequence)
+     * @see View#setContentDescription(CharSequence)
      */
     @UiThread
     public static void setStateDescription(@NonNull View view,
@@ -1737,6 +1758,20 @@
     /**
      * Allow accessibility services to find and activate clickable spans in the application.
      *
+     * <p>
+     * {@link android.text.style.ClickableSpan} is automatically supported from
+     * API 26. For compatibility back to API 19, this should be enabled.
+     * <p>
+     * {@link android.text.style.URLSpan}, a subclass of ClickableSpans, is
+     * automatically supported and does not need this enabled.
+     * <p>
+     * Do not put ClickableSpans in {@link View#setContentDescription(CharSequence)} or
+     * {@link View#setStateDescription(CharSequence)}.
+     * These links are only visible to accessibility services in
+     * {@link AccessibilityNodeInfoCompat#getText()}, which should be
+     * modifiable using helper methods on UI elements. For example, use
+     * {@link android.widget.TextView#setText(CharSequence)} to modify the text of TextViews.
+     *
      * @param view The view
      * <p>
      * Compatibility:
@@ -2129,24 +2164,36 @@
     }
 
     /**
-     * Sets the live region mode for the specified view. This indicates to
-     * accessibility services whether they should automatically notify the user
-     * about changes to the view's content description or text, or to the
-     * content descriptions or text of the view's children (where applicable).
+     * Sets the live region mode for this view. This indicates to accessibility
+     * services whether they should automatically notify the user about changes
+     * to the view's content description or text, or to the content descriptions
+     * or text of the view's children (where applicable).
      * <p>
-     * For example, in a login screen with a TextView that displays an "incorrect
-     * password" notification, that view should be marked as a live region with
-     * mode {@link #ACCESSIBILITY_LIVE_REGION_POLITE}.
+     * To indicate that the user should be notified of changes, use
+     * {@link #ACCESSIBILITY_LIVE_REGION_POLITE}. Announcements from this region are queued and
+     * do not disrupt ongoing speech.
+     * <p>
+     * For example, selecting an option in a dropdown menu may update a panel below with the updated
+     * content. This panel may be marked as a live region with
+     * {@link #ACCESSIBILITY_LIVE_REGION_POLITE} to notify users of the change.
+     * <p>
+     * For notifying users about errors, such as in a login screen with text that displays an
+     * "incorrect password" notification, that view should send an AccessibilityEvent of type
+     * {@link AccessibilityEvent#CONTENT_CHANGE_TYPE_ERROR} and set
+     * {@link AccessibilityNodeInfo#setError(CharSequence)} instead. Custom widgets should expose
+     * error-setting methods that support accessibility automatically. For example, instead of
+     * explicitly sending this event when using a TextView, use
+     * {@link android.widget.TextView#setError(CharSequence)}.
      * <p>
      * To disable change notifications for this view, use
      * {@link #ACCESSIBILITY_LIVE_REGION_NONE}. This is the default live region
      * mode for most views.
      * <p>
-     * To indicate that the user should be notified of changes, use
-     * {@link #ACCESSIBILITY_LIVE_REGION_POLITE}.
-     * <p>
      * If the view's changes should interrupt ongoing speech and notify the user
-     * immediately, use {@link #ACCESSIBILITY_LIVE_REGION_ASSERTIVE}.
+     * immediately, use {@link #ACCESSIBILITY_LIVE_REGION_ASSERTIVE}. This may result in disruptive
+     * announcements from an accessibility service, so it should generally be used only to convey
+     * information that is time-sensitive or critical for use of the application. Examples may
+     * include an incoming call or an emergency alert.
      *
      * @param view The view on which to set the live region mode
      * @param mode The live region mode for this view, one of:
@@ -4519,6 +4566,10 @@
      * <p>
      * Note: this is similar to using <a href="#attr_android:focusable">{@code android:focusable},
      * but does not impact input focus behavior.
+     * <p>This can be used to
+     * <a href="{@docRoot}guide/topics/ui/accessibility/principles#content-groups">group related
+     * content.</a>
+     * </p>
      *
      * @param view The view whose title should be set
      * @param screenReaderFocusable Whether the view should be treated as a unit by screen reader
@@ -4579,11 +4630,20 @@
 
     /**
      * Visually distinct portion of a window with window-like semantics are considered panes for
-     * accessibility purposes. One example is the content view of a fragment that is replaced.
+     * accessibility purposes. One example is the content view of a large fragment that is replaced.
      * In order for accessibility services to understand a pane's window-like behavior, panes
-     * should have descriptive titles. Views with pane titles produce {@link AccessibilityEvent}s
-     * when they appear, disappear, or change title.
+     * should have descriptive titles. Views with pane titles produce
+     * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}s when they appear, disappear, or change
+     * title.
      *
+     * <p>
+     * When transitioning from one Activity to another, instead of using
+     * setAccessibilityPaneTitle(), set a descriptive title for your activity's window by using
+     * {@code android:label} for the matching <activity> entry in your application’s manifest  or
+     * updating the title at runtime with {@link android.app.Activity#setTitle(CharSequence)}.
+     *
+     * <p>
+     * To set the pane title in xml, use {@code android:accessibilityPaneTitle}.
      * @param view The view whose pane title should be set.
      * @param accessibilityPaneTitle The pane's title. Setting to {@code null} indicates that this
      *                               View is not a pane.
diff --git a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityEventCompat.java b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityEventCompat.java
index 957d459..fbea1ca 100644
--- a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityEventCompat.java
+++ b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityEventCompat.java
@@ -21,7 +21,6 @@
 import android.os.Build;
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityRecord;
 import android.widget.EditText;
 
@@ -120,11 +119,13 @@
 
     /**
      * Represents the event of gaining accessibility focus.
+     * @see AccessibilityNodeInfoCompat.AccessibilityActionCompat#ACTION_ACCESSIBILITY_FOCUS
      */
     public static final int TYPE_VIEW_ACCESSIBILITY_FOCUSED = 0x00008000;
 
     /**
      * Represents the event of clearing accessibility focus.
+     * @see AccessibilityNodeInfoCompat.AccessibilityActionCompat#ACTION_CLEAR_ACCESSIBILITY_FOCUS
      */
     public static final int TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 0x00010000;
 
@@ -189,26 +190,28 @@
      * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
      * The node's text changed.
      */
-    public static final int CONTENT_CHANGE_TYPE_TEXT = 0x00000002;
+    public static final int CONTENT_CHANGE_TYPE_TEXT = 1 << 1;
 
     /**
      * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
      * The node's content description changed.
      */
-    public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 0x00000004;
+    public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 1 << 2;
 
     /**
      * Change type for {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED} event:
      * The node's pane title changed.
+     * @see androidx.core.view.ViewCompat#setAccessibilityPaneTitle(View, CharSequence)
      */
-    public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 0x00000008;
+    public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 1 << 3;
 
     /**
      * Change type for {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED} event:
      * The node has a pane title, and either just appeared or just was assigned a title when it
      * had none before.
+     * @see androidx.core.view.ViewCompat#setAccessibilityPaneTitle(View, CharSequence)
      */
-    public static final int CONTENT_CHANGE_TYPE_PANE_APPEARED = 0x00000010;
+    public static final int CONTENT_CHANGE_TYPE_PANE_APPEARED = 1 << 4;
 
     /**
      * Change type for {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED} event:
@@ -218,15 +221,16 @@
      * No source will be returned if the node is no longer on the screen. To make the change more
      * clear for the user, the first entry in {@link AccessibilityRecord#getText()} can return the
      * value that would have been returned by {@code getSource().getPaneTitle()}.
+     * @see androidx.core.view.ViewCompat#setAccessibilityPaneTitle(View, CharSequence)
      */
-    public static final int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 0x00000020;
+    public static final int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 1 << 5;
 
     /**
      * Change type for {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event:
      * state description of the node as returned by
-     * {@link AccessibilityNodeInfo#getStateDescription} was changed.
+     * {@link AccessibilityNodeInfoCompat#getStateDescription} was changed.
      */
-    public static final int CONTENT_CHANGE_TYPE_STATE_DESCRIPTION = 0x00000040;
+    public static final int CONTENT_CHANGE_TYPE_STATE_DESCRIPTION = 1 << 6;
 
     /**
      * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
@@ -234,18 +238,18 @@
      * AccessibilityAction, or via touch events. This is sent from the source that initiated the
      * drag.
      *
-     * @see AccessibilityNodeInfo.AccessibilityAction#ACTION_DRAG_START
+     * @see AccessibilityNodeInfoCompat.AccessibilityActionCompat#ACTION_DRAG_START
      */
-    public static final int CONTENT_CHANGE_TYPE_DRAG_STARTED = 0x00000080;
+    public static final int CONTENT_CHANGE_TYPE_DRAG_STARTED = 1 << 7;
 
     /**
      * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
      * A drag in with accessibility enabled has ended. This means the content has been
      * successfully dropped. This is sent from the target that accepted the dragged content.
      *
-     * @see AccessibilityNodeInfo.AccessibilityAction#ACTION_DRAG_DROP
+     * @see AccessibilityNodeInfoCompat.AccessibilityActionCompat#ACTION_DRAG_DROP
      */
-    public static final int CONTENT_CHANGE_TYPE_DRAG_DROPPED = 0x00000100;
+    public static final int CONTENT_CHANGE_TYPE_DRAG_DROPPED = 1 << 8;
 
     /**
      * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
@@ -254,9 +258,46 @@
      * no drop has been detected within a timeout and the drag was automatically cancelled. This is
      * sent from the source that initiated the drag.
      *
-     * @see AccessibilityNodeInfo.AccessibilityAction#ACTION_DRAG_CANCEL
+     * @see AccessibilityNodeInfoCompat.AccessibilityActionCompat#ACTION_DRAG_CANCEL
      */
-    public static final int CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 0x0000200;
+    public static final int CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 1 << 9;
+
+    /**
+     * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
+     * The source node changed its content validity returned by
+     * {@link AccessibilityNodeInfoCompat#isContentInvalid()}.
+     * The view changing content validity should call
+     * {@link AccessibilityNodeInfoCompat#setContentInvalid(boolean)} and then send this event.
+     *
+     * @see AccessibilityNodeInfoCompat#isContentInvalid()
+     * @see AccessibilityNodeInfoCompat#setContentInvalid(boolean)
+     */
+    public static final int CONTENT_CHANGE_TYPE_CONTENT_INVALID = 1 << 10;
+
+    /**
+     * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
+     * The source node changed its erroneous content's error message returned by
+     * {@link AccessibilityNodeInfoCompat#getError()}.
+     * The view changing erroneous content's error message should call
+     * {@link AccessibilityNodeInfoCompat#setError(CharSequence)} and then send this event.
+     *
+     * @see AccessibilityNodeInfoCompat#getError()
+     * @see AccessibilityNodeInfoCompat#setError(CharSequence)
+     */
+    public static final int CONTENT_CHANGE_TYPE_ERROR = 1 << 11;
+
+
+    /**
+     * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
+     * The source node changed its ability to interact returned by
+     * {@link AccessibilityNodeInfoCompat#isEnabled()}.
+     * The view changing content's ability to interact should call
+     * {@link AccessibilityNodeInfoCompat#setEnabled(boolean)} and then send this event.
+     *
+     * @see AccessibilityNodeInfoCompat#isEnabled()
+     * @see AccessibilityNodeInfoCompat#setEnabled(boolean)
+     */
+    public static final int CONTENT_CHANGE_TYPE_ENABLED = 1 << 12;
 
     /**
      * Mask for {@link AccessibilityEvent} all types.
@@ -298,7 +339,10 @@
                     CONTENT_CHANGE_TYPE_UNDEFINED,
                     CONTENT_CHANGE_TYPE_DRAG_STARTED,
                     CONTENT_CHANGE_TYPE_DRAG_DROPPED,
-                    CONTENT_CHANGE_TYPE_DRAG_CANCELLED
+                    CONTENT_CHANGE_TYPE_DRAG_CANCELLED,
+                    CONTENT_CHANGE_TYPE_CONTENT_INVALID,
+                    CONTENT_CHANGE_TYPE_ERROR,
+                    CONTENT_CHANGE_TYPE_ENABLED
             })
     @RestrictTo(LIBRARY_GROUP_PREFIX)
     @Retention(RetentionPolicy.SOURCE)
@@ -474,6 +518,72 @@
         }
     }
 
+    /**
+     * Whether the event should only be delivered to an
+     * {@link android.accessibilityservice.AccessibilityService} with the
+     * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool} property
+     * set to true.
+     *
+     * <p>
+     * Initial value matches the {@link android.view.View#isAccessibilityDataSensitive} property
+     * from the event's source node, if present, or false by default.
+     * </p>
+     *
+     * @return True if the event should be delivered only to isAccessibilityTool services, false
+     * otherwise.
+     * @see #setAccessibilityDataSensitive
+     */
+    public static boolean isAccessibilityDataSensitive(@NonNull AccessibilityEvent event) {
+        if (Build.VERSION.SDK_INT >= 34) {
+            return Api34Impl.isAccessibilityDataSensitive(event);
+        } else {
+            // To preserve behavior, assume the event's accessibility data is not sensitive.
+            return false;
+        }
+    }
+
+    /**
+     * Sets whether the event should only be delivered to an
+     * {@link android.accessibilityservice.AccessibilityService} with the
+     * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool} property
+     * set to true.
+     *
+     * <p>
+     * This will be set automatically based on the event's source (if present). If creating and
+     * sending an event directly through {@link android.view.accessibility.AccessibilityManager}
+     * (where an event may have no source) then this method must be called explicitly if you want
+     * non-default behavior.
+     * </p>
+     *
+     * @param event The event to update.
+     * @param accessibilityDataSensitive True if the event should be delivered only to
+     *                                   isAccessibilityTool services, false otherwise.
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public static void setAccessibilityDataSensitive(@NonNull AccessibilityEvent event,
+            boolean accessibilityDataSensitive) {
+        if (Build.VERSION.SDK_INT >= 34) {
+            Api34Impl.setAccessibilityDataSensitive(event, accessibilityDataSensitive);
+        }
+    }
+
+    @RequiresApi(34)
+    static class Api34Impl {
+        private Api34Impl() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        static boolean isAccessibilityDataSensitive(AccessibilityEvent event) {
+            return event.isAccessibilityDataSensitive();
+        }
+
+        @DoNotInline
+        static void setAccessibilityDataSensitive(AccessibilityEvent event,
+                boolean accessibilityDataSensitive) {
+            event.setAccessibilityDataSensitive(accessibilityDataSensitive);
+        }
+    }
     @RequiresApi(19)
     static class Api19Impl {
         private Api19Impl() {
diff --git a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityManagerCompat.java b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityManagerCompat.java
index e91ee92..0df339c 100644
--- a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityManagerCompat.java
+++ b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityManagerCompat.java
@@ -190,6 +190,36 @@
         }
     }
 
+
+    /**
+     * Whether the current accessibility request comes from an
+     * {@link android.accessibilityservice.AccessibilityService} with the
+     * {@link AccessibilityServiceInfo#isAccessibilityTool}
+     * property set to true.
+     *
+     * <p>
+     * You can use this method inside {@link android.view.accessibility.AccessibilityNodeProvider}
+     * to decide how to populate your nodes.
+     * </p>
+     *
+     * <p>
+     * <strong>Note:</strong> The return value is valid only when an
+     * {@link android.view.accessibility.AccessibilityNodeInfo} request is in progress, can
+     * change from one request to another, and has no meaning when a request is not in progress.
+     * </p>
+     *
+     * @return True if the current request is from a tool that sets isAccessibilityTool.
+     */
+    public static boolean isRequestFromAccessibilityTool(@NonNull AccessibilityManager manager) {
+        if (Build.VERSION.SDK_INT >= 34) {
+            return Api34Impl.isRequestFromAccessibilityTool(manager);
+        } else {
+            // To preserve behavior, assume every service isAccessibilityTool if the system does
+            // not support this check.
+            return true;
+        }
+    }
+
     @RequiresApi(19)
     private static final class TouchExplorationStateChangeListenerWrapper
             implements AccessibilityManager.TouchExplorationStateChangeListener {
@@ -273,6 +303,17 @@
     private AccessibilityManagerCompat() {
     }
 
+    @RequiresApi(34)
+    static class Api34Impl {
+        private Api34Impl() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        static boolean isRequestFromAccessibilityTool(AccessibilityManager accessibilityManager) {
+            return accessibilityManager.isRequestFromAccessibilityTool();
+        }
+    }
     @RequiresApi(19)
     static class Api19Impl {
         private Api19Impl() {
diff --git a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java
index 86d2cf1..150dcbe 100644
--- a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java
+++ b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java
@@ -22,6 +22,7 @@
 
 import static java.util.Collections.emptyList;
 
+import android.accessibilityservice.AccessibilityService;
 import android.annotation.SuppressLint;
 import android.content.ClipData;
 import android.graphics.Rect;
@@ -37,6 +38,7 @@
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.TouchDelegateInfo;
 
@@ -60,6 +62,7 @@
 import androidx.core.view.accessibility.AccessibilityViewCommand.SetTextArguments;
 
 import java.lang.ref.WeakReference;
+import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -107,12 +110,22 @@
 
         /**
          * Action that gives input focus to the node.
+         * <p>The focus request sends an event of {@link AccessibilityEvent#TYPE_VIEW_FOCUSED}
+         * if successful. In the View system, this is handled by {@link View#requestFocus}.
+         *
+         * <p>The node that is focused should return {@code true} for
+         * {@link AccessibilityNodeInfoCompat#isFocused()}.
+         *
+         * @see #ACTION_ACCESSIBILITY_FOCUS for the difference between system focus and
+         * accessibility focus.
          */
         public static final AccessibilityActionCompat ACTION_FOCUS =
                 new AccessibilityActionCompat(AccessibilityNodeInfoCompat.ACTION_FOCUS, null);
 
         /**
          * Action that clears input focus of the node.
+         * <p>The node that is cleared should return {@code false} for
+         * {@link AccessibilityNodeInfoCompat#isFocused()}.
          */
         public static final AccessibilityActionCompat ACTION_CLEAR_FOCUS =
                 new AccessibilityActionCompat(
@@ -134,12 +147,29 @@
 
         /**
          * Action that clicks on the node info.
+         *
+         * <p>The UI element that implements this should send a
+         * {@link AccessibilityEvent#TYPE_VIEW_CLICKED} event. In the View system,
+         * the default handling of this action when performed by a service is to call
+         * {@link View#performClick()}, and setting a
+         * {@link View#setOnClickListener(View.OnClickListener)} automatically adds this action.
+         *
+         * <p>{@link #isClickable()} should return true if this action is available.
          */
         public static final AccessibilityActionCompat ACTION_CLICK =
                 new AccessibilityActionCompat(AccessibilityNodeInfoCompat.ACTION_CLICK, null);
 
         /**
          * Action that long clicks on the node.
+         *
+         * <p>The UI element that implements this should send a
+         * {@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED} event. In the View system,
+         * the default handling of this action when performed by a service is to call
+         * {@link View#performLongClick()}, and setting a
+         * {@link View#setOnLongClickListener(View.OnLongClickListener)} automatically adds this
+         * action.
+         *
+         * <p>{@link #isLongClickable()} should return true if this action is available.
          */
         public static final AccessibilityActionCompat ACTION_LONG_CLICK =
                 new AccessibilityActionCompat(
@@ -147,6 +177,16 @@
 
         /**
          * Action that gives accessibility focus to the node.
+         * <p>The UI element that implements this should send a
+         * {@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUSED} event
+         * if successful. The node that is focused should return {@code true} for
+         * {@link AccessibilityNodeInfoCompat#isAccessibilityFocused()}.
+         *
+         * <p>This is intended to be used by screen readers to assist with user navigation. Apps
+         * changing focus can confuse screen readers, so the resulting behavior can vary by device
+         * and screen reader version.
+         * <p>This is distinct from {@link #ACTION_FOCUS}, which refers to system focus. System
+         * focus is typically used to convey targets for keyboard navigation.
          */
         public static final AccessibilityActionCompat ACTION_ACCESSIBILITY_FOCUS =
                 new AccessibilityActionCompat(
@@ -154,6 +194,10 @@
 
         /**
          * Action that clears accessibility focus of the node.
+         * <p>The UI element that implements this should send a
+         * {@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED} event if successful. The
+         * node that is cleared should return {@code false} for
+         * {@link AccessibilityNodeInfoCompat#isAccessibilityFocused()}.
          */
         public static final AccessibilityActionCompat ACTION_CLEAR_ACCESSIBILITY_FOCUS =
                 new AccessibilityActionCompat(
@@ -351,6 +395,11 @@
          * </code></pre></p>
          * </p>
          *
+         * <p> If this is a text selection, the UI element that implements this should send a
+         * {@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED} event if its selection is
+         * updated. This element should also return {@code true} for
+         * {@link AccessibilityNodeInfoCompat#isTextSelectable()}.
+         *
          * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SELECTION_START_INT
          *  AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_START_INT
          * @see AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SELECTION_END_INT
@@ -397,6 +446,10 @@
          *       "android");
          *  info.performAction(AccessibilityActionCompat.ACTION_SET_TEXT.getId(), arguments);
          * </code></pre></p>
+         * <p>The UI element that implements this should send a
+         * {@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED} event if its text is updated.
+         * This element should also return {@code true} for
+         * {@link AccessibilityNodeInfoCompat#isEditable()}.
          */
         public static final AccessibilityActionCompat ACTION_SET_TEXT =
                 new AccessibilityActionCompat(AccessibilityNodeInfoCompat.ACTION_SET_TEXT, null,
@@ -500,6 +553,18 @@
 
         /**
          * Action that context clicks the node.
+         *
+         * <p>The UI element that implements this should send a
+         * {@link AccessibilityEvent#TYPE_VIEW_CONTEXT_CLICKED} event. In the View system,
+         * the default handling of this action when performed by a service is to call
+         * {@link View#performContextClick()}, and setting a
+         * {@link View#setOnContextClickListener(View.OnContextClickListener)} automatically adds
+         * this action.
+         *
+         * <p>A context click usually occurs from a mouse pointer right-click or a stylus button
+         * press.
+         *
+         * <p>{@link #isContextClickable()} should return true if this action is available.
          */
         public static final AccessibilityActionCompat ACTION_CONTEXT_CLICK =
                 new AccessibilityActionCompat(Build.VERSION.SDK_INT >= 23
@@ -1166,6 +1231,169 @@
                 return false;
             }
         }
+
+        /**
+         * Gets the row title at which the item is located.
+         *
+         * @return The row title.
+         */
+        @Nullable
+        public String getRowTitle() {
+            if (Build.VERSION.SDK_INT >= 33) {
+                return Api33Impl.getCollectionItemRowTitle(mInfo);
+            } else {
+                return null;
+            }
+        }
+
+        /**
+         * Gets the column title at which the item is located.
+         *
+         * @return The column title.
+         */
+        @Nullable
+        public String getColumnTitle() {
+            if (Build.VERSION.SDK_INT >= 33) {
+                return Api33Impl.getCollectionItemColumnTitle(mInfo);
+            } else {
+                return null;
+            }
+        }
+
+        /**
+         * Builder for creating {@link CollectionItemInfoCompat} objects.
+         */
+        public static final class Builder {
+            private boolean mHeading;
+            private int mColumnIndex;
+            private int mRowIndex;
+            private int mColumnSpan;
+            private int mRowSpan;
+            private boolean mSelected;
+            private String mRowTitle;
+            private String mColumnTitle;
+
+            /**
+             * Creates a new Builder.
+             */
+            public Builder() {
+            }
+
+            /**
+             * Sets the collection item is a heading.
+             *
+             * @param heading The heading state
+             * @return This builder
+             */
+            @NonNull
+            public Builder setHeading(boolean heading) {
+                mHeading = heading;
+                return this;
+            }
+
+            /**
+             * Sets the column index at which the item is located.
+             *
+             * @param columnIndex The column index
+             * @return This builder
+             */
+            @NonNull
+            public Builder setColumnIndex(int columnIndex) {
+                mColumnIndex = columnIndex;
+                return this;
+            }
+
+            /**
+             * Sets the row index at which the item is located.
+             *
+             * @param rowIndex The row index
+             * @return This builder
+             */
+            @NonNull
+            public Builder setRowIndex(int rowIndex) {
+                mRowIndex = rowIndex;
+                return this;
+            }
+
+            /**
+             * Sets the number of columns the item spans.
+             *
+             * @param columnSpan The number of columns spans
+             * @return This builder
+             */
+            @NonNull
+            public Builder setColumnSpan(int columnSpan) {
+                mColumnSpan = columnSpan;
+                return this;
+            }
+
+            /**
+             * Sets the number of rows the item spans.
+             *
+             * @param rowSpan The number of rows spans
+             * @return This builder
+             */
+            @NonNull
+            public Builder setRowSpan(int rowSpan) {
+                mRowSpan = rowSpan;
+                return this;
+            }
+
+            /**
+             * Sets the collection item is selected.
+             *
+             * @param selected The number of rows spans
+             * @return This builder
+             */
+            @NonNull
+            public Builder setSelected(boolean selected) {
+                mSelected = selected;
+                return this;
+            }
+
+            /**
+             * Sets the row title at which the item is located.
+             *
+             * @param rowTitle The row title
+             * @return This builder
+             */
+            @NonNull
+            public Builder setRowTitle(@Nullable String rowTitle) {
+                mRowTitle = rowTitle;
+                return this;
+            }
+
+            /**
+             * Sets the column title at which the item is located.
+             *
+             * @param columnTitle The column title
+             * @return This builder
+             */
+            @NonNull
+            public Builder setColumnTitle(@Nullable String columnTitle) {
+                mColumnTitle = columnTitle;
+                return this;
+            }
+
+            /**
+             * Builds and returns a {@link AccessibilityNodeInfo.CollectionItemInfo}.
+             */
+            @NonNull
+            public CollectionItemInfoCompat build() {
+                if (Build.VERSION.SDK_INT >= 33) {
+                    return Api33Impl.buildCollectionItemInfoCompat(mHeading, mColumnIndex,
+                            mRowIndex, mColumnSpan, mRowSpan, mSelected, mRowTitle, mColumnTitle);
+                } else if (Build.VERSION.SDK_INT >= 21) {
+                    return Api21Impl.createCollectionItemInfo(mRowIndex, mRowSpan, mColumnIndex,
+                            mColumnSpan, mHeading, mSelected);
+                } else if (Build.VERSION.SDK_INT >= 19) {
+                    return Api19Impl.createCollectionItemInfo(mRowIndex, mRowSpan, mColumnIndex,
+                            mColumnSpan, mHeading);
+                } else {
+                    return new CollectionItemInfoCompat(null);
+                }
+            }
+        }
     }
 
     /**
@@ -1204,6 +1432,26 @@
         }
 
         /**
+         * Creates a new range.
+         *
+         * @param type The type of the range.
+         * @param min The minimum value. Use {@code Float.NEGATIVE_INFINITY} if the range has no
+         *            minimum.
+         * @param max The maximum value. Use {@code Float.POSITIVE_INFINITY} if the range has no
+         *            maximum.
+         * @param current The current value.
+         */
+        public RangeInfoCompat(int type, float min, float max, float current) {
+            if (Build.VERSION.SDK_INT >= 30) {
+                mInfo = Api30Impl.createRangeInfo(type, min, max, current);
+            } else if (Build.VERSION.SDK_INT >= 19) {
+                mInfo = Api19Impl.createRangeInfo(type, min, max, current);
+            } else {
+                mInfo = null;
+            }
+        }
+
+        /**
          * Gets the current value.
          *
          * @return The current value.
@@ -1382,6 +1630,12 @@
     private static final String UNIQUE_ID_KEY =
             "androidx.view.accessibility.AccessibilityNodeInfoCompat.UNIQUE_ID_KEY";
 
+    private static final String CONTAINER_TITLE_KEY =
+            "androidx.view.accessibility.AccessibilityNodeInfoCompat.CONTAINER_TITLE_KEY";
+
+    private static final String BOUNDS_IN_WINDOW_KEY =
+            "androidx.view.accessibility.AccessibilityNodeInfoCompat.BOUNDS_IN_WINDOW_KEY";
+
     private static final String MIN_DURATION_BETWEEN_CONTENT_CHANGES_KEY =
             "androidx.view.accessibility.AccessibilityNodeInfoCompat."
                     + "MIN_DURATION_BETWEEN_CONTENT_CHANGES_KEY";
@@ -1392,7 +1646,9 @@
     private static final int BOOLEAN_PROPERTY_IS_HEADING = 0x00000002;
     private static final int BOOLEAN_PROPERTY_IS_SHOWING_HINT = 0x00000004;
     private static final int BOOLEAN_PROPERTY_IS_TEXT_ENTRY_KEY = 0x00000008;
+
     private static final int BOOLEAN_PROPERTY_HAS_REQUEST_INITIAL_ACCESSIBILITY_FOCUS = 1 << 5;
+    private static final int BOOLEAN_PROPERTY_ACCESSIBILITY_DATA_SENSITIVE = 1 << 6;
     private static final int BOOLEAN_PROPERTY_TEXT_SELECTABLE = 1 << 23;
     private static final int BOOLEAN_PROPERTY_SUPPORTS_GRANULAR_SCROLLING = 1 << 26;
 
@@ -1410,31 +1666,37 @@
 
     /**
      * Action that focuses the node.
+     * @see AccessibilityActionCompat#ACTION_FOCUS
      */
     public static final int ACTION_FOCUS = 0x00000001;
 
     /**
      * Action that unfocuses the node.
+     * @see AccessibilityActionCompat#ACTION_CLEAR_FOCUS
      */
     public static final int ACTION_CLEAR_FOCUS = 0x00000002;
 
     /**
      * Action that selects the node.
+     * @see AccessibilityActionCompat#ACTION_SELECT
      */
     public static final int ACTION_SELECT = 0x00000004;
 
     /**
      * Action that unselects the node.
+     * @see AccessibilityActionCompat#ACTION_CLEAR_SELECTION
      */
     public static final int ACTION_CLEAR_SELECTION = 0x00000008;
 
     /**
      * Action that clicks on the node info.
+     * @see AccessibilityActionCompat#ACTION_CLICK
      */
     public static final int ACTION_CLICK = 0x00000010;
 
     /**
      * Action that long clicks on the node.
+     * @see AccessibilityActionCompat#ACTION_LONG_CLICK
      */
     public static final int ACTION_LONG_CLICK = 0x00000020;
 
@@ -1442,6 +1704,7 @@
 
     /**
      * Action that gives accessibility focus to the node.
+     * @see AccessibilityActionCompat#ACTION_ACCESSIBILITY_FOCUS
      */
     public static final int ACTION_ACCESSIBILITY_FOCUS = 0x00000040;
 
@@ -1479,6 +1742,7 @@
      * @see #MOVEMENT_GRANULARITY_LINE
      * @see #MOVEMENT_GRANULARITY_PARAGRAPH
      * @see #MOVEMENT_GRANULARITY_PAGE
+     * @see AccessibilityActionCompat#ACTION_NEXT_AT_MOVEMENT_GRANULARITY
      */
     public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 0x00000100;
 
@@ -1512,6 +1776,7 @@
      * @see #MOVEMENT_GRANULARITY_LINE
      * @see #MOVEMENT_GRANULARITY_PARAGRAPH
      * @see #MOVEMENT_GRANULARITY_PAGE
+     * @see AccessibilityActionCompat#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
      */
     public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 0x00000200;
 
@@ -1527,6 +1792,7 @@
      *   info.performAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, arguments);
      * </code></pre></p>
      * </p>
+     * @see AccessibilityActionCompat#ACTION_NEXT_HTML_ELEMENT
      */
     public static final int ACTION_NEXT_HTML_ELEMENT = 0x00000400;
 
@@ -1542,16 +1808,20 @@
      *   info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT, arguments);
      * </code></pre></p>
      * </p>
+     *
+     * @see AccessibilityActionCompat#ACTION_PREVIOUS_HTML_ELEMENT
      */
     public static final int ACTION_PREVIOUS_HTML_ELEMENT = 0x00000800;
 
     /**
      * Action to scroll the node content forward.
+     * @see AccessibilityActionCompat#ACTION_SCROLL_FORWARD
      */
     public static final int ACTION_SCROLL_FORWARD = 0x00001000;
 
     /**
      * Action to scroll the node content backward.
+     * @see AccessibilityActionCompat#ACTION_SCROLL_BACKWARD
      */
     public static final int ACTION_SCROLL_BACKWARD = 0x00002000;
 
@@ -1559,16 +1829,19 @@
 
     /**
      * Action to copy the current selection to the clipboard.
+     * @see AccessibilityActionCompat#ACTION_COPY
      */
     public static final int ACTION_COPY = 0x00004000;
 
     /**
      * Action to paste the current clipboard content.
+     * @see AccessibilityActionCompat#ACTION_PASTE
      */
     public static final int ACTION_PASTE = 0x00008000;
 
     /**
      * Action to cut the current selection and place it to the clipboard.
+     * @see AccessibilityActionCompat#ACTION_CUT
      */
     public static final int ACTION_CUT = 0x00010000;
 
@@ -1589,21 +1862,25 @@
      *
      * @see #ACTION_ARGUMENT_SELECTION_START_INT
      * @see #ACTION_ARGUMENT_SELECTION_END_INT
+     * @see AccessibilityActionCompat#ACTION_SET_SELECTION
      */
     public static final int ACTION_SET_SELECTION = 0x00020000;
 
     /**
      * Action to expand an expandable node.
+     * @see AccessibilityActionCompat#ACTION_EXPAND
      */
     public static final int ACTION_EXPAND = 0x00040000;
 
     /**
      * Action to collapse an expandable node.
+     * @see AccessibilityActionCompat#ACTION_COLLAPSE
      */
     public static final int ACTION_COLLAPSE = 0x00080000;
 
     /**
-     * Action to dismiss a dismissable node.
+     * Action to dismiss a dismissible node.
+     * @see AccessibilityActionCompat#ACTION_DISMISS
      */
     public static final int ACTION_DISMISS = 0x00100000;
 
@@ -1620,6 +1897,7 @@
      *       "android");
      *   info.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
      * </code></pre></p>
+     * @see AccessibilityActionCompat#ACTION_SET_TEXT
      */
     public static final int ACTION_SET_TEXT = 0x00200000;
 
@@ -1640,8 +1918,8 @@
      * Argument for which HTML element to get moving to the next/previous HTML element.
      * <p>
      * <strong>Type:</strong> String<br>
-     * <strong>Actions:</strong> {@link #ACTION_NEXT_HTML_ELEMENT},
-     *         {@link #ACTION_PREVIOUS_HTML_ELEMENT}
+     * <strong>Actions:</strong> {@link AccessibilityActionCompat#ACTION_NEXT_HTML_ELEMENT},
+     *         {@link AccessibilityActionCompat#ACTION_PREVIOUS_HTML_ELEMENT}
      * </p>
      */
     public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING =
@@ -1656,8 +1934,8 @@
      * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}
      * </p>
      *
-     * @see #ACTION_NEXT_AT_MOVEMENT_GRANULARITY
-     * @see #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+     * @see AccessibilityActionCompat#ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+     * @see AccessibilityActionCompat#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
      */
     public static final String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN =
             "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
@@ -1669,7 +1947,7 @@
      * <strong>Actions:</strong> {@link #ACTION_SET_SELECTION}
      * </p>
      *
-     * @see #ACTION_SET_SELECTION
+     * @see AccessibilityActionCompat#ACTION_SET_SELECTION
      */
     public static final String ACTION_ARGUMENT_SELECTION_START_INT =
             "ACTION_ARGUMENT_SELECTION_START_INT";
@@ -1681,7 +1959,7 @@
      * <strong>Actions:</strong> {@link #ACTION_SET_SELECTION}
      * </p>
      *
-     * @see #ACTION_SET_SELECTION
+     * @see AccessibilityActionCompat#ACTION_SET_SELECTION
      */
     public static final String ACTION_ARGUMENT_SELECTION_END_INT =
             "ACTION_ARGUMENT_SELECTION_END_INT";
@@ -1693,7 +1971,7 @@
      * <strong>Actions:</strong> {@link #ACTION_SET_TEXT}
      * </p>
      *
-     * @see #ACTION_SET_TEXT
+     * @see AccessibilityActionCompat#ACTION_SET_TEXT
      */
     public static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE =
             "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
@@ -1931,6 +2209,73 @@
      */
     public static final int EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH = 20000;
 
+    /**
+     * Prefetching strategy that prefetches the ancestors of the requested node.
+     * <p> Ancestors will be prefetched before siblings and descendants.
+     *
+     * @see #getChild(int, int)
+     * @see #getParent(int)
+     * @see AccessibilityWindowInfoCompat#getRoot(int)
+     * @see AccessibilityService#getRootInActiveWindow(int)
+     * @see AccessibilityEvent#getSource(int)
+     */
+    public static final int FLAG_PREFETCH_ANCESTORS = 0x00000001;
+
+    /**
+     * Prefetching strategy that prefetches the siblings of the requested node.
+     * <p> To avoid disconnected trees, this flag will also prefetch the parent. Siblings will be
+     * prefetched before descendants.
+     *
+     * @see #FLAG_PREFETCH_ANCESTORS for where to use these flags.
+     */
+    public static final int FLAG_PREFETCH_SIBLINGS = 0x00000002;
+
+    /**
+     * Prefetching strategy that prefetches the descendants in a hybrid depth first and breadth
+     * first approach.
+     * <p> The children of the root node is prefetched before recursing on the children. This
+     * must not be combined with {@link #FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST} or
+     * {@link #FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST} or this will trigger an
+     * IllegalArgumentException.
+     *
+     * @see #FLAG_PREFETCH_ANCESTORS for where to use these flags.
+     */
+    public static final int FLAG_PREFETCH_DESCENDANTS_HYBRID = 0x00000004;
+
+    /**
+     * Prefetching strategy that prefetches the descendants of the requested node depth-first.
+     * <p> This must not be combined with {@link #FLAG_PREFETCH_DESCENDANTS_HYBRID} or
+     * {@link #FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST} or this will trigger an
+     * IllegalArgumentException.
+     *
+     * @see #FLAG_PREFETCH_ANCESTORS for where to use these flags.
+     */
+    public static final int FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST = 0x00000008;
+
+    /**
+     * Prefetching strategy that prefetches the descendants of the requested node breadth-first.
+     * <p> This must not be combined with {@link #FLAG_PREFETCH_DESCENDANTS_HYBRID} or
+     * {@link #FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST} or this will trigger an
+     * IllegalArgumentException.
+     *
+     * @see #FLAG_PREFETCH_ANCESTORS for where to use these flags.
+     */
+    public static final int FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST = 0x00000010;
+
+    /**
+     * Prefetching flag that specifies prefetching should not be interrupted by a request to
+     * retrieve a node or perform an action on a node.
+     *
+     * @see #FLAG_PREFETCH_ANCESTORS for where to use these flags.
+     */
+    public static final int FLAG_PREFETCH_UNINTERRUPTIBLE = 0x00000020;
+
+    /**
+     * Maximum batch size of prefetched nodes for a request.
+     */
+    @SuppressLint("MinMaxConstant")
+    public static final int MAX_NUMBER_OF_PREFETCHED_NODES = 50;
+
     private static int sClickableSpanId = 0;
 
     /**
@@ -2153,6 +2498,26 @@
     }
 
     /**
+     * Get the child at given index.
+     *
+     * @param index The child index.
+     * @param prefetchingStrategy the prefetching strategy.
+     * @return The child node.
+     *
+     * @throws IllegalStateException If called outside of an {@link AccessibilityService} and before
+     *                               calling {@link #setQueryFromAppProcessEnabled}.
+     *
+     * @see AccessibilityNodeInfoCompat#getParent(int) for a description of prefetching.
+     */
+    @Nullable
+    public AccessibilityNodeInfoCompat getChild(int index, int prefetchingStrategy) {
+        if (Build.VERSION.SDK_INT >= 33) {
+            return Api33Impl.getChild(mInfo, index, prefetchingStrategy);
+        }
+        return getChild(index);
+    }
+
+    /**
      * Adds a child.
      * <p>
      * <strong>Note:</strong> Cannot be called from an
@@ -2417,6 +2782,41 @@
     }
 
     /**
+     * Gets the parent.
+     *
+     * <p>
+     * Use {@code prefetchingStrategy} to determine the types of
+     * nodes prefetched from the app if the requested node is not in the cache and must be retrieved
+     * by the app. The default strategy for {@link #getParent()} is a combination of ancestor and
+     * sibling strategies. The app will prefetch until all nodes fulfilling the strategies are
+     * fetched, another node request is sent, or the maximum prefetch batch size of
+     * {@link #MAX_NUMBER_OF_PREFETCHED_NODES} nodes is reached. To prevent interruption by another
+     * request and to force prefetching of the max batch size, use
+     * {@link AccessibilityNodeInfoCompat#FLAG_PREFETCH_UNINTERRUPTIBLE}.
+     * </p>
+     *
+     * @param prefetchingStrategy the prefetching strategy.
+     * @return The parent.
+     *
+     * @throws IllegalStateException If called outside of an {@link AccessibilityService} and before
+     *                               calling {@link #setQueryFromAppProcessEnabled}.
+     *
+     * @see #FLAG_PREFETCH_ANCESTORS
+     * @see #FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST
+     * @see #FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST
+     * @see #FLAG_PREFETCH_DESCENDANTS_HYBRID
+     * @see #FLAG_PREFETCH_SIBLINGS
+     * @see #FLAG_PREFETCH_UNINTERRUPTIBLE
+     */
+    @Nullable
+    public AccessibilityNodeInfoCompat getParent(int prefetchingStrategy) {
+        if (Build.VERSION.SDK_INT >= 33) {
+            return Api33Impl.getParent(mInfo, prefetchingStrategy);
+        }
+        return getParent();
+    }
+
+    /**
      * Sets the parent.
      * <p>
      * <strong>Note:</strong> Cannot be called from an
@@ -2526,6 +2926,57 @@
     }
 
     /**
+     * Gets the node bounds in window coordinates.
+     * <p>
+     * When magnification is enabled, the bounds in window are scaled up by magnification scale
+     * and the positions are also adjusted according to the offset of magnification viewport.
+     * For example, it returns Rect(-180, -180, 0, 0) for original bounds Rect(10, 10, 100, 100),
+     * when the magnification scale is 2 and offsets for X and Y are both 200.
+     * <p/>
+     * <p>
+     * Compatibility:
+     * <ul>
+     *     <li>API &lt; 19: No-op</li>
+     * </ul>
+     * @param outBounds The output node bounds.
+     */
+    public void getBoundsInWindow(@NonNull  Rect outBounds) {
+        if (Build.VERSION.SDK_INT >= 34) {
+            Api34Impl.getBoundsInWindow(mInfo, outBounds);
+        } else if (Build.VERSION.SDK_INT >= 19) {
+            Rect extraBounds = Api19Impl.getExtras(mInfo).getParcelable(BOUNDS_IN_WINDOW_KEY);
+            if (extraBounds != null) {
+                outBounds.set(extraBounds.left, extraBounds.top, extraBounds.right,
+                        extraBounds.bottom);
+            }
+        }
+    }
+
+    /**
+     * Sets the node bounds in window coordinates.
+     * <p>
+     *   <strong>Note:</strong> Cannot be called from an
+     *   {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * <p>
+     * Compatibility:
+     * <ul>
+     *     <li>API &lt; 19: No-op</li>
+     * </ul>
+     * @param bounds The node bounds.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setBoundsInWindow(@NonNull Rect bounds) {
+        if (Build.VERSION.SDK_INT >= 34) {
+            Api34Impl.setBoundsInWindow(mInfo, bounds);
+        } else if (Build.VERSION.SDK_INT >= 19) {
+            Api19Impl.getExtras(mInfo).putParcelable(BOUNDS_IN_WINDOW_KEY, bounds);
+        }
+    }
+
+    /**
      * Gets whether this node is checkable.
      *
      * @return True if the node is checkable.
@@ -2920,7 +3371,9 @@
      * Gets the minimum time duration between two content change events.
      */
     public long getMinDurationBetweenContentChangesMillis() {
-        if (Build.VERSION.SDK_INT >= 19) {
+        if (Build.VERSION.SDK_INT >= 34) {
+            return Api34Impl.getMinDurationBetweenContentChangeMillis(mInfo);
+        } else if (Build.VERSION.SDK_INT >= 19) {
             return Api19Impl.getExtras(mInfo).getLong(MIN_DURATION_BETWEEN_CONTENT_CHANGES_KEY);
         }
         return 0;
@@ -2941,7 +3394,9 @@
      * @param duration the minimum duration between content change events.
      */
     public void setMinDurationBetweenContentChangesMillis(long duration) {
-        if (Build.VERSION.SDK_INT >= 19) {
+        if (Build.VERSION.SDK_INT >= 34) {
+            Api34Impl.setMinDurationBetweenContentChangeMillis(mInfo, duration);
+        } else if (Build.VERSION.SDK_INT >= 19) {
             Api19Impl.getExtras(mInfo).putLong(MIN_DURATION_BETWEEN_CONTENT_CHANGES_KEY, duration);
         }
     }
@@ -2980,6 +3435,52 @@
     }
 
     /**
+     * Gets if the node's accessibility data is considered sensitive.
+     *
+     * @return True if the node's data is considered sensitive, false otherwise.
+     * @see View#isAccessibilityDataSensitive()
+     */
+    public boolean isAccessibilityDataSensitive() {
+        if (Build.VERSION.SDK_INT >= 34) {
+            return Api34Impl.isAccessibilityDataSensitive(mInfo);
+        } else {
+            return getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_DATA_SENSITIVE);
+        }
+    }
+
+    /**
+     * Sets whether this node's accessibility data is considered sensitive.
+     *
+     * <p>
+     * For SDK 34 and higher: when set to true the framework will hide this node from
+     * accessibility services with the
+     * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool}
+     * property set to false.
+     * </p>
+     * <p>
+     * Otherwise, for SDK 19 and higher: the framework cannot hide this node but this property may
+     * be read by accessibility services to provide modified behavior for sensitive nodes.
+     * </p>
+     * <p>
+     *   <strong>Note:</strong> Cannot be called from an
+     *   {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     *
+     * @param accessibilityDataSensitive True if the node's accessibility data is considered
+     *                                   sensitive.
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setAccessibilityDataSensitive(boolean accessibilityDataSensitive) {
+        if (Build.VERSION.SDK_INT >= 34) {
+            Api34Impl.setAccessibilityDataSensitive(mInfo, accessibilityDataSensitive);
+        } else {
+            setBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_DATA_SENSITIVE,
+                    accessibilityDataSensitive);
+        }
+    }
+
+    /**
      * Gets the package this node comes from.
      *
      * @return The package name.
@@ -3254,6 +3755,62 @@
     }
 
     /**
+     * Sets the container title for app-developer-defined container which can be any type of
+     * ViewGroup or layout.
+     * Container title will be used to group together related controls, similar to HTML fieldset.
+     * Or container title may identify a large piece of the UI that is visibly grouped together,
+     * such as a toolbar or a card, etc.
+     * <p>
+     * Container title helps to assist in navigation across containers and other groups.
+     * For example, a screen reader may use this to determine where to put accessibility focus.
+     * </p>
+     * <p>
+     * Container title is different from pane title{@link #setPaneTitle} which indicates that the
+     * node represents a window or activity.
+     * </p>
+     *
+     * <p>
+     *  Example: An app can set container titles on several non-modal menus, containing TextViews
+     *  or ImageButtons that have content descriptions, text, etc. Screen readers can quickly
+     *  switch accessibility focus among menus instead of child views.  Other accessibility-services
+     *  can easily find the menu.
+     * </p>
+     * <p>
+     * Compatibility:
+     * <ul>
+     *     <li>API &lt; 19: No-op</li>
+     * </ul>
+     * @param containerTitle The container title that is associated with a ViewGroup/Layout on the
+     *                       screen.
+     */
+    public void setContainerTitle(@Nullable CharSequence containerTitle) {
+        if (Build.VERSION.SDK_INT >= 34) {
+            Api34Impl.setContainerTitle(mInfo, containerTitle);
+        } else if (Build.VERSION.SDK_INT >= 19) {
+            Api19Impl.getExtras(mInfo).putCharSequence(CONTAINER_TITLE_KEY, containerTitle);
+        }
+    }
+
+    /**
+     * Returns the container title.
+     * <p>
+     * Compatibility:
+     * <ul>
+     *     <li>API &lt; 19: Returns null</li>
+     * </ul>
+     * @see #setContainerTitle for details.
+     */
+    @Nullable
+    public CharSequence getContainerTitle() {
+        if (Build.VERSION.SDK_INT >= 34) {
+            return Api34Impl.getContainerTitle(mInfo);
+        } else if (Build.VERSION.SDK_INT >= 19) {
+            return Api19Impl.getExtras(mInfo).getCharSequence(CONTAINER_TITLE_KEY);
+        }
+        return null;
+    }
+
+    /**
      * Return an instance back to be reused.
      * <p>
      * <strong>Note:</strong> You must not touch the object after calling this function.
@@ -4417,7 +4974,11 @@
      */
     @SuppressLint("KotlinPropertyAccess")
     public boolean hasRequestInitialAccessibilityFocus() {
-        return getBooleanProperty(BOOLEAN_PROPERTY_HAS_REQUEST_INITIAL_ACCESSIBILITY_FOCUS);
+        if (Build.VERSION.SDK_INT >= 34) {
+            return Api34Impl.hasRequestInitialAccessibilityFocus(mInfo);
+        } else {
+            return getBooleanProperty(BOOLEAN_PROPERTY_HAS_REQUEST_INITIAL_ACCESSIBILITY_FOCUS);
+        }
     }
 
     /**
@@ -4440,8 +5001,12 @@
      */
     @SuppressLint("GetterSetterNames")
     public void setRequestInitialAccessibilityFocus(boolean requestInitialAccessibilityFocus) {
-        setBooleanProperty(BOOLEAN_PROPERTY_HAS_REQUEST_INITIAL_ACCESSIBILITY_FOCUS,
-                requestInitialAccessibilityFocus);
+        if (Build.VERSION.SDK_INT >= 34) {
+            Api34Impl.setRequestInitialAccessibilityFocus(mInfo, requestInitialAccessibilityFocus);
+        } else {
+            setBooleanProperty(BOOLEAN_PROPERTY_HAS_REQUEST_INITIAL_ACCESSIBILITY_FOCUS,
+                    requestInitialAccessibilityFocus);
+        }
     }
 
     /**
@@ -4551,6 +5116,59 @@
         }
     }
 
+    /**
+     * Connects this node to the View's root so that operations on this node can query the entire
+     * {@link AccessibilityNodeInfoCompat} tree and perform accessibility actions on nodes.
+     *
+     * <p>
+     * Testing or debugging tools should create this {@link AccessibilityNodeInfoCompat} node using
+     * {@link ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)}
+     * or {@link AccessibilityNodeProviderCompat} and call this
+     * method, then navigate and interact with the node tree by calling methods on the node.
+     * Calling this method more than once on the same node is a no-op. After calling this method,
+     * all nodes linked to this node (children, ancestors, etc.) are also queryable.
+     * </p>
+     *
+     * <p>
+     * Here "query" refers to the following node operations:
+     * <ul>
+     *      <li>check properties of this node (example: {@link #isScrollable()})</li>
+     *      <li>find and query children (example: {@link #getChild(int)})</li>
+     *      <li>find and query the parent (example: {@link #getParent()})</li>
+     *      <li>find focus (examples: {@link #findFocus(int)}, {@link #focusSearch(int)})</li>
+     *      <li>find and query other nodes (example:
+     *      {@link #findAccessibilityNodeInfosByText(String)},
+     *      {@link #findAccessibilityNodeInfosByViewId(String)})</li>
+     *      <li>perform actions (example: {@link #performAction(int)})</li>
+     * </ul>
+     * </p>
+     *
+     * <p>
+     * This is intended for short-lived inspections from testing or debugging tools in the app
+     * process, as operations on this node tree will only succeed as long as the associated
+     * view hierarchy remains attached to a window. {@link AccessibilityNodeInfoCompat} objects can
+     * quickly become out of sync with their corresponding {@link View} objects; if you wish to
+     * inspect a changed or different view hierarchy then create a new node from any view in that
+     * hierarchy and call this method on that new node, instead of disabling & re-enabling the
+     * connection on the previous node.
+     * </p>
+     * <p>
+     * Compatibility:
+     * <ul>
+     *     <li>API &lt; 34: No-op</li>
+     * </ul>
+     *
+     * @param view The view that generated this node, or any view in the same view-root hierarchy.
+     * @param enabled Whether to enable (true) or disable (false) querying from the app process.
+     * @throws IllegalStateException If called from an {@link AccessibilityService}, or if provided
+     *                               a {@link View} that is not attached to a window.
+     */
+    public void setQueryFromAppProcessEnabled(@NonNull View view, boolean enabled) {
+        if (Build.VERSION.SDK_INT >= 34) {
+            Api34Impl.setQueryFromAppProcessEnabled(mInfo, view, enabled);
+        }
+    }
+
     @Override
     public int hashCode() {
         return (mInfo == null) ? 0 : mInfo.hashCode();
@@ -4599,6 +5217,9 @@
         getBoundsInScreen(bounds);
         builder.append("; boundsInScreen: " + bounds);
 
+        getBoundsInWindow(bounds);
+        builder.append("; boundsInWindow: " + bounds);
+
         builder.append("; packageName: ").append(getPackageName());
         builder.append("; className: ").append(getClassName());
         builder.append("; text: ").append(getText());
@@ -4621,10 +5242,12 @@
         builder.append("; enabled: ").append(isEnabled());
         builder.append("; password: ").append(isPassword());
         builder.append("; scrollable: " + isScrollable());
+        builder.append("; containerTitle: ").append(getContainerTitle());
         builder.append("; granularScrollingSupported: ").append(isGranularScrollingSupported());
         builder.append("; importantForAccessibility: ").append(isImportantForAccessibility());
         builder.append("; visible: ").append(isVisibleToUser());
         builder.append("; isTextSelectable: ").append(isTextSelectable());
+        builder.append("; accessibilityDataSensitive: ").append(isAccessibilityDataSensitive());
 
         builder.append("; [");
         if (Build.VERSION.SDK_INT >= 21) {
@@ -4762,6 +5385,46 @@
         }
     }
 
+    @RequiresApi(19)
+    private static class Api19Impl {
+        private Api19Impl() {
+            // This class is non instantiable.
+        }
+
+        @DoNotInline
+        public static Bundle getExtras(AccessibilityNodeInfo info) {
+            return info.getExtras();
+        }
+
+        @DoNotInline
+        public static Object createRangeInfo(int type, float min, float max, float current) {
+            return AccessibilityNodeInfo.RangeInfo.obtain(type, min, max, current);
+        }
+
+        @DoNotInline
+        public static CollectionItemInfoCompat createCollectionItemInfo(int rowIndex, int rowSpan,
+                int columnIndex, int columnSpan, boolean heading) {
+            return new CollectionItemInfoCompat(
+                    AccessibilityNodeInfo.CollectionItemInfo.obtain(rowIndex, rowSpan, columnIndex,
+                            columnSpan, heading));
+        }
+    }
+
+    @RequiresApi(21)
+    private static class Api21Impl {
+        private Api21Impl() {
+            // This class is non instantiable.
+        }
+
+        @DoNotInline
+        public static CollectionItemInfoCompat createCollectionItemInfo(int rowIndex, int rowSpan,
+                int columnIndex, int columnSpan, boolean heading, boolean selected) {
+            return new CollectionItemInfoCompat(
+                    AccessibilityNodeInfo.CollectionItemInfo.obtain(rowIndex, rowSpan, columnIndex,
+                            columnSpan, heading, selected));
+        }
+    }
+
     @RequiresApi(30)
     private static class Api30Impl {
         private Api30Impl() {
@@ -4778,6 +5441,11 @@
         public static CharSequence getStateDescription(AccessibilityNodeInfo info) {
             return info.getStateDescription();
         }
+
+        @DoNotInline
+        public static Object createRangeInfo(int type, float min, float max, float current) {
+            return new AccessibilityNodeInfo.RangeInfo(type, min, max, current);
+        }
     }
 
     @RequiresApi(33)
@@ -4803,6 +5471,36 @@
         }
 
         @DoNotInline
+        public static CollectionItemInfoCompat buildCollectionItemInfoCompat(
+                boolean heading, int columnIndex, int rowIndex, int columnSpan,
+                int rowSpan, boolean selected, String rowTitle, String columnTitle) {
+            return new CollectionItemInfoCompat(
+                    new AccessibilityNodeInfo.CollectionItemInfo.Builder()
+                    .setHeading(heading).setColumnIndex(columnIndex)
+                    .setRowIndex(rowIndex)
+                    .setColumnSpan(columnSpan)
+                    .setRowSpan(rowSpan)
+                    .setSelected(selected)
+                    .setRowTitle(rowTitle)
+                    .setColumnTitle(columnTitle)
+                    .build());
+        }
+
+        @DoNotInline
+        public static AccessibilityNodeInfoCompat getChild(AccessibilityNodeInfo info, int index,
+                int prefetchingStrategy) {
+            return AccessibilityNodeInfoCompat.wrapNonNullInstance(info.getChild(index,
+                    prefetchingStrategy));
+        }
+
+        @DoNotInline
+        public static AccessibilityNodeInfoCompat getParent(AccessibilityNodeInfo info,
+                int prefetchingStrategy) {
+            return AccessibilityNodeInfoCompat.wrapNonNullInstance(info.getParent(
+                    prefetchingStrategy));
+        }
+
+        @DoNotInline
         public static String getUniqueId(AccessibilityNodeInfo info) {
             return info.getUniqueId();
         }
@@ -4811,17 +5509,83 @@
         public static void setUniqueId(AccessibilityNodeInfo info, String uniqueId) {
             info.setUniqueId(uniqueId);
         }
+
+        @DoNotInline
+        public static String getCollectionItemRowTitle(Object info) {
+            return ((AccessibilityNodeInfo.CollectionItemInfo) info).getRowTitle();
+
+        }
+
+        @DoNotInline
+        public static String getCollectionItemColumnTitle(Object info) {
+            return ((AccessibilityNodeInfo.CollectionItemInfo) info).getColumnTitle();
+        }
     }
 
-    @RequiresApi(19)
-    private static class Api19Impl {
-        private Api19Impl() {
+    @RequiresApi(34)
+    private static class Api34Impl {
+        private Api34Impl() {
             // This class is non instantiable.
         }
 
         @DoNotInline
-        public static Bundle getExtras(AccessibilityNodeInfo info) {
-            return info.getExtras();
+        public static boolean isAccessibilityDataSensitive(AccessibilityNodeInfo info) {
+            return info.isAccessibilityDataSensitive();
+        }
+
+        @DoNotInline
+        public static void setAccessibilityDataSensitive(AccessibilityNodeInfo info,
+                boolean accessibilityDataSensitive) {
+            info.setAccessibilityDataSensitive(accessibilityDataSensitive);
+        }
+
+        @DoNotInline
+        public static CharSequence getContainerTitle(AccessibilityNodeInfo info) {
+            return info.getContainerTitle();
+        }
+
+        @DoNotInline
+        public static void setContainerTitle(AccessibilityNodeInfo info,
+                CharSequence containerTitle) {
+            info.setContainerTitle(containerTitle);
+        }
+
+        @DoNotInline
+        public static void getBoundsInWindow(AccessibilityNodeInfo info, Rect bounds) {
+            info.getBoundsInWindow(bounds);
+        }
+
+        @DoNotInline
+        public static void setBoundsInWindow(AccessibilityNodeInfo info, Rect bounds) {
+            info.setBoundsInWindow(bounds);
+        }
+
+        @DoNotInline
+        public static boolean hasRequestInitialAccessibilityFocus(AccessibilityNodeInfo info) {
+            return info.hasRequestInitialAccessibilityFocus();
+        }
+
+        @DoNotInline
+        public static void setRequestInitialAccessibilityFocus(AccessibilityNodeInfo info,
+                boolean requestInitialAccessibilityFocus) {
+            info.setRequestInitialAccessibilityFocus(requestInitialAccessibilityFocus);
+        }
+
+        @DoNotInline
+        public static long getMinDurationBetweenContentChangeMillis(AccessibilityNodeInfo info) {
+            return info.getMinDurationBetweenContentChanges().toMillis();
+        }
+
+        @DoNotInline
+        public static void setMinDurationBetweenContentChangeMillis(AccessibilityNodeInfo info,
+                long duration) {
+            info.setMinDurationBetweenContentChanges(Duration.ofMillis(duration));
+        }
+
+        @DoNotInline
+        public static void setQueryFromAppProcessEnabled(AccessibilityNodeInfo info, View view,
+                boolean enabled) {
+            info.setQueryFromAppProcessEnabled(view, enabled);
         }
     }
 }
diff --git a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeProviderCompat.java b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeProviderCompat.java
index 513e38f..6b12814 100644
--- a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeProviderCompat.java
+++ b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityNodeProviderCompat.java
@@ -30,6 +30,11 @@
 
 /**
  * Helper for accessing {@link android.view.accessibility.AccessibilityNodeProvider}.
+ * <p>
+ * <aside class="note">
+ * <b>Note:</b> Consider using a {@link androidx.customview.widget.ExploreByTouchHelper}, a utility
+ * extension of AccessibilityNodeProvider, to simplify many aspects of providing information to
+ * accessibility services and managing accessibility focus. </aside>
  */
 public class AccessibilityNodeProviderCompat {
     @RequiresApi(16)
diff --git a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java
index 91586a5..ab41730 100644
--- a/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java
+++ b/core/core/src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java
@@ -21,6 +21,9 @@
 
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.os.Build;
+import android.os.LocaleList;
+import android.os.SystemClock;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityWindowInfo;
 
@@ -28,6 +31,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
+import androidx.core.os.LocaleListCompat;
 
 /**
  * Helper for accessing {@link android.view.accessibility.AccessibilityWindowInfo}.
@@ -75,6 +79,12 @@
     public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5;
 
     /**
+     * Window type: A system window used to show the UI for the interaction with
+     * window-based magnification, which includes the magnified content and the option menu.
+     */
+    public static final int TYPE_MAGNIFICATION_OVERLAY = 6;
+
+    /**
      * Creates a wrapper for info implementation.
      *
      * @param object The info to wrap.
@@ -157,13 +167,33 @@
     }
 
     /**
-     * Check if the window is in picture-in-picture mode.
+     * Gets the root node in the window's hierarchy.
      *
+     * @param prefetchingStrategy the prefetching strategy.
+     * @return The root node.
+     *
+     * @see AccessibilityNodeInfoCompat#getParent(int) for a description of prefetching.
+     */
+    @Nullable
+    public AccessibilityNodeInfoCompat getRoot(int prefetchingStrategy) {
+        if (Build.VERSION.SDK_INT >= 33) {
+            return Api33Impl.getRoot(mInfo, prefetchingStrategy);
+        }
+        return getRoot();
+    }
+
+    /**
+     * Check if the window is in picture-in-picture mode.
+     * <p>
+     * Compatibility:
+     * <ul>
+     *     <li>API &lt; 26: Returns false.</li>
+     * </ul>
      * @return {@code true} if the window is in picture-in-picture mode, {@code false} otherwise.
      */
     public boolean isInPictureInPictureMode() {
-        if (SDK_INT >= 33) {
-            return Api33Impl.isInPictureInPictureMode((AccessibilityWindowInfo) mInfo);
+        if (SDK_INT >= 26) {
+            return Api26Impl.isInPictureInPictureMode((AccessibilityWindowInfo) mInfo);
         } else {
             return false;
         }
@@ -322,6 +352,40 @@
     }
 
     /**
+     * Returns the {@link SystemClock#uptimeMillis()} at which the last transition happens.
+     * A transition happens when {@link #getBoundsInScreen(Rect)} is changed.
+     * <p>
+     * Compatibility:
+     * <ul>
+     *   <li>Api &lt; 34: Will return 0.</li>
+     * </ul>
+     * @return The transition timestamp.
+     */
+    public long getTransitionTimeMillis() {
+        if (SDK_INT >= 34) {
+            return Api34Impl.getTransitionTimeMillis((AccessibilityWindowInfo) mInfo);
+        }
+        return 0;
+    }
+
+    /**
+     * Returns the {@link android.os.LocaleList} of the window.
+     * <p>
+     * Compatibility:
+     * <ul>
+     *   <li>Api &lt; 34: Will return {@link LocaleListCompat#getEmptyLocaleList()}.</li>
+     * </ul>
+     * @return the locales of the window.
+     */
+    public @NonNull LocaleListCompat getLocales() {
+        if (SDK_INT >= 34) {
+            return LocaleListCompat.wrap(Api34Impl.getLocales((AccessibilityWindowInfo) mInfo));
+        } else {
+            return LocaleListCompat.getEmptyLocaleList();
+        }
+    }
+
+    /**
      * Gets the title of the window.
      *
      * @return The title of the window, or the application label for the window if no title was
@@ -448,6 +512,8 @@
         builder.append(", active=").append(isActive());
         builder.append(", hasParent=").append(getParent() != null);
         builder.append(", hasChildren=").append(getChildCount() > 0);
+        builder.append(", transitionTime=").append(getTransitionTimeMillis());
+        builder.append(", locales=").append(getLocales());
         builder.append(']');
         return builder.toString();
     }
@@ -560,6 +626,18 @@
         }
     }
 
+    @RequiresApi(26)
+    private static class Api26Impl {
+        private Api26Impl() {
+            // This class is non instantiable.
+        }
+
+        @DoNotInline
+        static boolean isInPictureInPictureMode(AccessibilityWindowInfo info) {
+            return info.isInPictureInPictureMode();
+        }
+    }
+
     @RequiresApi(30)
     private static class Api30Impl {
         private Api30Impl() {
@@ -589,8 +667,26 @@
         }
 
         @DoNotInline
-        static boolean isInPictureInPictureMode(AccessibilityWindowInfo info) {
-            return info.isInPictureInPictureMode();
+        public static AccessibilityNodeInfoCompat getRoot(Object info, int prefetchingStrategy) {
+            return AccessibilityNodeInfoCompat.wrapNonNullInstance(
+                    ((AccessibilityWindowInfo) info).getRoot(prefetchingStrategy));
+        }
+    }
+
+    @RequiresApi(34)
+    private static class Api34Impl {
+        private Api34Impl() {
+            // This class is non instantiable.
+        }
+
+        @DoNotInline
+        public static long getTransitionTimeMillis(AccessibilityWindowInfo info) {
+            return info.getTransitionTimeMillis();
+        }
+
+        @DoNotInline
+        static LocaleList getLocales(AccessibilityWindowInfo info) {
+            return info.getLocales();
         }
     }
 }
\ No newline at end of file
diff --git a/core/core/src/test/java/androidx/core/math/MathUtilsTest.kt b/core/core/src/test/java/androidx/core/math/MathUtilsTest.kt
new file mode 100644
index 0000000..3074b56
--- /dev/null
+++ b/core/core/src/test/java/androidx/core/math/MathUtilsTest.kt
@@ -0,0 +1,354 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.core.math
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class MathUtilsTest {
+
+    @Test
+    fun testAddExact() {
+        // zero + zero
+        Assert.assertEquals(0, MathUtils.addExact(0, 0).toLong())
+        Assert.assertEquals(0L, MathUtils.addExact(0L, 0L))
+        // positive + positive
+        Assert.assertEquals(2, MathUtils.addExact(1, 1).toLong())
+        Assert.assertEquals(2L, MathUtils.addExact(1L, 1L))
+        // negative + negative
+        Assert.assertEquals(-2, MathUtils.addExact(-1, -1).toLong())
+        Assert.assertEquals(-2L, MathUtils.addExact(-1L, -1L))
+        // positive + negative
+        Assert.assertEquals(0, MathUtils.addExact(1, -1).toLong())
+        Assert.assertEquals(0L, MathUtils.addExact(1L, -1L))
+        Assert.assertEquals(-1, MathUtils.addExact(1, -2).toLong())
+        Assert.assertEquals(-1L, MathUtils.addExact(1L, -2L))
+        Assert.assertEquals(1, MathUtils.addExact(2, -1).toLong())
+        Assert.assertEquals(1L, MathUtils.addExact(2L, -1L))
+        // negative + positive
+        Assert.assertEquals(0, MathUtils.addExact(-1, 1).toLong())
+        Assert.assertEquals(0L, MathUtils.addExact(-1L, 1L))
+        Assert.assertEquals(1, MathUtils.addExact(-1, 2).toLong())
+        Assert.assertEquals(1L, MathUtils.addExact(-1L, 2L))
+        Assert.assertEquals(-1, MathUtils.addExact(-2, 1).toLong())
+        Assert.assertEquals(-1L, MathUtils.addExact(-2L, 1L))
+        // zero + positive, positive + zero
+        Assert.assertEquals(1, MathUtils.addExact(0, 1).toLong())
+        Assert.assertEquals(1L, MathUtils.addExact(0L, 1L))
+        Assert.assertEquals(1, MathUtils.addExact(1, 0).toLong())
+        Assert.assertEquals(1L, MathUtils.addExact(1L, 0L))
+        // zero + negative, negative + zero
+        Assert.assertEquals(-1, MathUtils.addExact(0, -1).toLong())
+        Assert.assertEquals(-1L, MathUtils.addExact(0L, -1L))
+        Assert.assertEquals(-1, MathUtils.addExact(-1, 0).toLong())
+        Assert.assertEquals(-1L, MathUtils.addExact(-1L, 0L))
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.addExact(
+                Int.MAX_VALUE,
+                1
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.addExact(
+                Long.MAX_VALUE,
+                1L
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.addExact(
+                Int.MIN_VALUE,
+                -1
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.addExact(
+                Long.MIN_VALUE,
+                -1L
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.addExact(
+                Integer.MIN_VALUE,
+                Integer.MIN_VALUE
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.addExact(
+                Long.MIN_VALUE,
+                Long.MIN_VALUE
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.addExact(
+                Integer.MAX_VALUE,
+                Integer.MAX_VALUE
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.addExact(
+                Long.MAX_VALUE,
+                Long.MAX_VALUE
+            )
+        }
+    }
+
+    @Test
+    fun testSubtractExact() {
+        // zero - zero
+        Assert.assertEquals(0, MathUtils.subtractExact(0, 0).toLong())
+        Assert.assertEquals(0L, MathUtils.subtractExact(0L, 0L))
+        // positive - positive
+        Assert.assertEquals(0, MathUtils.subtractExact(1, 1).toLong())
+        Assert.assertEquals(0L, MathUtils.subtractExact(1L, 1L))
+        Assert.assertEquals(1, MathUtils.subtractExact(2, 1).toLong())
+        Assert.assertEquals(1L, MathUtils.subtractExact(2L, 1L))
+        Assert.assertEquals(-1, MathUtils.subtractExact(1, 2).toLong())
+        Assert.assertEquals(-1L, MathUtils.subtractExact(1L, 2L))
+        // negative - negative
+        Assert.assertEquals(0, MathUtils.subtractExact(-1, -1).toLong())
+        Assert.assertEquals(0L, MathUtils.subtractExact(-1L, -1L))
+        Assert.assertEquals(-1, MathUtils.subtractExact(-2, -1).toLong())
+        Assert.assertEquals(-1L, MathUtils.subtractExact(-2L, -1L))
+        Assert.assertEquals(1, MathUtils.subtractExact(-1, -2).toLong())
+        Assert.assertEquals(1L, MathUtils.subtractExact(-1L, -2L))
+        // positive - negative, negative - positive
+        Assert.assertEquals(2, MathUtils.subtractExact(1, -1).toLong())
+        Assert.assertEquals(2L, MathUtils.subtractExact(1L, -1L))
+        Assert.assertEquals(-2, MathUtils.subtractExact(-1, 1).toLong())
+        Assert.assertEquals(-2L, MathUtils.subtractExact(-1L, 1L))
+        // zero - positive, positive - zero
+        Assert.assertEquals(-1, MathUtils.subtractExact(0, 1).toLong())
+        Assert.assertEquals(-1L, MathUtils.subtractExact(0L, 1L))
+        Assert.assertEquals(1, MathUtils.subtractExact(1, 0).toLong())
+        Assert.assertEquals(1L, MathUtils.subtractExact(1L, 0L))
+        // zero - negative, negative - zero
+        Assert.assertEquals(1, MathUtils.subtractExact(0, -1).toLong())
+        Assert.assertEquals(1L, MathUtils.subtractExact(0L, -1L))
+        Assert.assertEquals(-1, MathUtils.subtractExact(-1, 0).toLong())
+        Assert.assertEquals(-1L, MathUtils.subtractExact(-1L, 0))
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.subtractExact(
+                Int.MAX_VALUE,
+                -1
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.subtractExact(
+                Long.MAX_VALUE,
+                -1L
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.subtractExact(
+                Int.MIN_VALUE,
+                1
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.subtractExact(
+                Long.MIN_VALUE,
+                1L
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.subtractExact(
+                0,
+                Int.MIN_VALUE
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.subtractExact(
+                0,
+                Long.MIN_VALUE
+            )
+        }
+    }
+
+    @Test
+    fun testMultiplyExact() {
+        Assert.assertEquals(0, MathUtils.multiplyExact(0, 0).toLong())
+        Assert.assertEquals(4, MathUtils.multiplyExact(2, 2).toLong())
+        Assert.assertEquals(0L, MathUtils.multiplyExact(0L, 0L))
+        Assert.assertEquals(4L, MathUtils.multiplyExact(2L, 2L))
+        Assert.assertEquals(0, MathUtils.multiplyExact(2, 0).toLong())
+        Assert.assertEquals(0L, MathUtils.multiplyExact(2L, 0L))
+        Assert.assertEquals(-4, MathUtils.multiplyExact(2, -2).toLong())
+        Assert.assertEquals(-4L, MathUtils.multiplyExact(2L, -2L))
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.multiplyExact(
+                Int.MAX_VALUE,
+                2
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.multiplyExact(
+                Long.MAX_VALUE,
+                2L
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.multiplyExact(
+                Int.MIN_VALUE,
+                2
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.multiplyExact(
+                Long.MIN_VALUE,
+                2L
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.multiplyExact(
+                Int.MAX_VALUE / 2 + 1,
+                2
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.multiplyExact(
+                Long.MAX_VALUE / 2L + 1L,
+                2L
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.multiplyExact(
+                Int.MIN_VALUE / 2 - 1,
+                2
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.multiplyExact(
+                Long.MIN_VALUE / 2L - 1L,
+                2L
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.multiplyExact(
+                Int.MIN_VALUE,
+                -1
+            )
+        }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) {
+            MathUtils.multiplyExact(
+                Long.MIN_VALUE,
+                -1L
+            )
+        }
+    }
+
+    @Test
+    fun testIncrementExact() {
+        Assert.assertEquals(1, MathUtils.incrementExact(0).toLong())
+        Assert.assertEquals(1L, MathUtils.incrementExact(0L))
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) { MathUtils.incrementExact(Int.MAX_VALUE) }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) { MathUtils.incrementExact(Long.MAX_VALUE) }
+    }
+
+    @Test
+    fun testDecrementExact() {
+        Assert.assertEquals(-1, MathUtils.decrementExact(0).toLong())
+        Assert.assertEquals(-1L, MathUtils.decrementExact(0L))
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) { MathUtils.decrementExact(Int.MIN_VALUE) }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) { MathUtils.decrementExact(Long.MIN_VALUE) }
+    }
+
+    @Test
+    fun testNegateExact() {
+        Assert.assertEquals(
+            (Int.MIN_VALUE + 1).toLong(),
+            MathUtils.negateExact(Int.MAX_VALUE).toLong()
+        )
+        Assert.assertEquals(Long.MIN_VALUE + 1, MathUtils.negateExact(Long.MAX_VALUE))
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) { MathUtils.negateExact(Int.MIN_VALUE) }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) { MathUtils.negateExact(Long.MIN_VALUE) }
+    }
+
+    @Test
+    fun testToIntExact() {
+        Assert.assertEquals(1, MathUtils.toIntExact(1L).toLong())
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) { MathUtils.toIntExact(Long.MAX_VALUE) }
+        Assert.assertThrows(
+            ArithmeticException::class.java
+        ) { MathUtils.toIntExact(Long.MIN_VALUE) }
+    }
+}
diff --git a/credentials/credentials-play-services-auth/src/androidTest/AndroidManifest.xml b/credentials/credentials-play-services-auth/src/androidTest/AndroidManifest.xml
index 73badf7..d7b0e91 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/AndroidManifest.xml
+++ b/credentials/credentials-play-services-auth/src/androidTest/AndroidManifest.xml
@@ -21,9 +21,5 @@
             android:name="androidx.credentials.playservices.TestCredentialsActivity"
             android:exported="false"
             />
-        <activity
-            android:name="androidx.credentials.playservices.TestCredentialsFragmentActivity"
-            android:exported="false"
-            />
     </application>
 </manifest>
\ No newline at end of file
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/CredentialProviderPlayServicesImplTest.kt b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/CredentialProviderPlayServicesImplTest.kt
index ad59aa9..cc63892 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/CredentialProviderPlayServicesImplTest.kt
+++ b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/CredentialProviderPlayServicesImplTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.credentials.playservices
 
-import android.os.Build
 import androidx.credentials.playservices.TestUtils.Companion.ConnectionResultFailureCases
 import androidx.test.core.app.ActivityScenario
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -36,9 +35,6 @@
     @Test
     @SdkSuppress(maxSdkVersion = 33)
     fun isAvailableOnDevice_apiSuccess_returnsTrue() {
-        if (Build.VERSION.SDK_INT >= 34) {
-            return // TODO(b/285651071) : Fix once Mockito fixes mock issue, around these test cases
-        }
         val activityScenario = ActivityScenario.launch(
             TestCredentialsActivity::class.java
         )
@@ -61,9 +57,6 @@
     @Test
     @SdkSuppress(maxSdkVersion = 33)
     fun isAvailableOnDevice_apiNotSuccess_returnsFalse() {
-        if (Build.VERSION.SDK_INT >= 34) {
-            return // Wait until Mockito fixes 'mock' for API 34
-        }
         val activityScenario = ActivityScenario.launch(
             TestCredentialsActivity::class.java
         )
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/TestCredentialsFragmentActivity.kt b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/TestCredentialsFragmentActivity.kt
deleted file mode 100644
index f7ad106..0000000
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/TestCredentialsFragmentActivity.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.credentials.playservices
-
-import android.os.Build
-import androidx.annotation.RequiresApi
-import androidx.fragment.app.FragmentActivity
-
-/**
- * This is a test activity used by the Robolectric Activity Scenario tests. It acts as a calling
- * activity in our test cases. This activity uses fragments.
- */
-@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
-class TestCredentialsFragmentActivity : FragmentActivity()
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/TestUtils.kt b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/TestUtils.kt
index 1bf209d..f1cf823 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/TestUtils.kt
+++ b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/TestUtils.kt
@@ -38,7 +38,6 @@
         }
 
         /**
-         * // TODO remove this or modify so it has forced checks.
          * Given a superset and a subset json, this figures out if the subset can be found
          * within the superset by recursively checking for values that exist in the subset
          * also existing in the superset in the same format. Note this means that the superset
@@ -91,8 +90,6 @@
                     if (!superSet.containsAll(subSet)) {
                         return false
                     }
-                    // TODO("For specific sequences, place into a treeset (sorted by specific
-                    // TODO("required identifiers) and compare subset to superset"))
                 } else {
                     if (!values.equals(superValues)) {
                         return false
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerJavaTest.java b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerJavaTest.java
index acbfe57..e4cddda 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerJavaTest.java
+++ b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerJavaTest.java
@@ -20,14 +20,12 @@
 
 import static org.junit.Assert.assertThrows;
 
-import android.app.Activity;
-import android.os.Build;
-
 import androidx.credentials.GetCredentialRequest;
 import androidx.credentials.GetPasswordOption;
 import androidx.credentials.playservices.TestCredentialsActivity;
 import androidx.credentials.playservices.controllers.BeginSignIn.CredentialProviderBeginSignInController;
 import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.google.android.gms.auth.api.identity.BeginSignInRequest;
@@ -35,56 +33,19 @@
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
 
 import java.util.HashSet;
 import java.util.List;
 
-@RunWith(Parameterized.class)
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 @SuppressWarnings("deprecation")
 public class CredentialProviderBeginSignInControllerJavaTest {
-
-    private final boolean mUseFragmentActivity;
-
-    @Parameterized.Parameters
-    public static Object[] data() {
-        return new Object[] {true, false};
-    }
-
-    public CredentialProviderBeginSignInControllerJavaTest(final boolean useFragmentActivity)
-            throws Throwable {
-        mUseFragmentActivity = useFragmentActivity;
-    }
-
-    interface TestActivityListener {
-        void onActivity(Activity a);
-    }
-
-    private void launchTestActivity(TestActivityListener listener) {
-        if (mUseFragmentActivity && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
-            ActivityScenario<androidx.credentials.playservices.TestCredentialsFragmentActivity>
-                    activityScenario =
-                            ActivityScenario.launch(
-                                    androidx.credentials.playservices
-                                            .TestCredentialsFragmentActivity.class);
-            activityScenario.onActivity(
-                    activity -> {
-                        listener.onActivity((Activity) activity);
-                    });
-        } else {
-            ActivityScenario<TestCredentialsActivity> activityScenario =
-                    ActivityScenario.launch(TestCredentialsActivity.class);
-            activityScenario.onActivity(
-                    activity -> {
-                        listener.onActivity((Activity) activity);
-                    });
-        }
-    }
-
     @Test
     public void convertRequestToPlayServices_setPasswordOptionRequestAndFalseAutoSelect_success() {
-        launchTestActivity(
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+        activityScenario.onActivity(
                 activity -> {
                     BeginSignInRequest actualResponse =
                             CredentialProviderBeginSignInController.getInstance(activity)
@@ -99,7 +60,9 @@
 
     @Test
     public void convertRequestToPlayServices_setPasswordOptionRequestAndTrueAutoSelect_success() {
-        launchTestActivity(
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+        activityScenario.onActivity(
                 activity -> {
                     BeginSignInRequest actualResponse =
                             CredentialProviderBeginSignInController.getInstance(activity)
@@ -116,7 +79,9 @@
 
     @Test
     public void convertRequestToPlayServices_nullRequest_throws() {
-        launchTestActivity(
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+        activityScenario.onActivity(
                 activity -> {
                     assertThrows(
                             "null get credential request must throw exception",
@@ -129,7 +94,9 @@
 
     @Test
     public void convertResponseToCredentialManager_nullRequest_throws() {
-        launchTestActivity(
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+        activityScenario.onActivity(
                 activity -> {
                     assertThrows(
                             "null sign in credential response must throw exception",
@@ -142,6 +109,9 @@
 
     @Test
     public void convertRequestToPlayServices_setGoogleIdOptionRequestAndTrueAutoSelect_success() {
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+
         GetGoogleIdOption option =
                 new GetGoogleIdOption.Builder()
                         .setServerClientId("server_client_id")
@@ -152,7 +122,7 @@
                         .setAutoSelectEnabled(true)
                         .build();
 
-        launchTestActivity(
+        activityScenario.onActivity(
                 activity -> {
                     BeginSignInRequest actualRequest =
                             CredentialProviderBeginSignInController.getInstance(activity)
@@ -181,7 +151,9 @@
 
     @Test
     public void duplicateGetInstance_shouldBeEqual() {
-        launchTestActivity(
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+        activityScenario.onActivity(
                 activity -> {
                     CredentialProviderBeginSignInController firstInstance =
                             CredentialProviderBeginSignInController.getInstance(activity);
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerTest.kt b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerTest.kt
index 5e05605..d245c4b 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerTest.kt
+++ b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/beginsignin/CredentialProviderBeginSignInControllerTest.kt
@@ -16,55 +16,32 @@
 
 package androidx.credentials.playservices.beginsignin
 
-import android.app.Activity
 import android.os.Build
 import androidx.annotation.RequiresApi
 import androidx.credentials.GetCredentialRequest
 import androidx.credentials.GetPasswordOption
 import androidx.credentials.playservices.TestCredentialsActivity
-import androidx.credentials.playservices.TestCredentialsFragmentActivity
 import androidx.credentials.playservices.controllers.BeginSignIn.CredentialProviderBeginSignInController.Companion.getInstance
 import androidx.test.core.app.ActivityScenario
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.google.android.libraries.identity.googleid.GetGoogleIdOption
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
 
-@RunWith(Parameterized::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 @Suppress("deprecation")
 @RequiresApi(api = Build.VERSION_CODES.O)
-class CredentialProviderBeginSignInControllerTest(val useFragmentActivity: Boolean) {
-
-    companion object {
-        @JvmStatic
-        @Parameterized.Parameters(name = "{0}")
-        fun initParameters() = listOf(true, false)
-    }
-
-    private fun launchTestActivity(callback: (activity: Activity) -> Unit) {
-        if (useFragmentActivity && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
-            var activityScenario =
-                            ActivityScenario.launch(
-                                    androidx.credentials.playservices
-                                            .TestCredentialsFragmentActivity::class.java)
-            activityScenario.onActivity { activity: Activity ->
-                callback.invoke(activity)
-            }
-        } else {
-            var activityScenario = ActivityScenario.launch(TestCredentialsActivity::class.java)
-            activityScenario.onActivity { activity: Activity ->
-                callback.invoke(activity)
-            }
-        }
-    }
-
+class CredentialProviderBeginSignInControllerTest {
     @Test
     fun convertRequestToPlayServices_setPasswordOptionRequestAndFalseAutoSelect_success() {
-        launchTestActivity { activity: Activity ->
-            val actualResponse = getInstance(activity)
+        val activityScenario = ActivityScenario.launch(
+            TestCredentialsActivity::class.java
+        )
+        activityScenario.onActivity { activity: TestCredentialsActivity? ->
+            val actualResponse = getInstance(activity!!)
                 .convertRequestToPlayServices(
                     GetCredentialRequest(
                         listOf(
@@ -81,8 +58,11 @@
 
     @Test
     fun convertRequestToPlayServices_setPasswordOptionRequestAndTrueAutoSelect_success() {
-        launchTestActivity { activity: Activity ->
-            val actualResponse = getInstance(activity)
+        val activityScenario = ActivityScenario.launch(
+            TestCredentialsActivity::class.java
+        )
+        activityScenario.onActivity { activity: TestCredentialsActivity? ->
+            val actualResponse = getInstance(activity!!)
                 .convertRequestToPlayServices(
                     GetCredentialRequest(
                         listOf(
@@ -99,6 +79,10 @@
 
     @Test
     fun convertRequestToPlayServices_setGoogleIdOptionRequest_success() {
+        val activityScenario = ActivityScenario.launch(
+            TestCredentialsActivity::class.java
+        )
+
         val option = GetGoogleIdOption.Builder()
             .setServerClientId("server_client_id")
             .setNonce("nonce")
@@ -108,8 +92,8 @@
             .setAutoSelectEnabled(true)
             .build()
 
-        launchTestActivity { activity: Activity ->
-            val actualRequest = getInstance(activity)
+        activityScenario.onActivity { activity: TestCredentialsActivity? ->
+            val actualRequest = getInstance(activity!!)
                 .convertRequestToPlayServices(
                     GetCredentialRequest(
                         listOf(
@@ -136,9 +120,12 @@
 
     @Test
     fun duplicateGetInstance_shouldBeEqual() {
-        launchTestActivity { activity: Activity ->
+        val activityScenario = ActivityScenario.launch(
+            TestCredentialsActivity::class.java
+        )
+        activityScenario.onActivity { activity: TestCredentialsActivity? ->
 
-            val firstInstance = getInstance(activity)
+            val firstInstance = getInstance(activity!!)
             val secondInstance = getInstance(activity)
             assertThat(firstInstance).isEqualTo(secondInstance)
         }
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpassword/CredentialProviderCreatePasswordControllerJavaTest.java b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpassword/CredentialProviderCreatePasswordControllerJavaTest.java
index f0d5a85..00b86ba 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpassword/CredentialProviderCreatePasswordControllerJavaTest.java
+++ b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpassword/CredentialProviderCreatePasswordControllerJavaTest.java
@@ -20,8 +20,6 @@
 
 import static org.junit.Assert.assertThrows;
 
-import android.app.Activity;
-import android.os.Build;
 import android.os.Bundle;
 
 import androidx.credentials.CreateCredentialResponse;
@@ -31,6 +29,7 @@
 import androidx.credentials.playservices.TestUtils;
 import androidx.credentials.playservices.controllers.CreatePassword.CredentialProviderCreatePasswordController;
 import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.google.android.gms.auth.api.identity.SignInPassword;
@@ -39,53 +38,17 @@
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
 
-@RunWith(Parameterized.class)
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 public class CredentialProviderCreatePasswordControllerJavaTest {
 
-    private final boolean mUseFragmentActivity;
-
-    @Parameterized.Parameters
-    public static Object[] data() {
-        return new Object[] {true, false};
-    }
-
-    public CredentialProviderCreatePasswordControllerJavaTest(final boolean useFragmentActivity)
-            throws Throwable {
-        mUseFragmentActivity = useFragmentActivity;
-    }
-
-    interface TestActivityListener {
-        void onActivity(Activity a);
-    }
-
-    private void launchTestActivity(TestActivityListener listener) {
-        if (mUseFragmentActivity && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
-            ActivityScenario<androidx.credentials.playservices.TestCredentialsFragmentActivity>
-                    activityScenario =
-                            ActivityScenario.launch(
-                                    androidx.credentials.playservices
-                                            .TestCredentialsFragmentActivity.class);
-            activityScenario.onActivity(
-                    activity -> {
-                        listener.onActivity((Activity) activity);
-                    });
-        } else {
-            ActivityScenario<TestCredentialsActivity> activityScenario =
-                    ActivityScenario.launch(TestCredentialsActivity.class);
-            activityScenario.onActivity(
-                    activity -> {
-                        listener.onActivity((Activity) activity);
-                    });
-        }
-    }
-
     @Test
     public void convertResponseToCredentialManager_unitInput_success() {
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
         String expectedResponseType = new CreatePasswordResponse().getType();
-        launchTestActivity(
+        activityScenario.onActivity(
                 activity -> {
                     CreateCredentialResponse actualResponse =
                             CredentialProviderCreatePasswordController.getInstance(activity)
@@ -99,9 +62,11 @@
 
     @Test
     public void convertRequestToPlayServices_createPasswordRequest_success() {
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
         String expectedId = "LM";
         String expectedPassword = "SodaButton";
-        launchTestActivity(
+        activityScenario.onActivity(
                 activity -> {
                     SignInPassword actualRequest =
                             CredentialProviderCreatePasswordController.getInstance(activity)
@@ -118,7 +83,7 @@
     public void convertRequestToPlayServices_nullRequest_throws() {
         ActivityScenario<TestCredentialsActivity> activityScenario =
                 ActivityScenario.launch(TestCredentialsActivity.class);
-        launchTestActivity(
+        activityScenario.onActivity(
                 activity -> {
                     assertThrows(
                             "null create password request must throw exception",
@@ -132,7 +97,9 @@
 
     @Test
     public void convertResponseToCredentialManager_nullRequest_throws() {
-        launchTestActivity(
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+        activityScenario.onActivity(
                 activity -> {
                     assertThrows(
                             "null unit response must throw exception",
@@ -145,7 +112,9 @@
 
     @Test
     public void duplicateGetInstance_shouldBeEqual() {
-        launchTestActivity(
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+        activityScenario.onActivity(
                 activity -> {
                     CredentialProviderCreatePasswordController firstInstance =
                             CredentialProviderCreatePasswordController.getInstance(activity);
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpassword/CredentialProviderCreatePasswordControllerTest.kt b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpassword/CredentialProviderCreatePasswordControllerTest.kt
index cc2e365..0710e37 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpassword/CredentialProviderCreatePasswordControllerTest.kt
+++ b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpassword/CredentialProviderCreatePasswordControllerTest.kt
@@ -16,56 +16,31 @@
 
 package androidx.credentials.playservices.createpassword
 
-import android.app.Activity
-import android.os.Build
 import android.os.Bundle
-import androidx.annotation.DoNotInline
 import androidx.credentials.CreatePasswordRequest
 import androidx.credentials.CreatePasswordResponse
 import androidx.credentials.playservices.TestCredentialsActivity
 import androidx.credentials.playservices.TestUtils.Companion.equals
 import androidx.credentials.playservices.controllers.CreatePassword.CredentialProviderCreatePasswordController.Companion.getInstance
 import androidx.test.core.app.ActivityScenario
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
 
-@RunWith(Parameterized::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
-class CredentialProviderCreatePasswordControllerTest(val useFragmentActivity: Boolean) {
-
-    companion object {
-        @JvmStatic
-        @Parameterized.Parameters(name = "{0}")
-        fun initParameters() = listOf(true, false)
-    }
-
-    @DoNotInline
-    private fun launchTestActivity(callback: (activity: Activity) -> Unit) {
-        if (useFragmentActivity && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
-            var activityScenario =
-                            ActivityScenario.launch(
-                                    androidx.credentials.playservices
-                                            .TestCredentialsFragmentActivity::class.java)
-            activityScenario.onActivity { activity: Activity ->
-                callback.invoke(activity)
-            }
-        } else {
-            var activityScenario = ActivityScenario.launch(TestCredentialsActivity::class.java)
-            activityScenario.onActivity { activity: Activity ->
-                callback.invoke(activity)
-            }
-        }
-    }
-
+class CredentialProviderCreatePasswordControllerTest {
     @Test
     fun convertResponseToCredentialManager_unitInput_success() {
+        val activityScenario = ActivityScenario.launch(
+            TestCredentialsActivity::class.java
+        )
         val expectedResponseType = CreatePasswordResponse().type
-        launchTestActivity { activity: Activity ->
+        activityScenario.onActivity { activity: TestCredentialsActivity? ->
 
-            val actualResponse = getInstance(activity)
+            val actualResponse = getInstance(activity!!)
                 .convertResponseToCredentialManager(Unit)
 
             assertThat(actualResponse.type)
@@ -76,11 +51,14 @@
 
     @Test
     fun convertRequestToPlayServices_createPasswordRequest_success() {
+        val activityScenario = ActivityScenario.launch(
+            TestCredentialsActivity::class.java
+        )
         val expectedId = "LM"
         val expectedPassword = "SodaButton"
-        launchTestActivity { activity: Activity ->
+        activityScenario.onActivity { activity: TestCredentialsActivity? ->
 
-            val actualRequest = getInstance(activity)
+            val actualRequest = getInstance(activity!!)
                 .convertRequestToPlayServices(CreatePasswordRequest(
                         expectedId, expectedPassword)).signInPassword
 
@@ -92,9 +70,12 @@
 
     @Test
     fun duplicateGetInstance_shouldBeEqual() {
-        launchTestActivity { activity: Activity ->
+        val activityScenario = ActivityScenario.launch(
+            TestCredentialsActivity::class.java
+        )
+        activityScenario.onActivity { activity: TestCredentialsActivity? ->
 
-            val firstInstance = getInstance(activity)
+            val firstInstance = getInstance(activity!!)
             val secondInstance = getInstance(activity)
             assertThat(firstInstance).isEqualTo(secondInstance)
         }
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpublickeycredential/CreatePublicKeyCredentialControllerTestUtils.kt b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpublickeycredential/CreatePublicKeyCredentialControllerTestUtils.kt
index b4286c2..de8ef2b 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpublickeycredential/CreatePublicKeyCredentialControllerTestUtils.kt
+++ b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpublickeycredential/CreatePublicKeyCredentialControllerTestUtils.kt
@@ -185,7 +185,6 @@
             configureDescriptors(options, json)
             configureSelectionCriteriaAndAttestation(options, json)
 
-            // TODO("Handle extensions in this testing parsing")
             return json
         }
 
@@ -208,8 +207,6 @@
                     authSelect.put("requireResidentKey", requireResidentKey)
                 }
                 authSelect.put("userVerification", "preferred")
-                // TODO("Since fido impl accepts this input, but does not return it, adding)
-                // TODO(it directly for test comparison. When available, pull from impl object.")
                 json.put("authenticatorSelection", authSelect)
             }
             val attestation = options.attestationConveyancePreferenceAsString
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpublickeycredential/CredentialProviderCreatePublicKeyCredentialControllerJavaTest.java b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpublickeycredential/CredentialProviderCreatePublicKeyCredentialControllerJavaTest.java
index 5e92ef8..a814d5f 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpublickeycredential/CredentialProviderCreatePublicKeyCredentialControllerJavaTest.java
+++ b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpublickeycredential/CredentialProviderCreatePublicKeyCredentialControllerJavaTest.java
@@ -16,6 +16,7 @@
 
 package androidx.credentials.playservices.createpublickeycredential;
 
+
 import static androidx.credentials.playservices.createkeycredential.CreatePublicKeyCredentialControllerTestUtils.ALL_REQUIRED_AND_OPTIONAL_SIGNATURE;
 import static androidx.credentials.playservices.createkeycredential.CreatePublicKeyCredentialControllerTestUtils.ALL_REQUIRED_FIELDS_SIGNATURE;
 import static androidx.credentials.playservices.createkeycredential.CreatePublicKeyCredentialControllerTestUtils.MAIN_CREATE_JSON_ALL_REQUIRED_AND_OPTIONAL_FIELDS_PRESENT;
@@ -33,14 +34,12 @@
 
 import static org.junit.Assert.assertThrows;
 
-import android.app.Activity;
-import android.os.Build;
-
 import androidx.credentials.CreatePublicKeyCredentialRequest;
 import androidx.credentials.playservices.TestCredentialsActivity;
 import androidx.credentials.playservices.TestUtils;
 import androidx.credentials.playservices.controllers.CreatePublicKeyCredential.CredentialProviderCreatePublicKeyCredentialController;
 import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialCreationOptions;
@@ -49,209 +48,156 @@
 import org.json.JSONObject;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
 
-@RunWith(Parameterized.class)
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 public class CredentialProviderCreatePublicKeyCredentialControllerJavaTest {
-
-    private final boolean mUseFragmentActivity;
-
-    @Parameterized.Parameters
-    public static Object[] data() {
-        return new Object[] {true, false};
-    }
-
-    public CredentialProviderCreatePublicKeyCredentialControllerJavaTest(
-            final boolean useFragmentActivity) throws Throwable {
-        mUseFragmentActivity = useFragmentActivity;
-    }
-
-    interface TestActivityListener {
-        void onActivity(Activity a);
-    }
-
-    private void launchTestActivity(TestActivityListener listener) {
-        if (mUseFragmentActivity && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
-            ActivityScenario<androidx.credentials.playservices.TestCredentialsFragmentActivity>
-                    activityScenario =
-                            ActivityScenario.launch(
-                                    androidx.credentials.playservices
-                                            .TestCredentialsFragmentActivity.class);
-            activityScenario.onActivity(
-                    activity -> {
-                        listener.onActivity((Activity) activity);
-                    });
-        } else {
-            ActivityScenario<TestCredentialsActivity> activityScenario =
-                    ActivityScenario.launch(TestCredentialsActivity.class);
-            activityScenario.onActivity(
-                    activity -> {
-                        listener.onActivity((Activity) activity);
-                    });
-        }
-    }
-
-    private PublicKeyCredentialCreationOptions convertRequestToPlayServices(
-            Activity activity, String type) {
-        CreatePublicKeyCredentialRequest pubKeyRequest = new CreatePublicKeyCredentialRequest(type);
-        return CredentialProviderCreatePublicKeyCredentialController.getInstance(activity)
-                .convertRequestToPlayServices(pubKeyRequest);
-    }
-
     @Test
     public void convertRequestToPlayServices_correctRequiredOnlyRequest_success() {
-        launchTestActivity(
-                activity -> {
-                    try {
-                        JSONObject expectedJson =
-                                new JSONObject(MAIN_CREATE_JSON_ALL_REQUIRED_FIELDS_PRESENT);
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+        activityScenario.onActivity(activity -> {
+            try {
+                JSONObject expectedJson = new JSONObject(
+                        MAIN_CREATE_JSON_ALL_REQUIRED_FIELDS_PRESENT);
 
-                        PublicKeyCredentialCreationOptions actualResponse =
-                                convertRequestToPlayServices(
-                                        activity,
-                                        MAIN_CREATE_JSON_ALL_REQUIRED_FIELDS_PRESENT);
-                        JSONObject actualJson =
-                                createJsonObjectFromPublicKeyCredentialCreationOptions(
-                                        actualResponse);
-                        JSONObject requiredKeys = new JSONObject(ALL_REQUIRED_FIELDS_SIGNATURE);
+                PublicKeyCredentialCreationOptions actualResponse =
+                        CredentialProviderCreatePublicKeyCredentialController.getInstance(activity)
+                                .convertRequestToPlayServices(
+                                        new CreatePublicKeyCredentialRequest(
+                                                MAIN_CREATE_JSON_ALL_REQUIRED_FIELDS_PRESENT));
+                JSONObject actualJson = createJsonObjectFromPublicKeyCredentialCreationOptions(
+                        actualResponse);
+                JSONObject requiredKeys = new JSONObject(ALL_REQUIRED_FIELDS_SIGNATURE);
 
-                        assertThat(
-                                        TestUtils.Companion.isSubsetJson(
-                                                expectedJson, actualJson, requiredKeys))
-                                .isTrue();
-                        // TODO("Add remaining tests in detail after discussing ideal form")
-                    } catch (JSONException e) {
-                        throw new RuntimeException(e);
-                    }
-                });
+                assertThat(TestUtils.Companion.isSubsetJson(expectedJson, actualJson,
+                        requiredKeys)).isTrue();
+                // TODO("Add remaining tests in detail after discussing ideal form")
+            } catch (JSONException e) {
+                throw new RuntimeException(e);
+            }
+        });
     }
 
     @Test
     public void convertRequestToPlayServices_correctRequiredAndOptionalRequest_success() {
-        launchTestActivity(
-                activity -> {
-                    try {
-                        JSONObject expectedJson =
-                                new JSONObject(
-                                        MAIN_CREATE_JSON_ALL_REQUIRED_AND_OPTIONAL_FIELDS_PRESENT);
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+        activityScenario.onActivity(activity -> {
+            try {
+                JSONObject expectedJson = new JSONObject(
+                        MAIN_CREATE_JSON_ALL_REQUIRED_AND_OPTIONAL_FIELDS_PRESENT);
 
-                        PublicKeyCredentialCreationOptions actualResponse =
-                                convertRequestToPlayServices(
-                                        activity,
-                                        MAIN_CREATE_JSON_ALL_REQUIRED_AND_OPTIONAL_FIELDS_PRESENT);
-                        JSONObject actualJson =
-                                createJsonObjectFromPublicKeyCredentialCreationOptions(
-                                        actualResponse);
-                        JSONObject requiredKeys =
-                                new JSONObject(ALL_REQUIRED_AND_OPTIONAL_SIGNATURE);
+                PublicKeyCredentialCreationOptions actualResponse =
+                        CredentialProviderCreatePublicKeyCredentialController.getInstance(activity)
+                                .convertRequestToPlayServices(new CreatePublicKeyCredentialRequest(
+                                        MAIN_CREATE_JSON_ALL_REQUIRED_AND_OPTIONAL_FIELDS_PRESENT));
+                JSONObject actualJson =
+                        createJsonObjectFromPublicKeyCredentialCreationOptions(
+                                actualResponse);
+                JSONObject requiredKeys = new JSONObject(ALL_REQUIRED_AND_OPTIONAL_SIGNATURE);
 
-                        assertThat(
-                                        TestUtils.Companion.isSubsetJson(
-                                                expectedJson, actualJson, requiredKeys))
-                                .isTrue();
-                        // TODO("Add remaining tests in detail after discussing ideal form")
-                    } catch (JSONException e) {
-                        throw new RuntimeException(e);
-                    }
-                });
+                assertThat(TestUtils.Companion.isSubsetJson(expectedJson, actualJson,
+                        requiredKeys)).isTrue();
+                // TODO("Add remaining tests in detail after discussing ideal form")
+            } catch (JSONException e) {
+                throw new RuntimeException(e);
+            }
+        });
     }
-
     @Test
     public void convertRequestToPlayServices_missingRequired_throws() {
-        launchTestActivity(
-                activity -> {
-                    try {
-                        PublicKeyCredentialCreationOptions actualResponse =
-                                convertRequestToPlayServices(
-                                        activity,
-                                        MAIN_CREATE_JSON_ALL_REQUIRED_FIELDS_PRESENT);
-
-                        CreatePublicKeyCredentialRequest pubKeyRequest =
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+        activityScenario.onActivity(activity -> {
+            try {
+                CredentialProviderCreatePublicKeyCredentialController
+                        .getInstance(activity)
+                        .convertRequestToPlayServices(
                                 new CreatePublicKeyCredentialRequest(
-                                        MAIN_CREATE_JSON_ALL_REQUIRED_FIELDS_PRESENT);
-                        CredentialProviderCreatePublicKeyCredentialController.getInstance(activity)
-                                .convertRequestToPlayServices(
-                                        new CreatePublicKeyCredentialRequest(
-                                                MAIN_CREATE_JSON_MISSING_REQUIRED_FIELD));
+                                        MAIN_CREATE_JSON_MISSING_REQUIRED_FIELD));
 
-                        // Should not reach here.
-                        assertWithMessage("Exception should be thrown").that(true).isFalse();
-                    } catch (Exception e) {
-                        assertThat(e.getMessage().contains("No value for id")).isTrue();
-                        assertThat(e.getClass().getName().contains("JSONException")).isTrue();
-                    }
-                });
+                // Should not reach here.
+                assertWithMessage("Exception should be thrown").that(true).isFalse();
+            } catch (Exception e) {
+                assertThat(e.getMessage().contains("No value for id")).isTrue();
+                assertThat(e.getClass().getName().contains("JSONException")).isTrue();
+            }
+        });
     }
 
     @Test
     public void convertRequestToPlayServices_emptyRequired_throws() {
-        launchTestActivity(
-                activity -> {
-                    assertThrows(
-                            "Expected bad required json to throw",
-                            JSONException.class,
-                            () ->
-                                    convertRequestToPlayServices(
-                                            activity,
-                                            MAIN_CREATE_JSON_REQUIRED_FIELD_EMPTY));
-                });
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+        activityScenario.onActivity(activity -> {
+
+            assertThrows("Expected bad required json to throw",
+                    JSONException.class,
+                    () -> CredentialProviderCreatePublicKeyCredentialController
+                            .getInstance(activity).convertRequestToPlayServices(
+                                    new CreatePublicKeyCredentialRequest(
+                                            MAIN_CREATE_JSON_REQUIRED_FIELD_EMPTY)));
+        });
     }
 
     @Test
     public void convertRequestToPlayServices_missingOptionalRequired_throws() {
-        launchTestActivity(
-                activity -> {
-                    assertThrows(
-                            "Expected bad required json to throw",
-                            JSONException.class,
-                            () ->
-                                    convertRequestToPlayServices(
-                                            activity,
-                                            OPTIONAL_FIELD_MISSING_REQUIRED_SUBFIELD));
-                });
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+        activityScenario.onActivity(activity -> {
+
+            assertThrows("Expected bad required json to throw",
+                    JSONException.class,
+                    () -> CredentialProviderCreatePublicKeyCredentialController
+                            .getInstance(activity)
+                            .convertRequestToPlayServices(
+                                    new CreatePublicKeyCredentialRequest(
+                                            OPTIONAL_FIELD_MISSING_REQUIRED_SUBFIELD)));
+        });
     }
 
     @Test
     public void convertRequestToPlayServices_emptyOptionalRequired_throws() {
-        launchTestActivity(
-                activity -> {
-                    assertThrows(
-                            "Expected bad required json to throw",
-                            JSONException.class,
-                            () ->
-                                    convertRequestToPlayServices(
-                                            activity,
-                                            OPTIONAL_FIELD_WITH_EMPTY_REQUIRED_SUBFIELD));
-                });
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+        activityScenario.onActivity(activity -> {
+
+            assertThrows("Expected bad required json to throw",
+                    JSONException.class,
+                    () -> CredentialProviderCreatePublicKeyCredentialController
+                            .getInstance(activity)
+                            .convertRequestToPlayServices(
+                                    new CreatePublicKeyCredentialRequest(
+                                            OPTIONAL_FIELD_WITH_EMPTY_REQUIRED_SUBFIELD)));
+        });
     }
 
     @Test
     public void convertRequestToPlayServices_missingOptionalNotRequired_success() {
-        launchTestActivity(
-                activity -> {
-                    try {
-                        JSONObject expectedJson =
-                                new JSONObject(OPTIONAL_FIELD_MISSING_OPTIONAL_SUBFIELD);
+        ActivityScenario<TestCredentialsActivity> activityScenario =
+                ActivityScenario.launch(TestCredentialsActivity.class);
+        activityScenario.onActivity(activity -> {
+            try {
+                JSONObject expectedJson = new JSONObject(
+                        OPTIONAL_FIELD_MISSING_OPTIONAL_SUBFIELD);
 
-                        PublicKeyCredentialCreationOptions actualResponse =
-                                convertRequestToPlayServices(
-                                        activity,
-                                        OPTIONAL_FIELD_MISSING_OPTIONAL_SUBFIELD);
-                        JSONObject actualJson =
-                                createJsonObjectFromPublicKeyCredentialCreationOptions(
-                                        actualResponse);
-                        JSONObject requiredKeys =
-                                new JSONObject(OPTIONAL_FIELD_MISSING_OPTIONAL_SUBFIELD_SIGNATURE);
+                PublicKeyCredentialCreationOptions actualResponse =
+                        CredentialProviderCreatePublicKeyCredentialController.getInstance(activity)
+                                .convertRequestToPlayServices(
+                                        new CreatePublicKeyCredentialRequest(
+                                                OPTIONAL_FIELD_MISSING_OPTIONAL_SUBFIELD));
+                JSONObject actualJson = createJsonObjectFromPublicKeyCredentialCreationOptions(
+                        actualResponse);
+                JSONObject requiredKeys = new
+                        JSONObject(OPTIONAL_FIELD_MISSING_OPTIONAL_SUBFIELD_SIGNATURE);
 
-                        assertThat(
-                                        TestUtils.Companion.isSubsetJson(
-                                                expectedJson, actualJson, requiredKeys))
-                                .isTrue();
-                        // TODO("Add remaining tests in detail after discussing ideal form")
-                    } catch (JSONException e) {
-                        throw new RuntimeException(e);
-                    }
-                });
+                assertThat(TestUtils.Companion.isSubsetJson(expectedJson, actualJson,
+                        requiredKeys)).isTrue();
+                // TODO("Add remaining tests in detail after discussing ideal form")
+            } catch (JSONException e) {
+                throw new RuntimeException(e);
+            }
+        });
     }
 }
diff --git a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpublickeycredential/CredentialProviderCreatePublicKeyCredentialControllerTest.kt b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpublickeycredential/CredentialProviderCreatePublicKeyCredentialControllerTest.kt
index 34ca8a8..3a9ae60 100644
--- a/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpublickeycredential/CredentialProviderCreatePublicKeyCredentialControllerTest.kt
+++ b/credentials/credentials-play-services-auth/src/androidTest/java/androidx/credentials/playservices/createpublickeycredential/CredentialProviderCreatePublicKeyCredentialControllerTest.kt
@@ -16,9 +16,6 @@
 
 package androidx.credentials.playservices.createpublickeycredential
 
-import android.app.Activity
-import android.os.Build
-import androidx.annotation.DoNotInline
 import androidx.credentials.CreatePublicKeyCredentialRequest
 import androidx.credentials.playservices.TestCredentialsActivity
 import androidx.credentials.playservices.TestUtils.Companion.isSubsetJson
@@ -35,6 +32,7 @@
 import androidx.credentials.playservices.createkeycredential.CreatePublicKeyCredentialControllerTestUtils.Companion.OPTIONAL_FIELD_WITH_EMPTY_REQUIRED_SUBFIELD
 import androidx.credentials.playservices.createkeycredential.CreatePublicKeyCredentialControllerTestUtils.Companion.createJsonObjectFromPublicKeyCredentialCreationOptions
 import androidx.test.core.app.ActivityScenario
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
 import org.json.JSONException
@@ -43,45 +41,22 @@
 import org.junit.Test
 import org.junit.function.ThrowingRunnable
 import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
 
-@RunWith(Parameterized::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
-class CredentialProviderCreatePublicKeyCredentialControllerTest(val useFragmentActivity: Boolean) {
-
-    companion object {
-        @JvmStatic
-        @Parameterized.Parameters(name = "{0}")
-        fun initParameters() = listOf(true, false)
-    }
-
-    @DoNotInline
-    private fun launchTestActivity(callback: (activity: Activity) -> Unit) {
-        if (useFragmentActivity && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
-            var activityScenario =
-                            ActivityScenario.launch(
-                                    androidx.credentials.playservices
-                                            .TestCredentialsFragmentActivity::class.java)
-            activityScenario.onActivity { activity: Activity ->
-                callback.invoke(activity)
-            }
-        } else {
-            var activityScenario = ActivityScenario.launch(TestCredentialsActivity::class.java)
-            activityScenario.onActivity { activity: Activity ->
-                callback.invoke(activity)
-            }
-        }
-    }
-
+class CredentialProviderCreatePublicKeyCredentialControllerTest {
     @Test
     fun convertRequestToPlayServices_correctRequiredOnlyRequest_success() {
-        launchTestActivity { activity: Activity ->
+        val activityScenario = ActivityScenario.launch(
+            TestCredentialsActivity::class.java
+        )
+        activityScenario.onActivity { activity: TestCredentialsActivity? ->
             try {
                 val expectedJson = JSONObject(MAIN_CREATE_JSON_ALL_REQUIRED_FIELDS_PRESENT)
 
-                val actualResponse = getInstance(activity).convertRequestToPlayServices(
-                            CreatePublicKeyCredentialRequest(
-                                MAIN_CREATE_JSON_ALL_REQUIRED_FIELDS_PRESENT))
+                val actualResponse = getInstance(activity!!).convertRequestToPlayServices(
+                    CreatePublicKeyCredentialRequest(
+                        MAIN_CREATE_JSON_ALL_REQUIRED_FIELDS_PRESENT))
                 val actualJson =
                     createJsonObjectFromPublicKeyCredentialCreationOptions(actualResponse)
                 val requiredKeys =
@@ -97,14 +72,17 @@
 
     @Test
     fun convertRequestToPlayServices_correctRequiredAndOptionalRequest_success() {
-        launchTestActivity { activity: Activity ->
+        val activityScenario = ActivityScenario.launch(
+            TestCredentialsActivity::class.java
+        )
+        activityScenario.onActivity { activity: TestCredentialsActivity? ->
             try {
                 val expectedJson = JSONObject(
                     MAIN_CREATE_JSON_ALL_REQUIRED_AND_OPTIONAL_FIELDS_PRESENT)
 
-                val actualResponse = getInstance(activity)
-                        .convertRequestToPlayServices(CreatePublicKeyCredentialRequest(
-                            MAIN_CREATE_JSON_ALL_REQUIRED_AND_OPTIONAL_FIELDS_PRESENT))
+                val actualResponse = getInstance(activity!!)
+                    .convertRequestToPlayServices(CreatePublicKeyCredentialRequest(
+                        MAIN_CREATE_JSON_ALL_REQUIRED_AND_OPTIONAL_FIELDS_PRESENT))
                 val actualJson =
                     createJsonObjectFromPublicKeyCredentialCreationOptions(actualResponse)
                 val requiredKeys =
@@ -120,71 +98,86 @@
 
     @Test
     fun convertRequestToPlayServices_missingRequired_throws() {
-        launchTestActivity { activity: Activity ->
+        val activityScenario = ActivityScenario.launch(
+            TestCredentialsActivity::class.java
+        )
+        activityScenario.onActivity { activity: TestCredentialsActivity? ->
 
             Assert.assertThrows("Expected bad required json to throw",
                 JSONException::class.java,
                 ThrowingRunnable {
                     getInstance(
-                        activity
+                        activity!!
                     ).convertRequestToPlayServices(
-                            CreatePublicKeyCredentialRequest(
-                                MAIN_CREATE_JSON_MISSING_REQUIRED_FIELD
-                            )) })
+                        CreatePublicKeyCredentialRequest(
+                            MAIN_CREATE_JSON_MISSING_REQUIRED_FIELD
+                        )) })
         }
     }
 
     @Test
     fun convertRequestToPlayServices_emptyRequired_throws() {
-        launchTestActivity { activity: Activity ->
+        val activityScenario = ActivityScenario.launch(
+            TestCredentialsActivity::class.java
+        )
+        activityScenario.onActivity { activity: TestCredentialsActivity? ->
 
             Assert.assertThrows("Expected bad required json to throw",
                 JSONException::class.java,
-                ThrowingRunnable { getInstance(activity
-                    ).convertRequestToPlayServices(CreatePublicKeyCredentialRequest(
-                                MAIN_CREATE_JSON_REQUIRED_FIELD_EMPTY)) })
+                ThrowingRunnable { getInstance(activity!!
+                ).convertRequestToPlayServices(CreatePublicKeyCredentialRequest(
+                    MAIN_CREATE_JSON_REQUIRED_FIELD_EMPTY)) })
         }
     }
     @Test
     fun convertRequestToPlayServices_missingOptionalRequired_throws() {
-        launchTestActivity { activity: Activity ->
+        val activityScenario = ActivityScenario.launch(
+            TestCredentialsActivity::class.java
+        )
+        activityScenario.onActivity { activity: TestCredentialsActivity? ->
 
             Assert.assertThrows("Expected bad required json to throw",
                 JSONException::class.java,
                 ThrowingRunnable {
                     getInstance(
-                        activity
+                        activity!!
                     ).convertRequestToPlayServices(
-                            CreatePublicKeyCredentialRequest(
-                                OPTIONAL_FIELD_MISSING_REQUIRED_SUBFIELD)) })
+                        CreatePublicKeyCredentialRequest(
+                            OPTIONAL_FIELD_MISSING_REQUIRED_SUBFIELD)) })
         }
     }
 
     @Test
     fun convertRequestToPlayServices_emptyOptionalRequired_throws() {
-        launchTestActivity { activity: Activity ->
+        val activityScenario = ActivityScenario.launch(
+            TestCredentialsActivity::class.java
+        )
+        activityScenario.onActivity { activity: TestCredentialsActivity? ->
 
             Assert.assertThrows("Expected bad required json to throw",
                 JSONException::class.java,
-                ThrowingRunnable { getInstance(activity).convertRequestToPlayServices(
-                            CreatePublicKeyCredentialRequest(
-                                OPTIONAL_FIELD_WITH_EMPTY_REQUIRED_SUBFIELD)) })
+                ThrowingRunnable { getInstance(activity!!).convertRequestToPlayServices(
+                    CreatePublicKeyCredentialRequest(
+                        OPTIONAL_FIELD_WITH_EMPTY_REQUIRED_SUBFIELD)) })
         }
     }
 
     @Test
     fun convertRequestToPlayServices_missingOptionalNotRequired_success() {
-        launchTestActivity { activity: Activity ->
+        val activityScenario = ActivityScenario.launch(
+            TestCredentialsActivity::class.java
+        )
+        activityScenario.onActivity { activity: TestCredentialsActivity? ->
             try {
                 val expectedJson = JSONObject(OPTIONAL_FIELD_MISSING_OPTIONAL_SUBFIELD)
 
                 val actualResponse =
-                    getInstance(activity)
+                    getInstance(activity!!)
                         .convertRequestToPlayServices(
                             CreatePublicKeyCredentialRequest(
                                 OPTIONAL_FIELD_MISSING_OPTIONAL_SUBFIELD))
                 val actualJson = createJsonObjectFromPublicKeyCredentialCreationOptions(
-                        actualResponse)
+                    actualResponse)
                 val requiredKeys =
                     JSONObject(OPTIONAL_FIELD_MISSING_OPTIONAL_SUBFIELD_SIGNATURE)
 
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderFragment.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderFragment.kt
deleted file mode 100644
index 2d7f1e9..0000000
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderFragment.kt
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.credentials.playservices
-
-import android.content.Intent
-import android.os.Bundle
-import android.os.ResultReceiver
-import androidx.annotation.RestrictTo
-import androidx.credentials.playservices.controllers.CredentialProviderBaseController
-import androidx.fragment.app.Fragment
-
-/** A fragment used if we are passed a fragment activity. */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-@Suppress("Deprecation")
-open class CredentialProviderFragment : Fragment() {
-
-  private var resultReceiver: ResultReceiver? = null
-  private var mWaitingForActivityResult = false
-
-  override fun onCreate(savedInstanceState: Bundle?) {
-    super.onCreate(savedInstanceState)
-
-    resultReceiver = getResultReceiver()
-    if (resultReceiver == null) {
-      return
-    }
-
-    restoreState(savedInstanceState)
-    if (mWaitingForActivityResult) {
-      return // Past call still active
-    }
-  }
-
-  private fun getResultReceiver(): ResultReceiver? {
-    if (getArguments() == null) {
-      return null
-    }
-
-    return getArguments()!!.getParcelable(CredentialProviderBaseController.RESULT_RECEIVER_TAG)
-      as? ResultReceiver
-  }
-
-  private fun restoreState(savedInstanceState: Bundle?) {
-    if (savedInstanceState != null) {
-      mWaitingForActivityResult = savedInstanceState.getBoolean(KEY_AWAITING_RESULT, false)
-    }
-  }
-
-  override fun onSaveInstanceState(outState: Bundle) {
-    outState.putBoolean(KEY_AWAITING_RESULT, mWaitingForActivityResult)
-    super.onSaveInstanceState(outState)
-  }
-
-  override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
-    super.onActivityResult(requestCode, resultCode, data)
-    val bundle = Bundle()
-    bundle.putBoolean(CredentialProviderBaseController.FAILURE_RESPONSE_TAG, false)
-    bundle.putInt(CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG, requestCode)
-    bundle.putParcelable(CredentialProviderBaseController.RESULT_DATA_TAG, data)
-    resultReceiver?.send(resultCode, bundle)
-    mWaitingForActivityResult = false
-  }
-
-  companion object {
-    private const val TAG = "CredentialProviderFragment"
-    private const val KEY_AWAITING_RESULT = "androidx.credentials.playservices.AWAITING_RESULT"
-
-    fun createFrom(resultReceiver: ResultReceiver): CredentialProviderFragment {
-      val f = CredentialProviderFragment()
-
-      // Supply index input as an argument.
-      val args = Bundle()
-      args.putParcelable(CredentialProviderBaseController.RESULT_RECEIVER_TAG, resultReceiver)
-      f.setArguments(args)
-
-      return f
-    }
-  }
-}
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderMetadataHolder.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderMetadataHolder.kt
index 6069c27..c503cd5 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderMetadataHolder.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderMetadataHolder.kt
@@ -18,6 +18,7 @@
 
 import android.app.Service
 import android.content.Intent
+import android.os.Binder
 import android.os.IBinder
 import androidx.annotation.RestrictTo
 
@@ -27,7 +28,20 @@
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY)
 class CredentialProviderMetadataHolder : Service() {
-    override fun onBind(p0: Intent?): IBinder? {
-        TODO("Not yet implemented")
+    // Binder given to clients.
+    private val binder = LocalBinder()
+
+    /**
+     * Class used for the client Binder. Because we know this service always
+     * runs in the same process as its clients, we don't need to deal with IPC.
+     */
+    inner class LocalBinder : Binder() {
+        // Return this instance of CredentialProviderMetadataHolder so clients
+        // can call public methods.
+        fun getService(): CredentialProviderMetadataHolder = this@CredentialProviderMetadataHolder
+    }
+
+    override fun onBind(intent: Intent): IBinder {
+        return binder
     }
 }
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/GmsCoreUtils.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/GmsCoreUtils.kt
deleted file mode 100644
index 112599d..0000000
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/GmsCoreUtils.kt
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * 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.credentials.playservices
-
-import android.app.Activity
-import android.app.PendingIntent
-import android.content.IntentSender
-import android.os.Bundle
-import android.os.ResultReceiver
-import android.util.Log
-import androidx.annotation.RestrictTo
-import androidx.credentials.playservices.controllers.CredentialProviderBaseController
-import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.CREATE_INTERRUPTED
-import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.CREATE_UNKNOWN
-import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.GET_INTERRUPTED
-import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.GET_NO_CREDENTIALS
-import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.GET_UNKNOWN
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.FragmentActivity
-import com.google.android.gms.auth.api.identity.BeginSignInRequest
-import com.google.android.gms.auth.api.identity.CredentialSavingClient
-import com.google.android.gms.auth.api.identity.SavePasswordRequest
-import com.google.android.gms.auth.api.identity.SignInClient
-import com.google.android.gms.common.api.ApiException
-import com.google.android.gms.fido.fido2.Fido2ApiClient
-import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialCreationOptions
-
-/** A util class for interacting with GmsCore. */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-@Suppress("Deprecation", "ForbiddenSuperClass")
-open class GmsCoreUtils {
-
-  class GmsCoreUtilsResult(var waitingForActivityResult: Boolean, var hasFinished: Boolean)
-
-  internal companion object {
-    private const val TAG = "GmsCoreUtils"
-
-    const val DEFAULT_REQUEST_CODE = 1
-
-    class FragmentCreationException() : Exception("Failed to create exception")
-
-    fun handleCreatePublicKeyCredential(
-      apiClient: Fido2ApiClient,
-      resultReceiver: ResultReceiver,
-      fidoRegistrationRequest: PublicKeyCredentialCreationOptions?,
-      requestCode: Int,
-      activity: Activity
-    ): GmsCoreUtilsResult {
-      var waitingForActivityResult = false
-      var hasActivityFinished = false
-      var fragment = setupFragmentActivity(activity, resultReceiver)
-
-      fidoRegistrationRequest?.let {
-        apiClient
-          .getRegisterPendingIntent(fidoRegistrationRequest)
-          .addOnSuccessListener { result: PendingIntent ->
-            try {
-              startIntentSender(
-                activity,
-                result.intentSender,
-                requestCode,
-                fragment,
-              )
-            } catch (e: IntentSender.SendIntentException) {
-              setupFailure(
-                resultReceiver,
-                CREATE_UNKNOWN,
-                "During public key credential, found IntentSender " +
-                  "failure on public key creation: ${e.message}"
-              )
-            }
-          }
-          .addOnFailureListener { e: Exception ->
-            var errName: String = CREATE_UNKNOWN
-            if (e is ApiException && e.statusCode in CredentialProviderBaseController.retryables) {
-              errName = CREATE_INTERRUPTED
-            }
-            setupFailure(
-              resultReceiver,
-              errName,
-              "During create public key credential, fido registration " + "failure: ${e.message}"
-            )
-          }
-      }
-        ?: run {
-          Log.w(
-            TAG,
-            "During create public key credential, request is null, so nothing to " +
-              "launch for public key credentials"
-          )
-          hasActivityFinished = true
-        }
-      return GmsCoreUtilsResult(waitingForActivityResult, hasActivityFinished)
-    }
-
-    fun handleBeginSignIn(
-      apiClient: SignInClient,
-      resultReceiver: ResultReceiver,
-      params: BeginSignInRequest?,
-      requestCode: Int,
-      activity: Activity
-    ): GmsCoreUtilsResult {
-      var waitingForActivityResult = false
-      var hasFinished = false
-      var fragment = setupFragmentActivity(activity, resultReceiver)
-
-      params?.let {
-        apiClient
-          .beginSignIn(params)
-          .addOnSuccessListener {
-            try {
-              waitingForActivityResult = true
-              startIntentSender(
-                activity,
-                it.pendingIntent.intentSender,
-                requestCode,
-                fragment,
-              )
-            } catch (e: IntentSender.SendIntentException) {
-              setupFailure(
-                resultReceiver,
-                GET_UNKNOWN,
-                "During begin sign in, one tap ui intent sender " + "failure: ${e.message}"
-              )
-            }
-          }
-          .addOnFailureListener { e: Exception ->
-            var errName: String = GET_NO_CREDENTIALS
-            if (e is ApiException && e.statusCode in CredentialProviderBaseController.retryables) {
-              errName = GET_INTERRUPTED
-            }
-            setupFailure(
-              resultReceiver,
-              errName,
-              "During begin sign in, failure response from one tap: ${e.message}"
-            )
-          }
-      }
-        ?: run {
-          Log.i(
-            TAG,
-            "During begin sign in, params is null, nothing to launch for " + "begin sign in"
-          )
-          hasFinished = true
-        }
-      return GmsCoreUtilsResult(waitingForActivityResult, hasFinished)
-    }
-
-    fun handleCreatePassword(
-      apiClient: CredentialSavingClient,
-      resultReceiver: ResultReceiver,
-      params: SavePasswordRequest?,
-      requestCode: Int,
-      activity: Activity
-    ): GmsCoreUtilsResult {
-      var waitingForActivityResult = false
-      var hasFinished = false
-      var fragment = setupFragmentActivity(activity, resultReceiver)
-
-      params?.let {
-        apiClient
-          .savePassword(params)
-          .addOnSuccessListener {
-            try {
-              waitingForActivityResult = true
-              startIntentSender(
-                activity,
-                it.pendingIntent.intentSender,
-                requestCode,
-                fragment,
-              )
-            } catch (e: IntentSender.SendIntentException) {
-              setupFailure(
-                resultReceiver,
-                CREATE_UNKNOWN,
-                "During save password, found UI intent sender " + "failure: ${e.message}"
-              )
-            }
-          }
-          .addOnFailureListener { e: Exception ->
-            var errName: String = CREATE_UNKNOWN
-            if (e is ApiException && e.statusCode in CredentialProviderBaseController.retryables) {
-              errName = CREATE_INTERRUPTED
-            }
-            setupFailure(
-              resultReceiver,
-              errName,
-              "During save password, found " + "password failure response from one tap ${e.message}"
-            )
-          }
-      }
-        ?: run {
-          Log.i(
-            TAG,
-            "During save password, params is null, nothing to launch for create" + " password"
-          )
-          hasFinished = true
-        }
-      return GmsCoreUtilsResult(waitingForActivityResult, hasFinished)
-    }
-
-    private fun setupFragmentActivity(
-      activity: Activity,
-      resultReceiver: ResultReceiver
-    ): Fragment? {
-      if (activity is FragmentActivity) {
-        val fragment = CredentialProviderFragment.createFrom(resultReceiver)
-        val manager = activity.getSupportFragmentManager()
-        manager.beginTransaction().add(fragment, "credman").commit()
-
-        if (!fragment.isAdded()) {
-          throw FragmentCreationException()
-        }
-
-        return fragment
-      }
-
-      return null
-    }
-
-    private fun startIntentSender(
-      activity: Activity,
-      intentSender: IntentSender,
-      requestCode: Int,
-      fragment: Fragment?,
-    ) {
-      if (fragment != null && fragment.isAdded() && activity is FragmentActivity) {
-        activity.startIntentSenderFromFragment(
-          fragment,
-          intentSender,
-          requestCode,
-          null,
-          0,
-          0,
-          0,
-          null,
-        )
-        return
-      }
-
-      activity.startIntentSenderForResult(intentSender, requestCode, null, 0, 0, 0, null)
-    }
-
-    private fun setupFailure(resultReceiver: ResultReceiver, errName: String, errMsg: String) {
-      val bundle = Bundle()
-      bundle.putBoolean(CredentialProviderBaseController.FAILURE_RESPONSE_TAG, true)
-      bundle.putString(CredentialProviderBaseController.EXCEPTION_TYPE_TAG, errName)
-      bundle.putString(CredentialProviderBaseController.EXCEPTION_MESSAGE_TAG, errMsg)
-      resultReceiver.send(Integer.MAX_VALUE, bundle)
-    }
-  }
-}
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/HiddenActivity.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/HiddenActivity.kt
index 8586305..bde5471 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/HiddenActivity.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/HiddenActivity.kt
@@ -17,150 +17,230 @@
 package androidx.credentials.playservices
 
 import android.app.Activity
+import android.app.PendingIntent
 import android.content.Intent
+import android.content.IntentSender
 import android.os.Bundle
 import android.os.ResultReceiver
 import android.util.Log
 import androidx.annotation.RestrictTo
 import androidx.credentials.playservices.controllers.CredentialProviderBaseController
+import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.CREATE_INTERRUPTED
+import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.CREATE_UNKNOWN
+import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.GET_INTERRUPTED
+import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.GET_NO_CREDENTIALS
+import androidx.credentials.playservices.controllers.CredentialProviderBaseController.Companion.GET_UNKNOWN
 import com.google.android.gms.auth.api.identity.BeginSignInRequest
 import com.google.android.gms.auth.api.identity.Identity
 import com.google.android.gms.auth.api.identity.SavePasswordRequest
+import com.google.android.gms.common.api.ApiException
 import com.google.android.gms.fido.Fido
 import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialCreationOptions
 
-/** An activity used to ensure all required API versions work as intended. */
+/**
+ * An activity used to ensure all required API versions work as intended.
+ */
 @RestrictTo(RestrictTo.Scope.LIBRARY)
 @Suppress("Deprecation", "ForbiddenSuperClass")
 open class HiddenActivity : Activity() {
 
-  private var resultReceiver: ResultReceiver? = null
-  private var mWaitingForActivityResult = false
+    private var resultReceiver: ResultReceiver? = null
+    private var mWaitingForActivityResult = false
 
-  override fun onCreate(savedInstanceState: Bundle?) {
-    super.onCreate(savedInstanceState)
-    overridePendingTransition(0, 0)
-    val type: String? = intent.getStringExtra(CredentialProviderBaseController.TYPE_TAG)
-    resultReceiver = intent.getParcelableExtra(CredentialProviderBaseController.RESULT_RECEIVER_TAG)
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        overridePendingTransition(0, 0)
+        val type: String? = intent.getStringExtra(CredentialProviderBaseController.TYPE_TAG)
+        resultReceiver = intent.getParcelableExtra(
+            CredentialProviderBaseController.RESULT_RECEIVER_TAG)
 
-    if (resultReceiver == null) {
-      finish()
+        if (resultReceiver == null) {
+            finish()
+        }
+
+        restoreState(savedInstanceState)
+        if (mWaitingForActivityResult) {
+            return; // Past call still active
+        }
+
+        when (type) {
+            CredentialProviderBaseController.BEGIN_SIGN_IN_TAG -> {
+                handleBeginSignIn()
+            }
+            CredentialProviderBaseController.CREATE_PASSWORD_TAG -> {
+                handleCreatePassword()
+            }
+            CredentialProviderBaseController.CREATE_PUBLIC_KEY_CREDENTIAL_TAG -> {
+                handleCreatePublicKeyCredential()
+            } else -> {
+                Log.w(TAG, "Activity handed an unsupported type")
+                finish()
+            }
+        }
     }
 
-    restoreState(savedInstanceState)
-    if (mWaitingForActivityResult) {
-      return
-      // Past call still active
+    private fun restoreState(savedInstanceState: Bundle?) {
+        if (savedInstanceState != null) {
+            mWaitingForActivityResult = savedInstanceState.getBoolean(KEY_AWAITING_RESULT, false)
+        }
     }
 
-    when (type) {
-      CredentialProviderBaseController.BEGIN_SIGN_IN_TAG -> {
-        handleBeginSignIn(intent, resultReceiver)
-      }
-      CredentialProviderBaseController.CREATE_PASSWORD_TAG -> {
-        handleCreatePassword(intent, resultReceiver)
-      }
-      CredentialProviderBaseController.CREATE_PUBLIC_KEY_CREDENTIAL_TAG -> {
-        handleCreatePublicKeyCredential(intent, resultReceiver)
-      }
-      else -> {
-        Log.w(TAG, "Activity handed an unsupported type")
+    private fun handleCreatePublicKeyCredential() {
+        val fidoRegistrationRequest: PublicKeyCredentialCreationOptions? = intent
+            .getParcelableExtra(CredentialProviderBaseController.REQUEST_TAG)
+        val requestCode: Int = intent.getIntExtra(
+            CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG,
+                DEFAULT_VALUE)
+        fidoRegistrationRequest?.let {
+            Fido.getFido2ApiClient(this)
+                .getRegisterPendingIntent(fidoRegistrationRequest)
+                .addOnSuccessListener { result: PendingIntent ->
+                    try {
+                        mWaitingForActivityResult = true
+                        startIntentSenderForResult(
+                            result.intentSender,
+                            requestCode,
+                            null, /* fillInIntent= */
+                            0, /* flagsMask= */
+                            0, /* flagsValue= */
+                            0, /* extraFlags= */
+                            null /* options= */
+                        )
+                    } catch (e: IntentSender.SendIntentException) {
+                        setupFailure(resultReceiver!!,
+                            CREATE_UNKNOWN,
+                            "During public key credential, found IntentSender " +
+                                "failure on public key creation: ${e.message}")
+                    }
+                }
+                .addOnFailureListener { e: Exception ->
+                    var errName: String = CREATE_UNKNOWN
+                    if (e is ApiException && e.statusCode in
+                        CredentialProviderBaseController.retryables) {
+                        errName = CREATE_INTERRUPTED
+                    }
+                    setupFailure(resultReceiver!!, errName,
+                        "During create public key credential, fido registration " +
+                            "failure: ${e.message}")
+                }
+        } ?: run {
+            Log.w(TAG, "During create public key credential, request is null, so nothing to " +
+                "launch for public key credentials")
+            finish()
+        }
+    }
+
+    private fun setupFailure(resultReceiver: ResultReceiver, errName: String, errMsg: String) {
+        val bundle = Bundle()
+        bundle.putBoolean(CredentialProviderBaseController.FAILURE_RESPONSE_TAG, true)
+        bundle.putString(CredentialProviderBaseController.EXCEPTION_TYPE_TAG, errName)
+        bundle.putString(CredentialProviderBaseController.EXCEPTION_MESSAGE_TAG, errMsg)
+        resultReceiver.send(Integer.MAX_VALUE, bundle)
         finish()
-      }
     }
-  }
 
-  private fun restoreState(savedInstanceState: Bundle?) {
-    if (savedInstanceState != null) {
-      mWaitingForActivityResult = savedInstanceState.getBoolean(KEY_AWAITING_RESULT, false)
+    override fun onSaveInstanceState(outState: Bundle) {
+        outState.putBoolean(KEY_AWAITING_RESULT, mWaitingForActivityResult)
+        super.onSaveInstanceState(outState)
     }
-  }
 
-  private fun handleBeginSignIn(intent: Intent, resultReceiver: ResultReceiver?) {
-    val params: BeginSignInRequest? =
-      intent.getParcelableExtra(CredentialProviderBaseController.REQUEST_TAG)
-    val requestCode: Int =
-      intent.getIntExtra(CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG, DEFAULT_VALUE)
+    private fun handleBeginSignIn() {
+        val params: BeginSignInRequest? = intent.getParcelableExtra(
+            CredentialProviderBaseController.REQUEST_TAG)
+        val requestCode: Int = intent.getIntExtra(
+            CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG,
+            DEFAULT_VALUE)
+        params?.let {
+            Identity.getSignInClient(this).beginSignIn(params).addOnSuccessListener {
+                try {
+                    mWaitingForActivityResult = true
+                    startIntentSenderForResult(
+                        it.pendingIntent.intentSender,
+                        requestCode,
+                        null,
+                        0,
+                        0,
+                        0,
+                        null
+                    )
+                } catch (e: IntentSender.SendIntentException) {
+                    setupFailure(resultReceiver!!,
+                        GET_UNKNOWN,
+                            "During begin sign in, one tap ui intent sender " +
+                                "failure: ${e.message}")
+                }
+            }.addOnFailureListener { e: Exception ->
+                var errName: String = GET_NO_CREDENTIALS
+                if (e is ApiException && e.statusCode in
+                    CredentialProviderBaseController.retryables) {
+                    errName = GET_INTERRUPTED
+                }
+                setupFailure(resultReceiver!!, errName,
+                    "During begin sign in, failure response from one tap: ${e.message}")
+            }
+        } ?: run {
+            Log.i(TAG, "During begin sign in, params is null, nothing to launch for " +
+                "begin sign in")
+            finish()
+        }
+    }
 
-    if (intent.hasExtra(CredentialProviderBaseController.REQUEST_TAG) && resultReceiver != null) {
-      val result =
-        GmsCoreUtils.handleBeginSignIn(
-          Identity.getSignInClient(this),
-          resultReceiver,
-          params!!,
-          requestCode,
-          this
-        )
-      mWaitingForActivityResult = result.waitingForActivityResult
-      if (result.hasFinished) {
+    private fun handleCreatePassword() {
+        val params: SavePasswordRequest? = intent.getParcelableExtra(
+            CredentialProviderBaseController.REQUEST_TAG)
+        val requestCode: Int = intent.getIntExtra(
+            CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG,
+            DEFAULT_VALUE)
+        params?.let {
+            Identity.getCredentialSavingClient(this).savePassword(params)
+                .addOnSuccessListener {
+                    try {
+                        mWaitingForActivityResult = true
+                        startIntentSenderForResult(
+                            it.pendingIntent.intentSender,
+                            requestCode,
+                            null,
+                            0,
+                            0,
+                            0,
+                            null
+                        )
+                    } catch (e: IntentSender.SendIntentException) {
+                        setupFailure(resultReceiver!!,
+                            CREATE_UNKNOWN,
+                                "During save password, found UI intent sender " +
+                                    "failure: ${e.message}")
+                    }
+            }.addOnFailureListener { e: Exception ->
+                    var errName: String = CREATE_UNKNOWN
+                    if (e is ApiException && e.statusCode in
+                        CredentialProviderBaseController.retryables) {
+                        errName = CREATE_INTERRUPTED
+                    }
+                    setupFailure(resultReceiver!!, errName, "During save password, found " +
+                        "password failure response from one tap ${e.message}")
+            }
+        } ?: run {
+            Log.i(TAG, "During save password, params is null, nothing to launch for create" +
+                " password")
+            finish()
+        }
+    }
+
+    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+        super.onActivityResult(requestCode, resultCode, data)
+        val bundle = Bundle()
+        bundle.putBoolean(CredentialProviderBaseController.FAILURE_RESPONSE_TAG, false)
+        bundle.putInt(CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG, requestCode)
+        bundle.putParcelable(CredentialProviderBaseController.RESULT_DATA_TAG, data)
+        resultReceiver?.send(resultCode, bundle)
+        mWaitingForActivityResult = false
         finish()
-      }
     }
-  }
 
-  private fun handleCreatePassword(intent: Intent, resultReceiver: ResultReceiver?) {
-    val params: SavePasswordRequest? =
-      intent.getParcelableExtra(CredentialProviderBaseController.REQUEST_TAG)
-    val requestCode: Int =
-      intent.getIntExtra(CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG, DEFAULT_VALUE)
-
-    if (intent.hasExtra(CredentialProviderBaseController.REQUEST_TAG) && resultReceiver != null) {
-      val result =
-        GmsCoreUtils.handleCreatePassword(
-          Identity.getCredentialSavingClient(this),
-          resultReceiver,
-          params!!,
-          requestCode,
-          this
-        )
-      mWaitingForActivityResult = result.waitingForActivityResult
-      if (result.hasFinished) {
-        finish()
-      }
+    companion object {
+        private const val DEFAULT_VALUE: Int = 1
+        private const val TAG = "HiddenActivity"
+        private const val KEY_AWAITING_RESULT = "androidx.credentials.playservices.AWAITING_RESULT"
     }
-  }
-
-  private fun handleCreatePublicKeyCredential(intent: Intent, resultReceiver: ResultReceiver?) {
-    val fidoRegistrationRequest: PublicKeyCredentialCreationOptions? =
-      intent.getParcelableExtra(CredentialProviderBaseController.REQUEST_TAG)
-    val requestCode: Int =
-      intent.getIntExtra(CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG, DEFAULT_VALUE)
-
-    if (intent.hasExtra(CredentialProviderBaseController.REQUEST_TAG) && resultReceiver != null) {
-      val result =
-        GmsCoreUtils.handleCreatePublicKeyCredential(
-          Fido.getFido2ApiClient(this),
-          resultReceiver,
-          fidoRegistrationRequest!!,
-          requestCode,
-          this
-        )
-      mWaitingForActivityResult = result.waitingForActivityResult
-      if (result.hasFinished) {
-        finish()
-      }
-    }
-  }
-
-  override fun onSaveInstanceState(outState: Bundle) {
-    outState.putBoolean(KEY_AWAITING_RESULT, mWaitingForActivityResult)
-    super.onSaveInstanceState(outState)
-  }
-
-  override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
-    super.onActivityResult(requestCode, resultCode, data)
-    val bundle = Bundle()
-    bundle.putBoolean(CredentialProviderBaseController.FAILURE_RESPONSE_TAG, false)
-    bundle.putInt(CredentialProviderBaseController.ACTIVITY_REQUEST_CODE_TAG, requestCode)
-    bundle.putParcelable(CredentialProviderBaseController.RESULT_DATA_TAG, data)
-    resultReceiver?.send(resultCode, bundle)
-    mWaitingForActivityResult = false
-    finish()
-  }
-
-  companion object {
-    private const val DEFAULT_VALUE: Int = 1
-    private const val TAG = "HiddenActivity"
-    private const val KEY_AWAITING_RESULT = "androidx.credentials.playservices.AWAITING_RESULT"
-  }
 }
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/BeginSignInControllerUtility.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/BeginSignInControllerUtility.kt
index aaadc9b..34238d8 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/BeginSignInControllerUtility.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/BeginSignInControllerUtility.kt
@@ -63,8 +63,6 @@
                         )
                     }
                     isPublicKeyCredReqFound = true
-                    // TODO(b/262924507) : watch for GIS update on single vs multiple options of a
-                    // single type. Also make allow list update as GIS has done it.
                 } else if (option is GetGoogleIdOption) {
                     requestBuilder.setGoogleIdTokenRequestOptions(
                         convertToGoogleIdTokenOption(option)
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt
index 18f8a16..b0d9453 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt
@@ -36,13 +36,11 @@
 import androidx.credentials.exceptions.GetCredentialInterruptedException
 import androidx.credentials.exceptions.GetCredentialUnknownException
 import androidx.credentials.playservices.CredentialProviderPlayServicesImpl
-import androidx.credentials.playservices.GmsCoreUtils
 import androidx.credentials.playservices.HiddenActivity
 import androidx.credentials.playservices.controllers.BeginSignIn.BeginSignInControllerUtility.Companion.constructBeginSignInRequest
 import androidx.credentials.playservices.controllers.CreatePublicKeyCredential.PublicKeyCredentialControllerUtility
 import androidx.credentials.playservices.controllers.CredentialProviderBaseController
 import androidx.credentials.playservices.controllers.CredentialProviderController
-import androidx.fragment.app.FragmentActivity
 import com.google.android.gms.auth.api.identity.BeginSignInRequest
 import com.google.android.gms.auth.api.identity.Identity
 import com.google.android.gms.auth.api.identity.SignInCredential
@@ -119,20 +117,6 @@
         }
 
         val convertedRequest: BeginSignInRequest = this.convertRequestToPlayServices(request)
-
-        // If we were passed a fragment activity use that instead of a hidden one.
-        if (context is FragmentActivity) {
-            try {
-                GmsCoreUtils.handleBeginSignIn(
-                    Identity.getSignInClient(context),
-                    resultReceiver, convertedRequest, GmsCoreUtils.DEFAULT_REQUEST_CODE,
-                    context)
-                return
-            } catch (e: Exception) {
-                Log.e(TAG, "Failed to use fragment flow", e)
-            }
-        }
-
         val hiddenIntent = Intent(context, HiddenActivity::class.java)
         hiddenIntent.putExtra(REQUEST_TAG, convertedRequest)
         generateHiddenActivityIntent(resultReceiver, hiddenIntent, BEGIN_SIGN_IN_TAG)
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePassword/CredentialProviderCreatePasswordController.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePassword/CredentialProviderCreatePasswordController.kt
index ee4c3bb..360b0c6 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePassword/CredentialProviderCreatePasswordController.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePassword/CredentialProviderCreatePasswordController.kt
@@ -32,12 +32,9 @@
 import androidx.credentials.exceptions.CreateCredentialException
 import androidx.credentials.exceptions.CreateCredentialUnknownException
 import androidx.credentials.playservices.CredentialProviderPlayServicesImpl
-import androidx.credentials.playservices.GmsCoreUtils
 import androidx.credentials.playservices.HiddenActivity
 import androidx.credentials.playservices.controllers.CredentialProviderBaseController
 import androidx.credentials.playservices.controllers.CredentialProviderController
-import androidx.fragment.app.FragmentActivity
-import com.google.android.gms.auth.api.identity.Identity
 import com.google.android.gms.auth.api.identity.SavePasswordRequest
 import com.google.android.gms.auth.api.identity.SignInPassword
 import java.util.concurrent.Executor
@@ -103,20 +100,6 @@
         }
 
         val convertedRequest: SavePasswordRequest = this.convertRequestToPlayServices(request)
-
-        // If we were passed a fragment activity use that instead of a hidden one.
-        if (context is FragmentActivity) {
-            try {
-                GmsCoreUtils.handleCreatePassword(
-                    Identity.getCredentialSavingClient(context),
-                    resultReceiver, convertedRequest, GmsCoreUtils.DEFAULT_REQUEST_CODE,
-                    context)
-                return
-            } catch (e: Exception) {
-                Log.e(TAG, "Failed to use fragment flow", e)
-            }
-        }
-
         val hiddenIntent = Intent(context, HiddenActivity::class.java)
         hiddenIntent.putExtra(REQUEST_TAG, convertedRequest)
         generateHiddenActivityIntent(resultReceiver, hiddenIntent, CREATE_PASSWORD_TAG)
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/CredentialProviderCreatePublicKeyCredentialController.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/CredentialProviderCreatePublicKeyCredentialController.kt
index a3d614a..1302a81 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/CredentialProviderCreatePublicKeyCredentialController.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/CredentialProviderCreatePublicKeyCredentialController.kt
@@ -35,11 +35,9 @@
 import androidx.credentials.exceptions.domerrors.UnknownError
 import androidx.credentials.exceptions.publickeycredential.CreatePublicKeyCredentialDomException
 import androidx.credentials.playservices.CredentialProviderPlayServicesImpl
-import androidx.credentials.playservices.GmsCoreUtils
 import androidx.credentials.playservices.HiddenActivity
 import androidx.credentials.playservices.controllers.CredentialProviderBaseController
 import androidx.credentials.playservices.controllers.CredentialProviderController
-import androidx.fragment.app.FragmentActivity
 import com.google.android.gms.fido.Fido
 import com.google.android.gms.fido.fido2.api.common.PublicKeyCredential
 import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialCreationOptions
@@ -121,19 +119,6 @@
         if (CredentialProviderPlayServicesImpl.cancellationReviewer(cancellationSignal)) {
             return
         }
-
-        // If we were passed a fragment activity use that instead of a hidden one.
-        if (context is FragmentActivity) {
-            try {
-                GmsCoreUtils.handleCreatePublicKeyCredential(Fido.getFido2ApiClient(context),
-                    resultReceiver, fidoRegistrationRequest, GmsCoreUtils.DEFAULT_REQUEST_CODE,
-                    context)
-                return
-            } catch (e: Exception) {
-                Log.e(TAG, "Failed to use fragment flow", e)
-            }
-        }
-
         val hiddenIntent = Intent(context, HiddenActivity::class.java)
         hiddenIntent.putExtra(REQUEST_TAG, fidoRegistrationRequest)
         generateHiddenActivityIntent(resultReceiver, hiddenIntent,
@@ -217,7 +202,6 @@
     companion object {
         private const val TAG = "CreatePublicKey"
         private var controller: CredentialProviderCreatePublicKeyCredentialController? = null
-        // TODO(b/262924507) : Test multiple calls (re-instantiation validates but just in case)
 
         /**
          * This finds a past version of the
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/PublicKeyCredentialControllerUtility.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/PublicKeyCredentialControllerUtility.kt
index c7701c6..a18deea 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/PublicKeyCredentialControllerUtility.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/PublicKeyCredentialControllerUtility.kt
@@ -139,7 +139,6 @@
         fun toCreatePasskeyResponseJson(cred: PublicKeyCredential): String {
             val json = JSONObject()
             val authenticatorResponse = cred.response
-            // TODO(b/262924507) : Look for FIDO changes in conditional mediation available
             if (authenticatorResponse is AuthenticatorAttestationResponse) {
                 val responseJson = JSONObject()
                 responseJson.put(
@@ -260,7 +259,6 @@
                     JSON_KEY_USER_HANDLE, b64Encode(authenticatorResponse.userHandle!!)
                 )
             }
-            // TODO(b/262924507) : attestation object missing in fido impl
             json.put(JSON_KEY_RESPONSE, responseJson)
             json.put(JSON_KEY_ID, publicKeyCred.id)
             json.put(JSON_KEY_RAW_ID, b64Encode(publicKeyCred.rawId))
@@ -438,7 +436,6 @@
                         )
                     )
                 }
-                // TODO(b/262924507) : Fido implementation lacks userVerification in current impl
                 builder.setAuthenticatorSelection(
                     authSelectionBuilder.build()
                 )
@@ -497,8 +494,7 @@
                             descriptorType,
                             descriptorId, transports
                         )
-                    ) // TODO(b/262924507) : Ensure spec changes (i.e. int algorithm) in current
-                    // fido impl stays that way - edit if fido modifies
+                    )
                 }
             }
             builder.setExcludeList(excludeCredentialsList)
@@ -519,7 +515,6 @@
             val rp = json.getJSONObject(JSON_KEY_RP)
             val rpId = rp.getString(JSON_KEY_ID)
             val rpName = rp.optString(JSON_KEY_NAME, "")
-            // TODO(b/262924507) : Fido and spec differ; always keep re-checking if aligns
             var rpIcon: String? = rp.optString(JSON_KEY_ICON, "")
             if (rpIcon!!.isEmpty()) {
                 rpIcon = null
diff --git a/credentials/credentials/api/1.2.0-beta01.txt b/credentials/credentials/api/1.2.0-beta01.txt
index 1e8a92e..8c9cf86 100644
--- a/credentials/credentials/api/1.2.0-beta01.txt
+++ b/credentials/credentials/api/1.2.0-beta01.txt
@@ -263,6 +263,13 @@
 
 package androidx.credentials.exceptions {
 
+  public final class ClearCredentialCustomException extends androidx.credentials.exceptions.ClearCredentialException {
+    ctor public ClearCredentialCustomException(String type);
+    ctor public ClearCredentialCustomException(String type, optional CharSequence? errorMessage);
+    method public String getType();
+    property public String type;
+  }
+
   public abstract class ClearCredentialException extends java.lang.Exception {
   }
 
@@ -286,18 +293,18 @@
     ctor public ClearCredentialUnsupportedException(optional CharSequence? errorMessage);
   }
 
-  public final class ClearCustomCredentialException extends androidx.credentials.exceptions.ClearCredentialException {
-    ctor public ClearCustomCredentialException(String type);
-    ctor public ClearCustomCredentialException(String type, optional CharSequence? errorMessage);
-    method public String getType();
-    property public String type;
-  }
-
   public final class CreateCredentialCancellationException extends androidx.credentials.exceptions.CreateCredentialException {
     ctor public CreateCredentialCancellationException();
     ctor public CreateCredentialCancellationException(optional CharSequence? errorMessage);
   }
 
+  public final class CreateCredentialCustomException extends androidx.credentials.exceptions.CreateCredentialException {
+    ctor public CreateCredentialCustomException(String type);
+    ctor public CreateCredentialCustomException(String type, optional CharSequence? errorMessage);
+    method public String getType();
+    property public String type;
+  }
+
   public abstract class CreateCredentialException extends java.lang.Exception {
   }
 
@@ -326,18 +333,18 @@
     ctor public CreateCredentialUnsupportedException(optional CharSequence? errorMessage);
   }
 
-  public final class CreateCustomCredentialException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCustomCredentialException(String type);
-    ctor public CreateCustomCredentialException(String type, optional CharSequence? errorMessage);
-    method public String getType();
-    property public String type;
-  }
-
   public final class GetCredentialCancellationException extends androidx.credentials.exceptions.GetCredentialException {
     ctor public GetCredentialCancellationException();
     ctor public GetCredentialCancellationException(optional CharSequence? errorMessage);
   }
 
+  public final class GetCredentialCustomException extends androidx.credentials.exceptions.GetCredentialException {
+    ctor public GetCredentialCustomException(String type);
+    ctor public GetCredentialCustomException(String type, optional CharSequence? errorMessage);
+    method public String getType();
+    property public String type;
+  }
+
   public abstract class GetCredentialException extends java.lang.Exception {
   }
 
@@ -361,13 +368,6 @@
     ctor public GetCredentialUnsupportedException(optional CharSequence? errorMessage);
   }
 
-  public final class GetCustomCredentialException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public GetCustomCredentialException(String type);
-    ctor public GetCustomCredentialException(String type, optional CharSequence? errorMessage);
-    method public String getType();
-    property public String type;
-  }
-
   public final class NoCredentialException extends androidx.credentials.exceptions.GetCredentialException {
     ctor public NoCredentialException();
     ctor public NoCredentialException(optional CharSequence? errorMessage);
@@ -698,6 +698,7 @@
     method public String? getOrigin(String privilegedAllowlist);
     method public String getPackageName();
     method public android.content.pm.SigningInfo getSigningInfo();
+    method public boolean isOriginPopulated();
     property public final String packageName;
     property public final android.content.pm.SigningInfo signingInfo;
   }
diff --git a/credentials/credentials/api/current.ignore b/credentials/credentials/api/current.ignore
index be7971e..c25c22c 100644
--- a/credentials/credentials/api/current.ignore
+++ b/credentials/credentials/api/current.ignore
@@ -1,9 +1,3 @@
 // Baseline format: 1.0
-AddedMethod: androidx.credentials.provider.BeginGetPasswordOption#createForTest(android.os.Bundle, String):
-    Added method androidx.credentials.provider.BeginGetPasswordOption.createForTest(android.os.Bundle,String)
-
-
-RemovedMethod: androidx.credentials.provider.BeginGetPasswordOption#createFromEntrySliceForTest(android.os.Bundle, String):
-    Removed method androidx.credentials.provider.BeginGetPasswordOption.createFromEntrySliceForTest(android.os.Bundle,String)
-RemovedMethod: androidx.credentials.provider.BeginGetPasswordOption#createFromForTest(android.os.Bundle, String):
-    Removed method androidx.credentials.provider.BeginGetPasswordOption.createFromForTest(android.os.Bundle,String)
+AddedMethod: androidx.credentials.provider.CallingAppInfo#isOriginPopulated():
+    Added method androidx.credentials.provider.CallingAppInfo.isOriginPopulated()
diff --git a/credentials/credentials/api/current.txt b/credentials/credentials/api/current.txt
index 1e8a92e..8c9cf86 100644
--- a/credentials/credentials/api/current.txt
+++ b/credentials/credentials/api/current.txt
@@ -263,6 +263,13 @@
 
 package androidx.credentials.exceptions {
 
+  public final class ClearCredentialCustomException extends androidx.credentials.exceptions.ClearCredentialException {
+    ctor public ClearCredentialCustomException(String type);
+    ctor public ClearCredentialCustomException(String type, optional CharSequence? errorMessage);
+    method public String getType();
+    property public String type;
+  }
+
   public abstract class ClearCredentialException extends java.lang.Exception {
   }
 
@@ -286,18 +293,18 @@
     ctor public ClearCredentialUnsupportedException(optional CharSequence? errorMessage);
   }
 
-  public final class ClearCustomCredentialException extends androidx.credentials.exceptions.ClearCredentialException {
-    ctor public ClearCustomCredentialException(String type);
-    ctor public ClearCustomCredentialException(String type, optional CharSequence? errorMessage);
-    method public String getType();
-    property public String type;
-  }
-
   public final class CreateCredentialCancellationException extends androidx.credentials.exceptions.CreateCredentialException {
     ctor public CreateCredentialCancellationException();
     ctor public CreateCredentialCancellationException(optional CharSequence? errorMessage);
   }
 
+  public final class CreateCredentialCustomException extends androidx.credentials.exceptions.CreateCredentialException {
+    ctor public CreateCredentialCustomException(String type);
+    ctor public CreateCredentialCustomException(String type, optional CharSequence? errorMessage);
+    method public String getType();
+    property public String type;
+  }
+
   public abstract class CreateCredentialException extends java.lang.Exception {
   }
 
@@ -326,18 +333,18 @@
     ctor public CreateCredentialUnsupportedException(optional CharSequence? errorMessage);
   }
 
-  public final class CreateCustomCredentialException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCustomCredentialException(String type);
-    ctor public CreateCustomCredentialException(String type, optional CharSequence? errorMessage);
-    method public String getType();
-    property public String type;
-  }
-
   public final class GetCredentialCancellationException extends androidx.credentials.exceptions.GetCredentialException {
     ctor public GetCredentialCancellationException();
     ctor public GetCredentialCancellationException(optional CharSequence? errorMessage);
   }
 
+  public final class GetCredentialCustomException extends androidx.credentials.exceptions.GetCredentialException {
+    ctor public GetCredentialCustomException(String type);
+    ctor public GetCredentialCustomException(String type, optional CharSequence? errorMessage);
+    method public String getType();
+    property public String type;
+  }
+
   public abstract class GetCredentialException extends java.lang.Exception {
   }
 
@@ -361,13 +368,6 @@
     ctor public GetCredentialUnsupportedException(optional CharSequence? errorMessage);
   }
 
-  public final class GetCustomCredentialException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public GetCustomCredentialException(String type);
-    ctor public GetCustomCredentialException(String type, optional CharSequence? errorMessage);
-    method public String getType();
-    property public String type;
-  }
-
   public final class NoCredentialException extends androidx.credentials.exceptions.GetCredentialException {
     ctor public NoCredentialException();
     ctor public NoCredentialException(optional CharSequence? errorMessage);
@@ -698,6 +698,7 @@
     method public String? getOrigin(String privilegedAllowlist);
     method public String getPackageName();
     method public android.content.pm.SigningInfo getSigningInfo();
+    method public boolean isOriginPopulated();
     property public final String packageName;
     property public final android.content.pm.SigningInfo signingInfo;
   }
diff --git a/credentials/credentials/api/restricted_1.2.0-beta01.txt b/credentials/credentials/api/restricted_1.2.0-beta01.txt
index 1e8a92e..8c9cf86 100644
--- a/credentials/credentials/api/restricted_1.2.0-beta01.txt
+++ b/credentials/credentials/api/restricted_1.2.0-beta01.txt
@@ -263,6 +263,13 @@
 
 package androidx.credentials.exceptions {
 
+  public final class ClearCredentialCustomException extends androidx.credentials.exceptions.ClearCredentialException {
+    ctor public ClearCredentialCustomException(String type);
+    ctor public ClearCredentialCustomException(String type, optional CharSequence? errorMessage);
+    method public String getType();
+    property public String type;
+  }
+
   public abstract class ClearCredentialException extends java.lang.Exception {
   }
 
@@ -286,18 +293,18 @@
     ctor public ClearCredentialUnsupportedException(optional CharSequence? errorMessage);
   }
 
-  public final class ClearCustomCredentialException extends androidx.credentials.exceptions.ClearCredentialException {
-    ctor public ClearCustomCredentialException(String type);
-    ctor public ClearCustomCredentialException(String type, optional CharSequence? errorMessage);
-    method public String getType();
-    property public String type;
-  }
-
   public final class CreateCredentialCancellationException extends androidx.credentials.exceptions.CreateCredentialException {
     ctor public CreateCredentialCancellationException();
     ctor public CreateCredentialCancellationException(optional CharSequence? errorMessage);
   }
 
+  public final class CreateCredentialCustomException extends androidx.credentials.exceptions.CreateCredentialException {
+    ctor public CreateCredentialCustomException(String type);
+    ctor public CreateCredentialCustomException(String type, optional CharSequence? errorMessage);
+    method public String getType();
+    property public String type;
+  }
+
   public abstract class CreateCredentialException extends java.lang.Exception {
   }
 
@@ -326,18 +333,18 @@
     ctor public CreateCredentialUnsupportedException(optional CharSequence? errorMessage);
   }
 
-  public final class CreateCustomCredentialException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCustomCredentialException(String type);
-    ctor public CreateCustomCredentialException(String type, optional CharSequence? errorMessage);
-    method public String getType();
-    property public String type;
-  }
-
   public final class GetCredentialCancellationException extends androidx.credentials.exceptions.GetCredentialException {
     ctor public GetCredentialCancellationException();
     ctor public GetCredentialCancellationException(optional CharSequence? errorMessage);
   }
 
+  public final class GetCredentialCustomException extends androidx.credentials.exceptions.GetCredentialException {
+    ctor public GetCredentialCustomException(String type);
+    ctor public GetCredentialCustomException(String type, optional CharSequence? errorMessage);
+    method public String getType();
+    property public String type;
+  }
+
   public abstract class GetCredentialException extends java.lang.Exception {
   }
 
@@ -361,13 +368,6 @@
     ctor public GetCredentialUnsupportedException(optional CharSequence? errorMessage);
   }
 
-  public final class GetCustomCredentialException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public GetCustomCredentialException(String type);
-    ctor public GetCustomCredentialException(String type, optional CharSequence? errorMessage);
-    method public String getType();
-    property public String type;
-  }
-
   public final class NoCredentialException extends androidx.credentials.exceptions.GetCredentialException {
     ctor public NoCredentialException();
     ctor public NoCredentialException(optional CharSequence? errorMessage);
@@ -698,6 +698,7 @@
     method public String? getOrigin(String privilegedAllowlist);
     method public String getPackageName();
     method public android.content.pm.SigningInfo getSigningInfo();
+    method public boolean isOriginPopulated();
     property public final String packageName;
     property public final android.content.pm.SigningInfo signingInfo;
   }
diff --git a/credentials/credentials/api/restricted_current.ignore b/credentials/credentials/api/restricted_current.ignore
index be7971e..c25c22c 100644
--- a/credentials/credentials/api/restricted_current.ignore
+++ b/credentials/credentials/api/restricted_current.ignore
@@ -1,9 +1,3 @@
 // Baseline format: 1.0
-AddedMethod: androidx.credentials.provider.BeginGetPasswordOption#createForTest(android.os.Bundle, String):
-    Added method androidx.credentials.provider.BeginGetPasswordOption.createForTest(android.os.Bundle,String)
-
-
-RemovedMethod: androidx.credentials.provider.BeginGetPasswordOption#createFromEntrySliceForTest(android.os.Bundle, String):
-    Removed method androidx.credentials.provider.BeginGetPasswordOption.createFromEntrySliceForTest(android.os.Bundle,String)
-RemovedMethod: androidx.credentials.provider.BeginGetPasswordOption#createFromForTest(android.os.Bundle, String):
-    Removed method androidx.credentials.provider.BeginGetPasswordOption.createFromForTest(android.os.Bundle,String)
+AddedMethod: androidx.credentials.provider.CallingAppInfo#isOriginPopulated():
+    Added method androidx.credentials.provider.CallingAppInfo.isOriginPopulated()
diff --git a/credentials/credentials/api/restricted_current.txt b/credentials/credentials/api/restricted_current.txt
index 1e8a92e..8c9cf86 100644
--- a/credentials/credentials/api/restricted_current.txt
+++ b/credentials/credentials/api/restricted_current.txt
@@ -263,6 +263,13 @@
 
 package androidx.credentials.exceptions {
 
+  public final class ClearCredentialCustomException extends androidx.credentials.exceptions.ClearCredentialException {
+    ctor public ClearCredentialCustomException(String type);
+    ctor public ClearCredentialCustomException(String type, optional CharSequence? errorMessage);
+    method public String getType();
+    property public String type;
+  }
+
   public abstract class ClearCredentialException extends java.lang.Exception {
   }
 
@@ -286,18 +293,18 @@
     ctor public ClearCredentialUnsupportedException(optional CharSequence? errorMessage);
   }
 
-  public final class ClearCustomCredentialException extends androidx.credentials.exceptions.ClearCredentialException {
-    ctor public ClearCustomCredentialException(String type);
-    ctor public ClearCustomCredentialException(String type, optional CharSequence? errorMessage);
-    method public String getType();
-    property public String type;
-  }
-
   public final class CreateCredentialCancellationException extends androidx.credentials.exceptions.CreateCredentialException {
     ctor public CreateCredentialCancellationException();
     ctor public CreateCredentialCancellationException(optional CharSequence? errorMessage);
   }
 
+  public final class CreateCredentialCustomException extends androidx.credentials.exceptions.CreateCredentialException {
+    ctor public CreateCredentialCustomException(String type);
+    ctor public CreateCredentialCustomException(String type, optional CharSequence? errorMessage);
+    method public String getType();
+    property public String type;
+  }
+
   public abstract class CreateCredentialException extends java.lang.Exception {
   }
 
@@ -326,18 +333,18 @@
     ctor public CreateCredentialUnsupportedException(optional CharSequence? errorMessage);
   }
 
-  public final class CreateCustomCredentialException extends androidx.credentials.exceptions.CreateCredentialException {
-    ctor public CreateCustomCredentialException(String type);
-    ctor public CreateCustomCredentialException(String type, optional CharSequence? errorMessage);
-    method public String getType();
-    property public String type;
-  }
-
   public final class GetCredentialCancellationException extends androidx.credentials.exceptions.GetCredentialException {
     ctor public GetCredentialCancellationException();
     ctor public GetCredentialCancellationException(optional CharSequence? errorMessage);
   }
 
+  public final class GetCredentialCustomException extends androidx.credentials.exceptions.GetCredentialException {
+    ctor public GetCredentialCustomException(String type);
+    ctor public GetCredentialCustomException(String type, optional CharSequence? errorMessage);
+    method public String getType();
+    property public String type;
+  }
+
   public abstract class GetCredentialException extends java.lang.Exception {
   }
 
@@ -361,13 +368,6 @@
     ctor public GetCredentialUnsupportedException(optional CharSequence? errorMessage);
   }
 
-  public final class GetCustomCredentialException extends androidx.credentials.exceptions.GetCredentialException {
-    ctor public GetCustomCredentialException(String type);
-    ctor public GetCustomCredentialException(String type, optional CharSequence? errorMessage);
-    method public String getType();
-    property public String type;
-  }
-
   public final class NoCredentialException extends androidx.credentials.exceptions.GetCredentialException {
     ctor public NoCredentialException();
     ctor public NoCredentialException(optional CharSequence? errorMessage);
@@ -698,6 +698,7 @@
     method public String? getOrigin(String privilegedAllowlist);
     method public String getPackageName();
     method public android.content.pm.SigningInfo getSigningInfo();
+    method public boolean isOriginPopulated();
     property public final String packageName;
     property public final android.content.pm.SigningInfo signingInfo;
   }
diff --git a/credentials/credentials/samples/src/main/java/androidx/credentials/samples/CredentialManagerSample.kt b/credentials/credentials/samples/src/main/java/androidx/credentials/samples/CredentialManagerSample.kt
index 57232c9..24eb7ee 100644
--- a/credentials/credentials/samples/src/main/java/androidx/credentials/samples/CredentialManagerSample.kt
+++ b/credentials/credentials/samples/src/main/java/androidx/credentials/samples/CredentialManagerSample.kt
@@ -82,7 +82,9 @@
 val yourCoroutineScope = MainScope()
 
 fun generateGetPasskeyRequestJsonFromServer(): String {
-    TODO("Server call to generate the passkey request option json")
+    throw NotImplementedError("Apps using this sample code should " +
+         "add a call here to generate the passkey request json from " +
+         "their own server")
 }
 
 const val TAG: String = "TAG"
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialManagerJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialManagerJavaTest.java
index 81239e8..d54d4f6 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialManagerJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialManagerJavaTest.java
@@ -204,6 +204,9 @@
 
     @Test
     public void testClearCredentialSessionAsync_throws() throws InterruptedException {
+        if (isPostFrameworkApiLevel()) {
+            return;
+        }
         CountDownLatch latch = new CountDownLatch(1);
         AtomicReference<ClearCredentialException> loadedResult = new AtomicReference<>();
 
@@ -225,10 +228,6 @@
                 });
 
         latch.await(100L, TimeUnit.MILLISECONDS);
-        if (loadedResult.get() == null) {
-            return; // A strange flow occurred where an exception wasn't propagated up
-        }
-
         assertThat(loadedResult.get().getClass()).isEqualTo(
                 ClearCredentialProviderConfigurationException.class);
     }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/ClearCustomCredentialExceptionJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/ClearCredentialCustomExceptionJavaTest.java
similarity index 68%
rename from credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/ClearCustomCredentialExceptionJavaTest.java
rename to credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/ClearCredentialCustomExceptionJavaTest.java
index fd49190..bc6ac0a 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/ClearCustomCredentialExceptionJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/ClearCredentialCustomExceptionJavaTest.java
@@ -26,34 +26,34 @@
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
-public class ClearCustomCredentialExceptionJavaTest {
+public class ClearCredentialCustomExceptionJavaTest {
 
     @Test(expected = ClearCredentialException.class)
-    public void construct_inputsNonEmpty_success() throws ClearCustomCredentialException {
-        throw new ClearCustomCredentialException("type", "msg");
+    public void construct_inputsNonEmpty_success() throws ClearCredentialCustomException {
+        throw new ClearCredentialCustomException("type", "msg");
     }
 
-    @Test(expected = ClearCustomCredentialException.class)
-    public void construct_errorMessageNull_success() throws ClearCustomCredentialException {
-        throw new ClearCustomCredentialException("type", null);
+    @Test(expected = ClearCredentialCustomException.class)
+    public void construct_errorMessageNull_success() throws ClearCredentialCustomException {
+        throw new ClearCredentialCustomException("type", null);
     }
 
     @Test(expected = IllegalArgumentException.class)
-    public void construct_typeEmpty_throws() throws ClearCustomCredentialException {
-        throw new ClearCustomCredentialException("", "msg");
+    public void construct_typeEmpty_throws() throws ClearCredentialCustomException {
+        throw new ClearCredentialCustomException("", "msg");
     }
 
     @Test(expected = NullPointerException.class)
-    public void construct_typeNull_throws() throws ClearCustomCredentialException {
-        throw new ClearCustomCredentialException(null, "msg");
+    public void construct_typeNull_throws() throws ClearCredentialCustomException {
+        throw new ClearCredentialCustomException(null, "msg");
     }
 
     @Test
     public void getter_success() {
         String expectedType = "type";
         String expectedMessage = "message";
-        ClearCustomCredentialException exception = new
-                ClearCustomCredentialException(expectedType , expectedMessage);
+        ClearCredentialCustomException exception = new
+                ClearCredentialCustomException(expectedType , expectedMessage);
         assertThat(exception.getType()).isEqualTo(expectedType);
         assertThat(exception.getErrorMessage()).isEqualTo(expectedMessage);
     }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/ClearCustomCredentialExceptionTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/ClearCredentialCustomExceptionTest.kt
similarity index 77%
rename from credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/ClearCustomCredentialExceptionTest.kt
rename to credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/ClearCredentialCustomExceptionTest.kt
index 08d0e28..a8043f6 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/ClearCustomCredentialExceptionTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/ClearCredentialCustomExceptionTest.kt
@@ -24,27 +24,27 @@
 
 @RunWith(AndroidJUnit4::class)
 @SmallTest
-class ClearCustomCredentialExceptionTest {
-    @Test(expected = ClearCustomCredentialException::class)
+class ClearCredentialCustomExceptionTest {
+    @Test(expected = ClearCredentialCustomException::class)
     fun construct_inputsNonEmpty_success() {
-        throw ClearCustomCredentialException("type", "msg")
+        throw ClearCredentialCustomException("type", "msg")
     }
 
-    @Test(expected = ClearCustomCredentialException::class)
+    @Test(expected = ClearCredentialCustomException::class)
     fun construct_errorMessageNull_success() {
-        throw ClearCustomCredentialException("type", null)
+        throw ClearCredentialCustomException("type", null)
     }
 
     @Test(expected = IllegalArgumentException::class)
     fun construct_typeEmpty_throws() {
-        throw ClearCustomCredentialException("", "msg")
+        throw ClearCredentialCustomException("", "msg")
     }
 
     @Test
     fun getter_success() {
         val expectedType = "type"
         val expectedMessage = "message"
-        val exception = ClearCustomCredentialException(expectedType, expectedMessage)
+        val exception = ClearCredentialCustomException(expectedType, expectedMessage)
         assertThat(exception.type).isEqualTo(expectedType)
         assertThat(exception.errorMessage).isEqualTo(expectedMessage)
     }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/CreateCustomCredentialExceptionJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/CreateCredentialCustomExceptionJavaTest.java
similarity index 65%
rename from credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/CreateCustomCredentialExceptionJavaTest.java
rename to credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/CreateCredentialCustomExceptionJavaTest.java
index a5d4ddd..5ff1b36 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/CreateCustomCredentialExceptionJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/CreateCredentialCustomExceptionJavaTest.java
@@ -26,33 +26,33 @@
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
-public class CreateCustomCredentialExceptionJavaTest {
-    @Test(expected = CreateCustomCredentialException.class)
-    public void construct_inputsNonEmpty_success() throws CreateCustomCredentialException {
-        throw new CreateCustomCredentialException("type", "msg");
+public class CreateCredentialCustomExceptionJavaTest {
+    @Test(expected = CreateCredentialCustomException.class)
+    public void construct_inputsNonEmpty_success() throws CreateCredentialCustomException {
+        throw new CreateCredentialCustomException("type", "msg");
     }
 
-    @Test(expected = CreateCustomCredentialException.class)
-    public void construct_errorMessageNull_success() throws CreateCustomCredentialException {
-        throw new CreateCustomCredentialException("type", null);
+    @Test(expected = CreateCredentialCustomException.class)
+    public void construct_errorMessageNull_success() throws CreateCredentialCustomException {
+        throw new CreateCredentialCustomException("type", null);
     }
 
     @Test(expected = IllegalArgumentException.class)
-    public void construct_typeEmpty_throws() throws CreateCustomCredentialException {
-        throw new CreateCustomCredentialException("", "msg");
+    public void construct_typeEmpty_throws() throws CreateCredentialCustomException {
+        throw new CreateCredentialCustomException("", "msg");
     }
 
     @Test(expected = NullPointerException.class)
-    public void construct_typeNull_throws() throws CreateCustomCredentialException {
-        throw new CreateCustomCredentialException(null, "msg");
+    public void construct_typeNull_throws() throws CreateCredentialCustomException {
+        throw new CreateCredentialCustomException(null, "msg");
     }
 
     @Test
     public void getter_success() {
         String expectedType = "type";
         String expectedMessage = "message";
-        CreateCustomCredentialException exception = new
-                CreateCustomCredentialException(expectedType , expectedMessage);
+        CreateCredentialCustomException exception = new
+                CreateCredentialCustomException(expectedType , expectedMessage);
         assertThat(exception.getType()).isEqualTo(expectedType);
         assertThat(exception.getErrorMessage()).isEqualTo(expectedMessage);
     }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/CreateCustomCredentialExceptionTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/CreateCredentialCustomExceptionTest.kt
similarity index 77%
rename from credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/CreateCustomCredentialExceptionTest.kt
rename to credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/CreateCredentialCustomExceptionTest.kt
index 79870a9..bd86cab 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/CreateCustomCredentialExceptionTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/CreateCredentialCustomExceptionTest.kt
@@ -24,27 +24,27 @@
 
 @RunWith(AndroidJUnit4::class)
 @SmallTest
-class CreateCustomCredentialExceptionTest {
-    @Test(expected = CreateCustomCredentialException::class)
+class CreateCredentialCustomExceptionTest {
+    @Test(expected = CreateCredentialCustomException::class)
     fun construct_inputsNonEmpty_success() {
-        throw CreateCustomCredentialException("type", "msg")
+        throw CreateCredentialCustomException("type", "msg")
     }
 
-    @Test(expected = CreateCustomCredentialException::class)
+    @Test(expected = CreateCredentialCustomException::class)
     fun construct_errorMessageNull_success() {
-        throw CreateCustomCredentialException("type", null)
+        throw CreateCredentialCustomException("type", null)
     }
 
     @Test(expected = IllegalArgumentException::class)
     fun construct_typeEmpty_throws() {
-        throw CreateCustomCredentialException("", "msg")
+        throw CreateCredentialCustomException("", "msg")
     }
 
     @Test
     fun getter_success() {
         val expectedType = "type"
         val expectedMessage = "message"
-        val exception = CreateCustomCredentialException(expectedType, expectedMessage)
+        val exception = CreateCredentialCustomException(expectedType, expectedMessage)
         assertThat(exception.type).isEqualTo(expectedType)
         assertThat(exception.errorMessage).isEqualTo(expectedMessage)
     }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/GetCustomCredentialExceptionJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/GetCredentialCustomExceptionJavaTest.java
similarity index 63%
rename from credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/GetCustomCredentialExceptionJavaTest.java
rename to credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/GetCredentialCustomExceptionJavaTest.java
index a7a1ae4..4be700e 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/GetCustomCredentialExceptionJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/GetCredentialCustomExceptionJavaTest.java
@@ -26,33 +26,33 @@
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
-public class GetCustomCredentialExceptionJavaTest {
-    @Test(expected = GetCustomCredentialException.class)
-    public void construct_inputsNonEmpty_success() throws GetCustomCredentialException {
-        throw new GetCustomCredentialException("type", "msg");
+public class GetCredentialCustomExceptionJavaTest {
+    @Test(expected = GetCredentialCustomException.class)
+    public void construct_inputsNonEmpty_success() throws GetCredentialCustomException {
+        throw new GetCredentialCustomException("type", "msg");
     }
 
-    @Test(expected = GetCustomCredentialException.class)
-    public void construct_errorMessageNull_success() throws GetCustomCredentialException {
-        throw new GetCustomCredentialException("type", null);
+    @Test(expected = GetCredentialCustomException.class)
+    public void construct_errorMessageNull_success() throws GetCredentialCustomException {
+        throw new GetCredentialCustomException("type", null);
     }
 
     @Test(expected = IllegalArgumentException.class)
-    public void construct_typeEmpty_throws() throws GetCustomCredentialException {
-        throw new GetCustomCredentialException("", "msg");
+    public void construct_typeEmpty_throws() throws GetCredentialCustomException {
+        throw new GetCredentialCustomException("", "msg");
     }
 
     @Test(expected = NullPointerException.class)
-    public void construct_typeNull_throws() throws GetCustomCredentialException {
-        throw new GetCustomCredentialException(null, "msg");
+    public void construct_typeNull_throws() throws GetCredentialCustomException {
+        throw new GetCredentialCustomException(null, "msg");
     }
 
     @Test
     public void getter_success() {
         String expectedType = "type";
         String expectedMessage = "message";
-        GetCustomCredentialException exception = new
-                GetCustomCredentialException(expectedType , expectedMessage);
+        GetCredentialCustomException exception = new
+                GetCredentialCustomException(expectedType , expectedMessage);
         assertThat(exception.getType()).isEqualTo(expectedType);
         assertThat(exception.getErrorMessage()).isEqualTo(expectedMessage);
     }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/GetCustomCredentialExceptionTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/GetCredentialCustomExceptionTest.kt
similarity index 77%
rename from credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/GetCustomCredentialExceptionTest.kt
rename to credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/GetCredentialCustomExceptionTest.kt
index d1bf8c3..db7bf97 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/GetCustomCredentialExceptionTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/GetCredentialCustomExceptionTest.kt
@@ -24,27 +24,27 @@
 
 @RunWith(AndroidJUnit4::class)
 @SmallTest
-class GetCustomCredentialExceptionTest {
-    @Test(expected = GetCustomCredentialException::class)
+class GetCredentialCustomExceptionTest {
+    @Test(expected = GetCredentialCustomException::class)
     fun construct_inputsNonEmpty_success() {
-        throw GetCustomCredentialException("type", "msg")
+        throw GetCredentialCustomException("type", "msg")
     }
 
-    @Test(expected = GetCustomCredentialException::class)
+    @Test(expected = GetCredentialCustomException::class)
     fun construct_errorMessageNull_success() {
-        throw GetCustomCredentialException("type", null)
+        throw GetCredentialCustomException("type", null)
     }
 
     @Test(expected = IllegalArgumentException::class)
     fun construct_typeEmpty_throws() {
-        throw GetCustomCredentialException("", "msg")
+        throw GetCredentialCustomException("", "msg")
     }
 
     @Test
     fun getter_success() {
         val expectedType = "type"
         val expectedMessage = "message"
-        val exception = GetCustomCredentialException(expectedType, expectedMessage)
+        val exception = GetCredentialCustomException(expectedType, expectedMessage)
         assertThat(exception.type).isEqualTo(expectedType)
         assertThat(exception.errorMessage).isEqualTo(expectedMessage)
     }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/CreatePublicKeyCredentialDomExceptionJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/CreatePublicKeyCredentialDomExceptionJavaTest.java
index e782efc..9ec0c643 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/CreatePublicKeyCredentialDomExceptionJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/CreatePublicKeyCredentialDomExceptionJavaTest.java
@@ -16,8 +16,12 @@
 
 package androidx.credentials.exceptions.publickeycredential;
 
+import static androidx.credentials.exceptions.publickeycredential.DomExceptionUtils.SEPARATOR;
+
 import static com.google.common.truth.Truth.assertThat;
 
+import androidx.credentials.exceptions.CreateCredentialCustomException;
+import androidx.credentials.exceptions.CreateCredentialException;
 import androidx.credentials.exceptions.domerrors.AbortError;
 import androidx.credentials.exceptions.domerrors.DomError;
 import androidx.credentials.exceptions.domerrors.EncodingError;
@@ -51,7 +55,7 @@
         DomError expectedDomError = new EncodingError();
         String expectedType =
                 CreatePublicKeyCredentialDomException
-                        .TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION
+                        .TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION + SEPARATOR
                         + expectedDomError.getType();
 
         CreatePublicKeyCredentialDomException exception = new
@@ -60,4 +64,38 @@
         assertThat(exception.getType()).isEqualTo(expectedType);
         assertThat(exception.getErrorMessage()).isEqualTo(expectedMessage);
     }
+
+
+    @Test
+    public void frameworkToJetpackConversion_success() {
+        String expectedMessage = "msg";
+        DomError expectedDomError = new EncodingError();
+        String expectedType = CreatePublicKeyCredentialDomException
+                .TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION + SEPARATOR
+                + expectedDomError.getType();
+
+        CreateCredentialException exception = CreatePublicKeyCredentialException
+                .createFrom(expectedType, expectedMessage);
+
+        assertThat(exception).isInstanceOf(CreatePublicKeyCredentialDomException.class);
+        assertThat(((CreatePublicKeyCredentialDomException) exception).getDomError())
+                .isInstanceOf(EncodingError.class);
+        assertThat(exception.getType()).isEqualTo(expectedType);
+        assertThat(exception.getMessage()).isEqualTo(expectedMessage);
+    }
+
+    @Test
+    public void frameworkToJetpackConversion_failure_createsCustomException() {
+        String expectedMessage = "CustomMessage";
+        String expectedType =
+                CreatePublicKeyCredentialDomException
+                        .TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION + "/CustomType";
+
+        CreateCredentialException exception = CreatePublicKeyCredentialException
+                .createFrom(expectedType, expectedMessage);
+
+        assertThat(exception).isInstanceOf(CreateCredentialCustomException.class);
+        assertThat(exception.getType()).isEqualTo(expectedType);
+        assertThat(exception.getMessage()).isEqualTo(expectedMessage);
+    }
 }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/CreatePublicKeyCredentialDomExceptionTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/CreatePublicKeyCredentialDomExceptionTest.kt
index 5be32b62..3f637a2 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/CreatePublicKeyCredentialDomExceptionTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/CreatePublicKeyCredentialDomExceptionTest.kt
@@ -16,9 +16,12 @@
 
 package androidx.credentials.exceptions.publickeycredential
 
+import androidx.credentials.exceptions.CreateCredentialCustomException
 import androidx.credentials.exceptions.domerrors.AbortError
 import androidx.credentials.exceptions.domerrors.EncodingError
-import com.google.common.truth.Truth
+import androidx.credentials.exceptions.publickeycredential.CreatePublicKeyCredentialException.Companion.createFrom
+import androidx.credentials.exceptions.publickeycredential.DomExceptionUtils.Companion.SEPARATOR
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 
 class CreatePublicKeyCredentialDomExceptionTest {
@@ -35,11 +38,46 @@
         val expectedDomError = EncodingError()
         val expectedType =
             CreatePublicKeyCredentialDomException.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION +
-                expectedDomError.type
+                SEPARATOR + expectedDomError.type
 
         val exception = CreatePublicKeyCredentialDomException(expectedDomError, expectedMessage)
 
-        Truth.assertThat(exception.type).isEqualTo(expectedType)
-        Truth.assertThat(exception.errorMessage).isEqualTo(expectedMessage)
+        assertThat(exception.type).isEqualTo(expectedType)
+        assertThat(exception.errorMessage).isEqualTo(expectedMessage)
+    }
+
+    @Test
+    fun frameworkToJetpackConversion_success() {
+        val expectedMessage = "msg"
+        val expectedDomError = EncodingError()
+        val expectedType = CreatePublicKeyCredentialDomException
+            .TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION + SEPARATOR + expectedDomError.type
+
+        val exception = CreatePublicKeyCredentialException.createFrom(expectedType,
+            expectedMessage)
+
+        assertThat(exception).isInstanceOf(
+            CreatePublicKeyCredentialDomException::class.java
+        )
+        assertThat((exception as CreatePublicKeyCredentialDomException).domError)
+            .isInstanceOf(EncodingError::class.java)
+        assertThat(exception.type).isEqualTo(expectedType)
+        assertThat(exception.errorMessage).isEqualTo(expectedMessage)
+    }
+
+    @Test
+    fun frameworkToJetpackConversion_failure_createsCustomException() {
+        val expectedMessage = "CustomMessage"
+        val expectedType =
+            CreatePublicKeyCredentialDomException.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION +
+                "/CustomType"
+
+        val exception = createFrom(expectedType, expectedMessage)
+
+        assertThat(exception).isInstanceOf(
+            CreateCredentialCustomException::class.java
+        )
+        assertThat(exception.type).isEqualTo(expectedType)
+        assertThat(exception.message).isEqualTo(expectedMessage)
     }
 }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/CreatePublicKeyCredentialExceptionJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/CreatePublicKeyCredentialExceptionJavaTest.java
index b40128e..ceb1b57 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/CreatePublicKeyCredentialExceptionJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/CreatePublicKeyCredentialExceptionJavaTest.java
@@ -16,8 +16,14 @@
 
 package androidx.credentials.exceptions.publickeycredential;
 
+import static androidx.credentials.exceptions.publickeycredential.DomExceptionUtils.SEPARATOR;
+
 import static com.google.common.truth.Truth.assertThat;
 
+import androidx.credentials.exceptions.CreateCredentialCustomException;
+import androidx.credentials.exceptions.CreateCredentialException;
+import androidx.credentials.exceptions.domerrors.DataCloneError;
+import androidx.credentials.exceptions.domerrors.DomError;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
@@ -57,4 +63,35 @@
         assertThat(exception.getType()).isEqualTo(expectedType);
         assertThat(exception.getErrorMessage()).isEqualTo(expectedMessage);
     }
+
+    @Test
+    public void frameworkToJetpackConversion_success() {
+        String expectedMessage = "msg";
+        DomError expectedDomError = new DataCloneError();
+        String expectedType = CreatePublicKeyCredentialDomException
+                .TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION + SEPARATOR
+                + expectedDomError.getType();
+
+        CreateCredentialException exception = CreatePublicKeyCredentialException
+                .createFrom(expectedType, expectedMessage);
+
+        assertThat(exception).isInstanceOf(CreatePublicKeyCredentialDomException.class);
+        assertThat(((CreatePublicKeyCredentialDomException) exception).getDomError())
+                .isInstanceOf(DataCloneError.class);
+        assertThat(exception.getType()).isEqualTo(expectedType);
+        assertThat(exception.getMessage()).isEqualTo(expectedMessage);
+    }
+
+    @Test
+    public void frameworkToJetpackConversion_failure_createsCustomException() {
+        String expectedMessage = "CustomMessage";
+        String expectedType = "CustomType";
+
+        CreateCredentialException exception = CreatePublicKeyCredentialException
+                .createFrom(expectedType, expectedMessage);
+
+        assertThat(exception.getClass()).isEqualTo(CreateCredentialCustomException.class);
+        assertThat(exception.getType()).isEqualTo(expectedType);
+        assertThat(exception.getMessage()).isEqualTo(expectedMessage);
+    }
 }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/CreatePublicKeyCredentialExceptionTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/CreatePublicKeyCredentialExceptionTest.kt
index fb7d7b9..8d5b1a5 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/CreatePublicKeyCredentialExceptionTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/CreatePublicKeyCredentialExceptionTest.kt
@@ -16,6 +16,11 @@
 
 package androidx.credentials.exceptions.publickeycredential
 
+import androidx.credentials.exceptions.CreateCredentialCustomException
+import androidx.credentials.exceptions.domerrors.DataCloneError
+import androidx.credentials.exceptions.domerrors.DomError
+import androidx.credentials.exceptions.publickeycredential.CreatePublicKeyCredentialException.Companion.createFrom
+import androidx.credentials.exceptions.publickeycredential.DomExceptionUtils.Companion.SEPARATOR
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
@@ -45,8 +50,43 @@
     fun getter_success() {
         val expectedType = "type"
         val expectedMessage = "message"
+
         val exception = CreatePublicKeyCredentialException(expectedType, expectedMessage)
+
         assertThat(exception.type).isEqualTo(expectedType)
         assertThat(exception.errorMessage).isEqualTo(expectedMessage)
     }
+
+    @Test
+    fun frameworkToJetpackConversion_success() {
+        val expectedMessage = "msg"
+        val expectedDomError: DomError = DataCloneError()
+        val expectedType =
+            CreatePublicKeyCredentialDomException.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION +
+                SEPARATOR + expectedDomError.type
+
+        val exception = createFrom(expectedType, expectedMessage)
+
+        assertThat(exception).isInstanceOf(
+            CreatePublicKeyCredentialDomException::class.java
+        )
+        assertThat((exception as CreatePublicKeyCredentialDomException).domError)
+            .isInstanceOf(DataCloneError::class.java)
+        assertThat(exception.type).isEqualTo(expectedType)
+        assertThat(exception.message).isEqualTo(expectedMessage)
+    }
+
+    @Test
+    fun frameworkToJetpackConversion_failure_createsCustomException() {
+        val expectedMessage = "CustomMessage"
+        val expectedType = "CustomType"
+
+        val exception = createFrom(expectedType, expectedMessage)
+
+        assertThat(exception).isInstanceOf(
+            CreateCredentialCustomException::class.java
+        )
+        assertThat(exception.type).isEqualTo(expectedType)
+        assertThat(exception.message).isEqualTo(expectedMessage)
+    }
 }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/GetPublicKeyCredentialDomExceptionJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/GetPublicKeyCredentialDomExceptionJavaTest.java
index 561065f..2c0ad70 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/GetPublicKeyCredentialDomExceptionJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/GetPublicKeyCredentialDomExceptionJavaTest.java
@@ -16,8 +16,12 @@
 
 package androidx.credentials.exceptions.publickeycredential;
 
+import static androidx.credentials.exceptions.publickeycredential.DomExceptionUtils.SEPARATOR;
+
 import static com.google.common.truth.Truth.assertThat;
 
+import androidx.credentials.exceptions.GetCredentialCustomException;
+import androidx.credentials.exceptions.GetCredentialException;
 import androidx.credentials.exceptions.domerrors.AbortError;
 import androidx.credentials.exceptions.domerrors.DomError;
 import androidx.credentials.exceptions.domerrors.EncodingError;
@@ -51,7 +55,7 @@
         DomError expectedDomError = new EncodingError();
         String expectedType =
                 GetPublicKeyCredentialDomException
-                        .TYPE_GET_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION
+                        .TYPE_GET_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION + SEPARATOR
                         + expectedDomError.getType();
 
         GetPublicKeyCredentialDomException exception = new
@@ -60,4 +64,37 @@
         assertThat(exception.getType()).isEqualTo(expectedType);
         assertThat(exception.getErrorMessage()).isEqualTo(expectedMessage);
     }
+
+    @Test
+    public void frameworkToJetpackConversion_success() {
+        String expectedMessage = "msg";
+        DomError expectedDomError = new EncodingError();
+        String expectedType = GetPublicKeyCredentialDomException
+                .TYPE_GET_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION + SEPARATOR
+                + expectedDomError.getType();
+
+        GetCredentialException exception = GetPublicKeyCredentialException
+                .createFrom(expectedType, expectedMessage);
+
+        assertThat(exception).isInstanceOf(GetPublicKeyCredentialDomException.class);
+        assertThat(((GetPublicKeyCredentialDomException) exception).getDomError())
+                .isInstanceOf(EncodingError.class);
+        assertThat(exception.getType()).isEqualTo(expectedType);
+        assertThat(exception.getMessage()).isEqualTo(expectedMessage);
+    }
+
+    @Test
+    public void frameworkToJetpackConversion_failure_createsCustomException() {
+        String expectedMessage = "CustomMessage";
+        String expectedType =
+                GetPublicKeyCredentialDomException
+                        .TYPE_GET_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION + "/CustomType";
+
+        GetCredentialException exception = GetPublicKeyCredentialException
+                .createFrom(expectedType, expectedMessage);
+
+        assertThat(exception).isInstanceOf(GetCredentialCustomException.class);
+        assertThat(exception.getType()).isEqualTo(expectedType);
+        assertThat(exception.getMessage()).isEqualTo(expectedMessage);
+    }
 }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/GetPublicKeyCredentialDomExceptionTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/GetPublicKeyCredentialDomExceptionTest.kt
index 7dcdd71..9ace0a1 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/GetPublicKeyCredentialDomExceptionTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/GetPublicKeyCredentialDomExceptionTest.kt
@@ -16,9 +16,14 @@
 
 package androidx.credentials.exceptions.publickeycredential
 
+import androidx.credentials.exceptions.GetCredentialCustomException
 import androidx.credentials.exceptions.domerrors.AbortError
+import androidx.credentials.exceptions.domerrors.DomError
 import androidx.credentials.exceptions.domerrors.EncodingError
-import com.google.common.truth.Truth
+import androidx.credentials.exceptions.publickeycredential.DomExceptionUtils.Companion.SEPARATOR
+import androidx.credentials.exceptions.publickeycredential.GetPublicKeyCredentialDomException.Companion.createFrom
+import androidx.credentials.exceptions.publickeycredential.GetPublicKeyCredentialException.Companion.createFrom
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 
 class GetPublicKeyCredentialDomExceptionTest {
@@ -35,11 +40,48 @@
         val expectedDomError = EncodingError()
         val expectedType =
             GetPublicKeyCredentialDomException.TYPE_GET_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION +
-                expectedDomError.type
+                SEPARATOR + expectedDomError.type
 
         val exception = GetPublicKeyCredentialDomException(expectedDomError, expectedMessage)
 
-        Truth.assertThat(exception.type).isEqualTo(expectedType)
-        Truth.assertThat(exception.errorMessage).isEqualTo(expectedMessage)
+        assertThat(exception.type).isEqualTo(expectedType)
+        assertThat(exception.errorMessage).isEqualTo(expectedMessage)
+    }
+
+    @Test
+    fun frameworkToJetpackConversion_success() {
+        val expectedMessage = "msg"
+        val expectedDomError: DomError = EncodingError()
+        val expectedType =
+            GetPublicKeyCredentialDomException.TYPE_GET_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION +
+                SEPARATOR + expectedDomError.type
+
+        val exception = GetPublicKeyCredentialException
+            .createFrom(expectedType, expectedMessage)
+
+        assertThat(exception).isInstanceOf(
+            GetPublicKeyCredentialDomException::class.java
+        )
+        assertThat((exception as GetPublicKeyCredentialDomException).domError)
+            .isInstanceOf(EncodingError::class.java)
+        assertThat(exception.type).isEqualTo(expectedType)
+        assertThat(exception.message).isEqualTo(expectedMessage)
+    }
+
+    @Test
+    fun frameworkToJetpackConversion_failure_createsCustomException() {
+        val expectedMessage = "CustomMessage"
+        val expectedType =
+            GetPublicKeyCredentialDomException.TYPE_GET_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION +
+                "/CustomType"
+
+        val exception = GetPublicKeyCredentialException
+            .createFrom(expectedType, expectedMessage)
+
+        assertThat(exception).isInstanceOf(
+            GetCredentialCustomException::class.java
+        )
+        assertThat(exception.type).isEqualTo(expectedType)
+        assertThat(exception.message).isEqualTo(expectedMessage)
     }
 }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/GetPublicKeyCredentialExceptionJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/GetPublicKeyCredentialExceptionJavaTest.java
index 919c35f..15e18a8 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/GetPublicKeyCredentialExceptionJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/GetPublicKeyCredentialExceptionJavaTest.java
@@ -16,8 +16,14 @@
 
 package androidx.credentials.exceptions.publickeycredential;
 
+import static androidx.credentials.exceptions.publickeycredential.DomExceptionUtils.SEPARATOR;
+
 import static com.google.common.truth.Truth.assertThat;
 
+import androidx.credentials.exceptions.GetCredentialCustomException;
+import androidx.credentials.exceptions.GetCredentialException;
+import androidx.credentials.exceptions.domerrors.DomError;
+import androidx.credentials.exceptions.domerrors.EncodingError;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
@@ -57,4 +63,35 @@
         assertThat(exception.getType()).isEqualTo(expectedType);
         assertThat(exception.getErrorMessage()).isEqualTo(expectedMessage);
     }
+
+    @Test
+    public void frameworkToJetpackConversion_success() {
+        String expectedMessage = "msg";
+        DomError expectedDomError = new EncodingError();
+        String expectedType = GetPublicKeyCredentialDomException
+                .TYPE_GET_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION + SEPARATOR
+                + expectedDomError.getType();
+
+        GetCredentialException exception = GetPublicKeyCredentialException
+                .createFrom(expectedType, expectedMessage);
+
+        assertThat(exception).isInstanceOf(GetPublicKeyCredentialDomException.class);
+        assertThat(((GetPublicKeyCredentialDomException) exception).getDomError())
+                .isInstanceOf(EncodingError.class);
+        assertThat(exception.getType()).isEqualTo(expectedType);
+        assertThat(exception.getMessage()).isEqualTo(expectedMessage);
+    }
+
+    @Test
+    public void frameworkToJetpackConversion_failure_createsCustomException() {
+        String expectedMessage = "CustomMessage";
+        String expectedType = "CustomType";
+
+        GetCredentialException exception = GetPublicKeyCredentialException
+                .createFrom(expectedType, expectedMessage);
+
+        assertThat(exception).isInstanceOf(GetCredentialCustomException.class);
+        assertThat(exception.getType()).isEqualTo(expectedType);
+        assertThat(exception.getMessage()).isEqualTo(expectedMessage);
+    }
 }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/GetPublicKeyCredentialExceptionTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/GetPublicKeyCredentialExceptionTest.kt
index 7124611..ad63b38 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/GetPublicKeyCredentialExceptionTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/exceptions/publickeycredential/GetPublicKeyCredentialExceptionTest.kt
@@ -16,6 +16,11 @@
 
 package androidx.credentials.exceptions.publickeycredential
 
+import androidx.credentials.exceptions.GetCredentialCustomException
+import androidx.credentials.exceptions.domerrors.DomError
+import androidx.credentials.exceptions.domerrors.EncodingError
+import androidx.credentials.exceptions.publickeycredential.DomExceptionUtils.Companion.SEPARATOR
+import androidx.credentials.exceptions.publickeycredential.GetPublicKeyCredentialException.Companion.createFrom
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
@@ -45,8 +50,43 @@
     fun getter_success() {
         val expectedType = "type"
         val expectedMessage = "message"
+
         val exception = GetPublicKeyCredentialException(expectedType, expectedMessage)
+
         assertThat(exception.type).isEqualTo(expectedType)
         assertThat(exception.errorMessage).isEqualTo(expectedMessage)
     }
+
+    @Test
+    fun frameworkToJetpackConversion_success() {
+        val expectedMessage = "msg"
+        val expectedDomError: DomError = EncodingError()
+        val expectedType =
+            GetPublicKeyCredentialDomException.TYPE_GET_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION +
+                SEPARATOR + expectedDomError.type
+
+        val exception = createFrom(expectedType, expectedMessage)
+
+        assertThat(exception).isInstanceOf(
+            GetPublicKeyCredentialDomException::class.java
+        )
+        assertThat((exception as GetPublicKeyCredentialDomException).domError)
+            .isInstanceOf(EncodingError::class.java)
+        assertThat(exception.type).isEqualTo(expectedType)
+        assertThat(exception.message).isEqualTo(expectedMessage)
+    }
+
+    @Test
+    fun frameworkToJetpackConversion_failure_createsCustomException() {
+        val expectedMessage = "CustomMessage"
+        val expectedType = "CustomType"
+
+        val exception = createFrom(expectedType, expectedMessage)
+
+        assertThat(exception).isInstanceOf(
+            GetCredentialCustomException::class.java
+        )
+        assertThat(exception.type).isEqualTo(expectedType)
+        assertThat(exception.message).isEqualTo(expectedMessage)
+    }
 }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CallingAppInfoTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CallingAppInfoTest.kt
index a15e88d..cac92a9 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CallingAppInfoTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CallingAppInfoTest.kt
@@ -16,69 +16,357 @@
 
 package androidx.credentials.provider
 
+import android.content.Context
+import android.content.pm.PackageManager
 import android.content.pm.SigningInfo
+import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
-import org.junit.Assert
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertThrows
+import org.junit.Assert.assertTrue
+import org.junit.BeforeClass
 import org.junit.Test
 import org.junit.runner.RunWith
 
- @SdkSuppress(minSdkVersion = 28)
- @RunWith(AndroidJUnit4::class)
- @SmallTest
- class CallingAppInfoTest {
+@SdkSuppress(minSdkVersion = 28)
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class CallingAppInfoTest {
 
-     @Test
-     fun constructor_success() {
-         CallingAppInfo("name", SigningInfo())
-     }
+    companion object {
+        lateinit var signingInfo: SigningInfo
+        lateinit var packageName: String
 
-     @Test
-     fun constructor_success_withOrigin() {
-         val origin = "origin"
-         val callingAppInfo = CallingAppInfo("name", SigningInfo(), origin)
+        private const val ORIGIN = "origin"
 
-         assertThat(callingAppInfo.getOrigin("{\"key\":\"value\"}")).isNull()
-         assertThat(callingAppInfo.origin).isEqualTo(origin)
-     }
+        private const val ALLOWLIST_VALID_JSON = "{\"apps\": [\n" +
+            "   {\n" +
+            "      \"type\": \"android\", \n" +
+            "      \"info\": {\n" +
+            "         \"package_name\": \"androidx.credentials.test\",\n" +
+            "         \"signatures\" : [\n" +
+            "         {\"build\": \"release\",\n" +
+            "             \"cert_fingerprint_sha256\": \"EE:B9:3D:E4:98:2F:A1:2E:AD:5B:C1:16:7A" +
+            ":6E:10:BD:23:49:B4:04:65:C4:3A:01:CC:54:06:4D:E5:2A:38:04\"\n" +
+            "         },\n" +
+            "         {\"build\": \"ud\",\n" +
+            "         \"cert_fingerprint_sha256\": \"59:0D:2D:7B:33:6A:BD:FB:54:CD:3D:8B:" +
+            "36:8C:5C:3A:7D:22:67:5A:9A:85:9A:6A:65:47:FD:4C:8A:7C:30:32\"\n" +
+            "         }]\n" +
+            "      }\n" +
+            "    }\n" +
+            "]}\n" +
+            "\n"
 
-     @Test
-     fun constructor_fail_emptyPackageName() {
-         Assert.assertThrows(
+        private const val ALLOWLIST_VALID_DEBUG_CERT_JSON = "{\"apps\": [\n" +
+            "   {\n" +
+            "      \"type\": \"android\", \n" +
+            "      \"info\": {\n" +
+            "         \"package_name\": \"androidx.credentials.test\",\n" +
+            "         \"signatures\" : [\n" +
+            "         {\"build\": \"ud\",\n" +
+            "             \"cert_fingerprint_sha256\": \"EE:B9:3D:E4:98:2F:A1:2E:AD:5B:C1:16:7A" +
+            ":6E:10:BD:23:49:B4:04:65:C4:3A:01:CC:54:06:4D:E5:2A:38:04\"\n" +
+            "         },\n" +
+            "         {\"build\": \"release\",\n" +
+            "         \"cert_fingerprint_sha256\": \"59:0D:2D:7B:33:6A:BD:FB:54:CD:3D:8B:" +
+            "36:8C:5C:3A:7D:22:67:5A:9A:85:9A:6A:65:47:FD:4C:8A:7C:30:32\"\n" +
+            "         }]\n" +
+            "      }\n" +
+            "    }\n" +
+            "]}\n" +
+            "\n"
+
+        private const val ALLOWLIST_NO_MATCH_PKG_JSON = "{\"apps\": [\n" +
+            "   {\n" +
+            "      \"type\": \"android\", \n" +
+            "      \"info\": {\n" +
+            "         \"package_name\": \"androidx.sample\",\n" +
+            "         \"signatures\" : [\n" +
+            "         {\"build\": \"release\",\n" +
+            "             \"cert_fingerprint_sha256\": \"EE:B9:3D:E4:98:2F:A1:2E:AD:5B:C1:16:7A" +
+            ":6E:10:BD:23:49:B4:04:65:C4:3A:01:CC:54:06:4D:E5:2A:38:04\"\n" +
+            "         },\n" +
+            "         {\"build\": \"ud\",\n" +
+            "         \"cert_fingerprint_sha256\": \"59:0D:2D:7B:33:6A:BD:FB:54:CD:3D:8B:" +
+            "36:8C:5C:3A:7D:22:67:5A:9A:85:9A:6A:65:47:FD:4C:8A:7C:30:32\"\n" +
+            "         }]\n" +
+            "      }\n" +
+            "    }\n" +
+            "]}\n" +
+            "\n"
+
+        private const val ALLOWLIST_NO_MATCH_CERT_JSON = "{\"apps\": [\n" +
+            "   {\n" +
+            "      \"type\": \"android\", \n" +
+            "      \"info\": {\n" +
+            "         \"package_name\": \"androidx.credentials.test\",\n" +
+            "         \"signatures\" : [\n" +
+            "         {\"build\": \"release\",\n" +
+            "             \"cert_fingerprint_sha256\": \"EE:B8:3D:E4:98:2F:A1:2E:AD:5B:C1:16:7A" +
+            ":6E:10:BD:23:49:B4:04:65:C4:3A:01:CC:54:06:4D:E5:2A:38:04\"\n" +
+            "         },\n" +
+            "         {\"build\": \"ud\",\n" +
+            "         \"cert_fingerprint_sha256\": \"59:0D:0D:7B:33:6A:BD:FB:54:CD:3D:8B:" +
+            "36:8C:5C:3A:7D:22:67:5A:9A:85:9A:6A:65:47:FD:4C:8A:7C:30:32\"\n" +
+            "         }]\n" +
+            "      }\n" +
+            "    }\n" +
+            "]}\n" +
+            "\n"
+
+        private const val ALLOWLIST_NON_ANDROID_JSON = "{\"apps\": [\n" +
+            "   {\n" +
+            "      \"type\": \"non-android\", \n" +
+            "      \"info\": {\n" +
+            "         \"package_name\": \"androidx.credentials.test\",\n" +
+            "         \"signatures\" : [\n" +
+            "         {\"build\": \"release\",\n" +
+            "             \"cert_fingerprint_sha256\": \"EE:B9:3D:E4:98:2F:A1:2E:AD:5B:C1:16:7A" +
+            ":6E:10:BD:23:49:B4:04:65:C4:3A:01:CC:54:06:4D:E5:2A:38:04\"\n" +
+            "         },\n" +
+            "         {\"build\": \"ud\",\n" +
+            "         \"cert_fingerprint_sha256\": \"59:0D:2D:7B:33:6A:BD:FB:54:CD:3D:8B:" +
+            "36:8C:5C:3A:7D:22:67:5A:9A:85:9A:6A:65:47:FD:4C:8A:7C:30:32\"\n" +
+            "         }]\n" +
+            "      }\n" +
+            "    }\n" +
+            "]}\n" +
+            "\n"
+
+        private const val ALLOWLIST_INVALID_APPS_TAG_JSON = "{\"apps2\": [\n" +
+            "   {\n" +
+            "      \"type\": \"android\", \n" +
+            "      \"info\": {\n" +
+            "         \"package_name\": \"com.example.myapp\",\n" +
+            "         \"signatures\" : [\n" +
+            "         {\"build\": \"release\",\n" +
+            "             \"cert_fingerprint_sha256\": \"59:0D:2D:7B:33:6A:BD:FB:54:CD:3D" +
+            ":8B:36:8C:5C:3A:7D:22:67:5A:9A:85:9A:6A:65:47:FD:4C:8A:7C:30:32\"\n" +
+            "         },\n" +
+            "         {\"build\": \"userdebug\",\n" +
+            "         \"cert_fingerprint_sha256\": \"59:0D:2D:7B:33:6A:BD:FB:54:CD:3D:8B:" +
+            "36:8C:5C:3A:7D:22:67:5A:9A:85:9A:6A:65:47:FD:4C:8A:7C:30:32\"\n" +
+            "         }]\n" +
+            "      }\n" +
+            "    }\n" +
+            "]}\n" +
+            "\n"
+
+        private const val ALLOWLIST_MISSING_PACKAGE_NAME_JSON = "{\"apps\": [\n" +
+            "   {\n" +
+            "      \"type\": \"android\", \n" +
+            "      \"info\": {\n" +
+            "         \"signatures\" : [\n" +
+            "         {\"build\": \"release\",\n" +
+            "             \"cert_fingerprint_sha256\": \"59:0D:2D:7B:33:6A:BD:FB:54:CD:3D" +
+            ":8B:36:8C:5C:3A:7D:22:67:5A:9A:85:9A:6A:65:47:FD:4C:8A:7C:30:32\"\n" +
+            "         },\n" +
+            "         {\"build\": \"userdebug\",\n" +
+            "         \"cert_fingerprint_sha256\": \"59:0D:2D:7B:33:6A:BD:FB:54:CD:3D:8B:" +
+            "36:8C:5C:3A:7D:22:67:5A:9A:85:9A:6A:65:47:FD:4C:8A:7C:30:32\"\n" +
+            "         }]\n" +
+            "      }\n" +
+            "    }\n" +
+            "]}\n" +
+            "\n"
+
+        @BeforeClass
+        @JvmStatic
+        fun initiateSigningInfo() {
+            val context = ApplicationProvider.getApplicationContext<Context>()
+            packageName = context.packageName
+            try {
+                val packageInfo = context.packageManager.getPackageInfo(
+                    packageName, PackageManager.GET_SIGNING_CERTIFICATES
+                )
+                signingInfo = packageInfo.signingInfo
+            } catch (_: PackageManager.NameNotFoundException) {
+            }
+        }
+    }
+
+    @Test
+    fun constructor_success() {
+        CallingAppInfo("name", SigningInfo())
+    }
+
+    @Test
+    fun constructor_success_withOrigin() {
+        val callingAppInfo = CallingAppInfo(packageName, signingInfo, ORIGIN)
+
+        assertThat(callingAppInfo.getOrigin(ALLOWLIST_VALID_JSON)).isEqualTo(ORIGIN)
+        assertThat(callingAppInfo.origin).isEqualTo(ORIGIN)
+    }
+
+    @Test
+    fun getOrigin_validReleaseCert_success() {
+        val callingAppInfo = CallingAppInfo(packageName, signingInfo, ORIGIN)
+
+        assertThat(callingAppInfo.getOrigin(ALLOWLIST_VALID_JSON))
+            .isEqualTo(ORIGIN)
+    }
+
+    @Test
+    fun getOrigin_validDebugCert_success() {
+        val callingAppInfo = CallingAppInfo(packageName, signingInfo, ORIGIN)
+
+        assertThat(callingAppInfo.getOrigin(ALLOWLIST_VALID_DEBUG_CERT_JSON))
+            .isEqualTo(ORIGIN)
+    }
+
+    @Test
+    fun constructor_fail_emptyPackageName() {
+        assertThrows(
             "Expected exception from no package name",
             IllegalArgumentException::class.java
         ) {
-            CallingAppInfo("", SigningInfo(), "origin")
+            CallingAppInfo("", signingInfo, ALLOWLIST_VALID_JSON)
         }
-     }
+    }
 
-     @Test
-     fun getOrigin_emptyPrivilegedAllowlist_throwsException() {
-         val origin = "origin"
-         val callingAppInfo = CallingAppInfo("name", SigningInfo(), origin)
+    @Test
+    fun getOrigin_emptyPrivilegedAllowlist_throwsException() {
+        val callingAppInfo = CallingAppInfo(packageName, signingInfo, ORIGIN)
 
-         assertThat(callingAppInfo.origin).isEqualTo(origin)
-         Assert.assertThrows(
-             "Expected exception from emptyPrivilegedAllowList",
-             IllegalArgumentException::class.java
-         ) {
-             callingAppInfo.getOrigin("")
-         }
-     }
+        assertThat(callingAppInfo.origin).isEqualTo(ORIGIN)
+        assertThrows(
+            "Expected exception from empty privilegedAllowList",
+            IllegalArgumentException::class.java
+        ) {
+            callingAppInfo.getOrigin("")
+        }
+    }
 
-     @Test
-     fun getOrigin_invalidJSON_throwsException() {
-         val origin = "origin"
-         val callingAppInfo = CallingAppInfo("name", SigningInfo(), origin)
+    @Test
+    fun getOrigin_missingPackageNameInAllowlist_throwsException() {
+        val callingAppInfo = CallingAppInfo(packageName, signingInfo, ORIGIN)
 
-         assertThat(callingAppInfo.origin).isEqualTo(origin)
-         Assert.assertThrows(
-             "Expected exception from emptyPrivilegedAllowList",
-             IllegalArgumentException::class.java
-         ) {
-             callingAppInfo.getOrigin("invalid_json")
-         }
-     }
- }
+        assertThat(callingAppInfo.origin).isEqualTo(ORIGIN)
+        assertThrows(
+            "Expected exception from invalid json - no package name",
+            IllegalArgumentException::class.java
+        ) {
+            callingAppInfo.getOrigin(ALLOWLIST_MISSING_PACKAGE_NAME_JSON)
+        }
+    }
+
+    @Test
+    fun getOrigin_invalidJSON_throwsException() {
+        val callingAppInfo = CallingAppInfo(packageName, signingInfo, ORIGIN)
+
+        assertThat(callingAppInfo.origin).isEqualTo(ORIGIN)
+        assertThrows(
+            "Expected exception from emptyPrivilegedAllowList",
+            IllegalArgumentException::class.java
+        ) {
+            callingAppInfo.getOrigin("invalid_json")
+        }
+    }
+
+    @Test
+    fun getOrigin_nonAndroidJSON_returnsNull() {
+        val callingAppInfo = CallingAppInfo(packageName, signingInfo, ORIGIN)
+
+        assertThat(callingAppInfo.origin).isEqualTo(ORIGIN)
+        assertThrows(
+            "Expected exception from incorrect type",
+            IllegalStateException::class.java
+        ) {
+            callingAppInfo.getOrigin(ALLOWLIST_NON_ANDROID_JSON)
+        }
+    }
+
+    @Test
+    fun getOrigin_invalidAppTagNameJSONFormat_throwsException() {
+        val callingAppInfo = CallingAppInfo(packageName, signingInfo, ORIGIN)
+
+        assertThat(callingAppInfo.origin).isEqualTo(ORIGIN)
+        assertThrows(
+            "Expected exception from incorrect apps tag",
+            IllegalArgumentException::class.java
+        ) {
+            callingAppInfo.getOrigin(ALLOWLIST_INVALID_APPS_TAG_JSON)
+        }
+    }
+
+    @Test
+    fun getOrigin_noMatchPackageName1_throwsException() {
+        val callingAppInfo = CallingAppInfo(
+            "incorrect_package_name",
+            signingInfo, ORIGIN
+        )
+
+        assertThat(callingAppInfo.origin).isEqualTo(ORIGIN)
+        assertThrows(
+            "Expected exception from no matching package name",
+            IllegalStateException::class.java
+        ) {
+            callingAppInfo.getOrigin(ALLOWLIST_VALID_JSON)
+        }
+    }
+
+    @Test
+    fun getOrigin_noMatchPackageName2_throwsException() {
+        val callingAppInfo = CallingAppInfo(
+            packageName,
+            signingInfo, ORIGIN
+        )
+
+        assertThat(callingAppInfo.origin).isEqualTo(ORIGIN)
+        assertThrows(
+            "Expected exception from no matching package name",
+            IllegalStateException::class.java
+        ) {
+            callingAppInfo.getOrigin(ALLOWLIST_NO_MATCH_PKG_JSON)
+        }
+    }
+
+    @Test
+    fun getOrigin_noMatchCert_throwsException() {
+        val callingAppInfo = CallingAppInfo(
+            packageName,
+            signingInfo, ORIGIN
+        )
+
+        assertThat(callingAppInfo.origin).isEqualTo(ORIGIN)
+        assertThrows(
+            "Expected exception from no matching cert",
+            IllegalStateException::class.java
+        ) {
+            callingAppInfo.getOrigin(ALLOWLIST_NO_MATCH_CERT_JSON)
+        }
+    }
+
+    @Test
+    fun getOrigin_noMatchCert_originNull_throwsException() {
+        val callingAppInfo = CallingAppInfo(
+            packageName,
+            signingInfo, null
+        )
+
+        assertThat(callingAppInfo.origin).isNull()
+        assertThat(callingAppInfo.getOrigin(ALLOWLIST_NO_MATCH_CERT_JSON)).isNull()
+    }
+
+    fun isOriginPopulated_originPopulated_returnsTrue() {
+        val callingAppInfo = CallingAppInfo(packageName, signingInfo, ORIGIN)
+
+        assertTrue(callingAppInfo.isOriginPopulated())
+    }
+
+    fun isOriginPopulated_originNotPopulated_returnsFalse() {
+        val callingAppInfo = CallingAppInfo(packageName, signingInfo)
+
+        assertFalse(callingAppInfo.isOriginPopulated())
+    }
+
+    fun isOriginPopulated_originPopulatedAsNull_returnsFalse() {
+        val callingAppInfo = CallingAppInfo(packageName, signingInfo, null)
+
+        assertFalse(callingAppInfo.isOriginPopulated())
+    }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CredentialProviderServiceJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CredentialProviderServiceJavaTest.java
index 175992e..e8d55f0 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CredentialProviderServiceJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CredentialProviderServiceJavaTest.java
@@ -31,6 +31,7 @@
 import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
+import java.util.List;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -70,26 +71,30 @@
         CredentialProviderServiceTestImpl service = new CredentialProviderServiceTestImpl();
         service.setTestMode(true);
 
-        BeginGetCredentialRequest request =
-                new BeginGetCredentialRequest(new ArrayList<BeginGetCredentialOption>());
-        OutcomeReceiver<
-                        androidx.credentials.provider.BeginGetCredentialResponse,
-                        androidx.credentials.exceptions.GetCredentialException>
-                outcome =
-                        new OutcomeReceiver<
-                                androidx.credentials.provider.BeginGetCredentialResponse,
-                                androidx.credentials.exceptions.GetCredentialException>() {
-                    public void onResult(
-                                    androidx.credentials.provider.BeginGetCredentialResponse
-                                            response) {}
+        android.service.credentials.BeginGetCredentialOption option =
+                new android.service.credentials.BeginGetCredentialOption(
+                        "id", "type", new Bundle());
+        List<android.service.credentials.BeginGetCredentialOption> options = new ArrayList<>();
+        options.add(option);
 
-                    public void onError(
-                                    androidx.credentials.exceptions.GetCredentialException error) {}
+        android.service.credentials.BeginGetCredentialRequest request =
+                        new android.service.credentials.BeginGetCredentialRequest.Builder()
+                .setBeginGetCredentialOptions(options).build();
+        OutcomeReceiver<
+                        android.service.credentials.BeginGetCredentialResponse,
+                        android.credentials.GetCredentialException>
+                outcome = new OutcomeReceiver<
+                        android.service.credentials.BeginGetCredentialResponse,
+                                android.credentials.GetCredentialException>() {
+                        public void onResult(
+                                android.service.credentials.BeginGetCredentialResponse response) {}
+
+                        public void onError(android.credentials.GetCredentialException error) {}
                 };
 
         // Call the service.
         assertThat(service.getLastGetRequest()).isNull();
-        service.onBeginGetCredentialRequest(request, new CancellationSignal(), outcome);
+        service.onBeginGetCredential(request, new CancellationSignal(), outcome);
         assertThat(service.getLastGetRequest()).isNotNull();
     }
 
@@ -98,21 +103,22 @@
         CredentialProviderServiceTestImpl service = new CredentialProviderServiceTestImpl();
         service.setTestMode(true);
 
-        ProviderClearCredentialStateRequest request =
-                new ProviderClearCredentialStateRequest(
-                        new CallingAppInfo("name", new SigningInfo()));
-        OutcomeReceiver<Void, androidx.credentials.exceptions.ClearCredentialException> outcome =
+        android.service.credentials.ClearCredentialStateRequest request =
+                new android.service.credentials.ClearCredentialStateRequest(
+                        new android.service.credentials.CallingAppInfo(
+                                "name", new SigningInfo()), new Bundle());
+        OutcomeReceiver<Void, android.credentials.ClearCredentialStateException> outcome =
                 new OutcomeReceiver<
-                        Void, androidx.credentials.exceptions.ClearCredentialException>() {
+                        Void, android.credentials.ClearCredentialStateException>() {
                     public void onResult(Void response) {}
 
                     public void onError(
-                            androidx.credentials.exceptions.ClearCredentialException error) {}
+                            android.credentials.ClearCredentialStateException error) {}
                 };
 
         // Call the service.
         assertThat(service.getLastClearRequest()).isNull();
-        service.onClearCredentialStateRequest(request, new CancellationSignal(), outcome);
+        service.onClearCredentialState(request, new CancellationSignal(), outcome);
         assertThat(service.getLastClearRequest()).isNotNull();
     }
 }
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CredentialProviderServiceTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CredentialProviderServiceTest.kt
index f040162..df32b01 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CredentialProviderServiceTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/CredentialProviderServiceTest.kt
@@ -42,10 +42,11 @@
 
         var request = android.service.credentials.BeginCreateCredentialRequest("test", Bundle())
         val outcome = OutcomeReceiver<android.service.credentials.BeginCreateCredentialResponse,
-                android.credentials.CreateCredentialException> {
+            android.credentials.CreateCredentialException> {
             fun onResult(response: android.service.credentials.BeginCreateCredentialResponse) {
                 Log.i(LOG_TAG, "create request: " + response.toString())
             }
+
             fun onError(error: android.credentials.CreateCredentialException) {
                 Log.e(LOG_TAG, "create request error", error)
             }
@@ -62,20 +63,24 @@
         var service = CredentialProviderServiceTestImpl()
         service.isTestMode = true
 
-        var request = BeginGetCredentialRequest(listOf<BeginGetCredentialOption>())
-        val outcome = OutcomeReceiver<androidx.credentials.provider.BeginGetCredentialResponse,
-                androidx.credentials.exceptions.GetCredentialException> {
-            fun onResult(response: androidx.credentials.provider.BeginGetCredentialResponse) {
+        var option = android.service.credentials.BeginGetCredentialOption("id", "type", Bundle())
+        var request = android.service.credentials.BeginGetCredentialRequest.Builder()
+            .setBeginGetCredentialOptions(listOf(option)).build()
+        val outcome = OutcomeReceiver<
+            android.service.credentials.BeginGetCredentialResponse,
+            android.credentials.GetCredentialException> {
+            fun onResult(response: android.service.credentials.BeginGetCredentialResponse) {
                 Log.i(LOG_TAG, "get request: " + response.toString())
             }
-            fun onError(error: androidx.credentials.exceptions.GetCredentialException) {
+
+            fun onError(error: android.credentials.GetCredentialException) {
                 Log.e(LOG_TAG, "get request error", error)
             }
         }
 
         // Call the service.
         assertThat(service.lastGetRequest).isNull()
-        service.onBeginGetCredentialRequest(request, CancellationSignal(), outcome)
+        service.onBeginGetCredential(request, CancellationSignal(), outcome)
         assertThat(service.lastGetRequest).isNotNull()
     }
 
@@ -84,20 +89,22 @@
         var service = CredentialProviderServiceTestImpl()
         service.isTestMode = true
 
-        var request = ProviderClearCredentialStateRequest(CallingAppInfo("name", SigningInfo()))
-        val outcome = OutcomeReceiver<Void?,
-                androidx.credentials.exceptions.ClearCredentialException> {
-            fun onResult(response: Void?) {
+        var request = android.service.credentials.ClearCredentialStateRequest(
+            android.service.credentials.CallingAppInfo("name", SigningInfo()), Bundle())
+        val outcome = OutcomeReceiver<Void,
+            android.credentials.ClearCredentialStateException> {
+            fun onResult(response: Void) {
                 Log.i(LOG_TAG, "clear request: " + response.toString())
             }
-            fun onError(error: androidx.credentials.exceptions.ClearCredentialException) {
+
+            fun onError(error: android.credentials.ClearCredentialStateException) {
                 Log.e(LOG_TAG, "clear request error", error)
             }
         }
 
         // Call the service.
         assertThat(service.lastClearRequest).isNull()
-        service.onClearCredentialStateRequest(request, CancellationSignal(), outcome)
+        service.onClearCredentialState(request, CancellationSignal(), outcome)
         assertThat(service.lastClearRequest).isNotNull()
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CredentialProviderFrameworkImpl.kt b/credentials/credentials/src/main/java/androidx/credentials/CredentialProviderFrameworkImpl.kt
index 2f59f17..2843f46 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CredentialProviderFrameworkImpl.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CredentialProviderFrameworkImpl.kt
@@ -29,17 +29,23 @@
 import androidx.credentials.exceptions.ClearCredentialUnknownException
 import androidx.credentials.exceptions.ClearCredentialUnsupportedException
 import androidx.credentials.exceptions.CreateCredentialCancellationException
+import androidx.credentials.exceptions.CreateCredentialCustomException
 import androidx.credentials.exceptions.CreateCredentialException
 import androidx.credentials.exceptions.CreateCredentialInterruptedException
 import androidx.credentials.exceptions.CreateCredentialNoCreateOptionException
 import androidx.credentials.exceptions.CreateCredentialUnknownException
 import androidx.credentials.exceptions.CreateCredentialUnsupportedException
 import androidx.credentials.exceptions.GetCredentialCancellationException
+import androidx.credentials.exceptions.GetCredentialCustomException
 import androidx.credentials.exceptions.GetCredentialException
 import androidx.credentials.exceptions.GetCredentialInterruptedException
 import androidx.credentials.exceptions.GetCredentialUnknownException
 import androidx.credentials.exceptions.GetCredentialUnsupportedException
 import androidx.credentials.exceptions.NoCredentialException
+import androidx.credentials.exceptions.publickeycredential.CreatePublicKeyCredentialDomException
+import androidx.credentials.exceptions.publickeycredential.CreatePublicKeyCredentialException
+import androidx.credentials.exceptions.publickeycredential.GetPublicKeyCredentialDomException
+import androidx.credentials.exceptions.publickeycredential.GetPublicKeyCredentialException
 import androidx.credentials.internal.FrameworkImplHelper
 import java.util.concurrent.Executor
 
@@ -215,7 +221,6 @@
                     FrameworkImplHelper.getFinalCreateCredentialData(request, context),
                     request.candidateQueryData)
                 .setIsSystemProviderRequired(request.isSystemProviderRequired)
-                // TODO("change to taking value from the request when ready")
                 .setAlwaysSendAppInfoToProvider(true)
         setOriginForCreateRequest(request, createCredentialRequestBuilder)
         return createCredentialRequestBuilder.build()
@@ -236,16 +241,6 @@
         val builder = android.credentials.GetCredentialRequest.Builder(
             GetCredentialRequest.toRequestDataBundle(request))
         request.credentialOptions.forEach {
-            // TODO(b/278308121): clean up the temporary bundle value injection after the Beta 2
-            // release.
-            if (request.preferImmediatelyAvailableCredentials &&
-                it is GetPublicKeyCredentialOption) {
-                it.requestData.putBoolean(
-                    "androidx.credentials.BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS",
-                    true,
-                )
-            }
-
             builder.addCredentialOption(
                 android.credentials.CredentialOption.Builder(
                     it.type, it.requestData, it.candidateQueryData
@@ -275,6 +270,7 @@
 
     internal fun convertToJetpackGetException(error: android.credentials.GetCredentialException):
         GetCredentialException {
+
         return when (error.type) {
             android.credentials.GetCredentialException.TYPE_NO_CREDENTIAL ->
                 NoCredentialException(error.message)
@@ -285,7 +281,16 @@
             android.credentials.GetCredentialException.TYPE_INTERRUPTED ->
                 GetCredentialInterruptedException(error.message)
 
-            else -> GetCredentialUnknownException(error.message)
+            android.credentials.GetCredentialException.TYPE_UNKNOWN ->
+                GetCredentialUnknownException(error.message)
+
+            else -> {
+                if (error.type.startsWith(GET_DOM_EXCEPTION_PREFIX)) {
+                    GetPublicKeyCredentialException.createFrom(error.type, error.message)
+                } else {
+                    GetCredentialCustomException(error.type, error.message)
+                }
+            }
         }
     }
 
@@ -302,7 +307,16 @@
             android.credentials.CreateCredentialException.TYPE_INTERRUPTED ->
                 CreateCredentialInterruptedException(error.message)
 
-            else -> CreateCredentialUnknownException(error.message)
+            android.credentials.CreateCredentialException.TYPE_UNKNOWN ->
+                CreateCredentialUnknownException(error.message)
+
+            else -> {
+                if (error.type.startsWith(CREATE_DOM_EXCEPTION_PREFIX)) {
+                    CreatePublicKeyCredentialException.createFrom(error.type, error.message)
+                } else {
+                    CreateCredentialCustomException(error.type, error.message)
+                }
+            }
         }
     }
 
@@ -373,5 +387,10 @@
 
     private companion object {
         private const val TAG = "CredManProvService"
+
+        private const val GET_DOM_EXCEPTION_PREFIX =
+            GetPublicKeyCredentialDomException.TYPE_GET_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION
+        private const val CREATE_DOM_EXCEPTION_PREFIX =
+            CreatePublicKeyCredentialDomException.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/ClearCustomCredentialException.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/ClearCredentialCustomException.kt
similarity index 95%
rename from credentials/credentials/src/main/java/androidx/credentials/exceptions/ClearCustomCredentialException.kt
rename to credentials/credentials/src/main/java/androidx/credentials/exceptions/ClearCredentialCustomException.kt
index 2715172..3519fb9 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/ClearCustomCredentialException.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/ClearCredentialCustomException.kt
@@ -30,7 +30,7 @@
  * @throws IllegalArgumentException If [type] is empty
  * @throws NullPointerException If [type] is null
  */
-class ClearCustomCredentialException @JvmOverloads constructor(
+class ClearCredentialCustomException @JvmOverloads constructor(
     override val type: String,
     errorMessage: CharSequence? = null
 ) : ClearCredentialException(type, errorMessage) {
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/CreateCustomCredentialException.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/CreateCredentialCustomException.kt
similarity index 95%
rename from credentials/credentials/src/main/java/androidx/credentials/exceptions/CreateCustomCredentialException.kt
rename to credentials/credentials/src/main/java/androidx/credentials/exceptions/CreateCredentialCustomException.kt
index bd995fe3..46e4045 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/CreateCustomCredentialException.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/CreateCredentialCustomException.kt
@@ -30,7 +30,7 @@
  * @throws IllegalArgumentException If [type] is empty
  * @throws NullPointerException If [type] is null
  */
-class CreateCustomCredentialException @JvmOverloads constructor(
+class CreateCredentialCustomException @JvmOverloads constructor(
     override val type: String,
     errorMessage: CharSequence? = null
 ) : CreateCredentialException(type, errorMessage) {
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/GetCustomCredentialException.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/GetCredentialCustomException.kt
similarity index 95%
rename from credentials/credentials/src/main/java/androidx/credentials/exceptions/GetCustomCredentialException.kt
rename to credentials/credentials/src/main/java/androidx/credentials/exceptions/GetCredentialCustomException.kt
index 9f42bb7..2c22b5e 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/GetCustomCredentialException.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/GetCredentialCustomException.kt
@@ -30,7 +30,7 @@
  * @throws IllegalArgumentException If [type] is empty
  * @throws NullPointerException If [type] is null
  */
-class GetCustomCredentialException @JvmOverloads constructor(
+class GetCredentialCustomException @JvmOverloads constructor(
     override val type: String,
     errorMessage: CharSequence? = null
 ) : GetCredentialException(type, errorMessage) {
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/AbortError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/AbortError.kt
index 6981ab1..d23ce5f 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/AbortError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/AbortError.kt
@@ -26,6 +26,6 @@
     DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_ABORT_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_ABORT_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_ABORT_ERROR"
+            "androidx.credentials.TYPE_ABORT_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/ConstraintError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/ConstraintError.kt
index 3d8bf782..2943f84 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/ConstraintError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/ConstraintError.kt
@@ -27,6 +27,6 @@
     DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_CONSTRAINT_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_CONSTRAINT_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_CONSTRAINT_ERROR"
+            "androidx.credentials.TYPE_CONSTRAINT_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/DataCloneError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/DataCloneError.kt
index a5a3a29..2203405 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/DataCloneError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/DataCloneError.kt
@@ -27,6 +27,6 @@
     DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_DATA_CLONE_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_DATA_CLONE_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_DATA_CLONE_ERROR"
+            "androidx.credentials.TYPE_DATA_CLONE_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/DataError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/DataError.kt
index 15f2037..3a34b42 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/DataError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/DataError.kt
@@ -27,6 +27,6 @@
 class DataError : DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_DATA_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_DATA_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_DATA_ERROR"
+            "androidx.credentials.TYPE_DATA_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/EncodingError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/EncodingError.kt
index 05c23cb..4279d69 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/EncodingError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/EncodingError.kt
@@ -27,6 +27,6 @@
     DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_ENCODING_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_ENCODING_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_ENCODING_ERROR"
+            "androidx.credentials.TYPE_ENCODING_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/HierarchyRequestError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/HierarchyRequestError.kt
index f676b25..2697895 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/HierarchyRequestError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/HierarchyRequestError.kt
@@ -27,6 +27,6 @@
     DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_HIERARCHY_REQUEST_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_HIERARCHY_REQUEST_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_HIERARCHY_REQUEST_ERROR"
+            "androidx.credentials.TYPE_HIERARCHY_REQUEST_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/InUseAttributeError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/InUseAttributeError.kt
index 56de3f6..c1b192c 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/InUseAttributeError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/InUseAttributeError.kt
@@ -27,6 +27,6 @@
     DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_IN_USE_ATTRIBUTE_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_IN_USE_ATTRIBUTE_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_IN_USE_ATTRIBUTE_ERROR"
+            "androidx.credentials.TYPE_IN_USE_ATTRIBUTE_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/InvalidCharacterError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/InvalidCharacterError.kt
index e35872e..eb47791 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/InvalidCharacterError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/InvalidCharacterError.kt
@@ -27,6 +27,6 @@
     DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_INVALID_CHARACTER_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_INVALID_CHARACTER_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_INVALID_CHARACTER_ERROR"
+            "androidx.credentials.TYPE_INVALID_CHARACTER_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/InvalidModificationError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/InvalidModificationError.kt
index 29a6ff8..f340995 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/InvalidModificationError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/InvalidModificationError.kt
@@ -27,6 +27,6 @@
     DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_INVALID_MODIFICATION_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_INVALID_MODIFICATION_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_INVALID_MODIFICATION_ERROR"
+            "androidx.credentials.TYPE_INVALID_MODIFICATION_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/InvalidNodeTypeError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/InvalidNodeTypeError.kt
index c5dd16c..c1eaebe 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/InvalidNodeTypeError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/InvalidNodeTypeError.kt
@@ -27,6 +27,6 @@
     DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_INVALID_NODE_TYPE_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_INVALID_NODE_TYPE_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_INVALID_NODE_TYPE_ERROR"
+            "androidx.credentials.TYPE_INVALID_NODE_TYPE_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/InvalidStateError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/InvalidStateError.kt
index d9a6d3e..79853da 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/InvalidStateError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/InvalidStateError.kt
@@ -27,6 +27,6 @@
     DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_INVALID_STATE_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_INVALID_STATE_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_INVALID_STATE_ERROR"
+            "androidx.credentials.TYPE_INVALID_STATE_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/NamespaceError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/NamespaceError.kt
index 0a17765..22fc31d 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/NamespaceError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/NamespaceError.kt
@@ -27,6 +27,6 @@
     DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_NAMESPACE_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_NAMESPACE_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_NAMESPACE_ERROR"
+            "androidx.credentials.TYPE_NAMESPACE_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/NetworkError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/NetworkError.kt
index 8f1b697..c2b7aaf 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/NetworkError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/NetworkError.kt
@@ -26,6 +26,6 @@
     DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_NETWORK_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_NETWORK_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_NETWORK_ERROR"
+            "androidx.credentials.TYPE_NETWORK_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/NoModificationAllowedError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/NoModificationAllowedError.kt
index 0186cf4..36da1e7 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/NoModificationAllowedError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/NoModificationAllowedError.kt
@@ -27,6 +27,6 @@
     DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_NO_MODIFICATION_ALLOWED_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_NO_MODIFICATION_ALLOWED_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_NO_MODIFICATION_ALLOWED_ERROR"
+            "androidx.credentials.TYPE_NO_MODIFICATION_ALLOWED_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/NotAllowedError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/NotAllowedError.kt
index e612ba2..78538e0 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/NotAllowedError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/NotAllowedError.kt
@@ -28,6 +28,6 @@
     DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_NOT_ALLOWED_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_NOT_ALLOWED_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_NOT_ALLOWED_ERROR"
+            "androidx.credentials.TYPE_NOT_ALLOWED_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/NotFoundError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/NotFoundError.kt
index 25d974c..3236283 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/NotFoundError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/NotFoundError.kt
@@ -27,6 +27,6 @@
     DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_NOT_FOUND_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_NOT_FOUND_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_NOT_FOUND_ERROR"
+            "androidx.credentials.TYPE_NOT_FOUND_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/NotReadableError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/NotReadableError.kt
index 43c30d0..b07b868 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/NotReadableError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/NotReadableError.kt
@@ -27,6 +27,6 @@
     DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_NOT_READABLE_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_NOT_READABLE_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_NOT_READABLE_ERROR"
+            "androidx.credentials.TYPE_NOT_READABLE_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/NotSupportedError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/NotSupportedError.kt
index 3a25e04..c2a3202 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/NotSupportedError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/NotSupportedError.kt
@@ -27,6 +27,6 @@
     DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_NOT_SUPPORTED_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_NOT_SUPPORTED_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_NOT_SUPPORTED_ERROR"
+            "androidx.credentials.TYPE_NOT_SUPPORTED_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/OperationError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/OperationError.kt
index f55f92d..ec738e1 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/OperationError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/OperationError.kt
@@ -27,6 +27,6 @@
     DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_OPERATION_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_OPERATION_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_OPERATION_ERROR"
+            "androidx.credentials.TYPE_OPERATION_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/OptOutError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/OptOutError.kt
index e45d315..37c4a0b 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/OptOutError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/OptOutError.kt
@@ -27,6 +27,6 @@
     DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_OPT_OUT_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_OPT_OUT_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_OPT_OUT_ERROR"
+            "androidx.credentials.TYPE_OPT_OUT_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/QuotaExceededError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/QuotaExceededError.kt
index c00f181..21bc479 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/QuotaExceededError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/QuotaExceededError.kt
@@ -27,6 +27,6 @@
     DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_QUOTA_EXCEEDED_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_QUOTA_EXCEEDED_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_QUOTA_EXCEEDED_ERROR"
+            "androidx.credentials.TYPE_QUOTA_EXCEEDED_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/ReadOnlyError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/ReadOnlyError.kt
index 18aedb5..fe4d3e9 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/ReadOnlyError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/ReadOnlyError.kt
@@ -27,6 +27,6 @@
     DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_READ_ONLY_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_READ_ONLY_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_READ_ONLY_ERROR"
+            "androidx.credentials.TYPE_READ_ONLY_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/SecurityError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/SecurityError.kt
index d970890..6358dfc 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/SecurityError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/SecurityError.kt
@@ -27,6 +27,6 @@
     DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_SECURITY_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_SECURITY_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_SECURITY_ERROR"
+            "androidx.credentials.TYPE_SECURITY_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/SyntaxError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/SyntaxError.kt
index ef01d77..77145cd 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/SyntaxError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/SyntaxError.kt
@@ -27,6 +27,6 @@
     DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_SYNTAX_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_SYNTAX_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_SYNTAX_ERROR"
+            "androidx.credentials.TYPE_SYNTAX_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/TimeoutError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/TimeoutError.kt
index bcca5b4..8ba6a1e0 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/TimeoutError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/TimeoutError.kt
@@ -26,6 +26,6 @@
     DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_TIMEOUT_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_TIMEOUT_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_TIMEOUT_ERROR"
+            "androidx.credentials.TYPE_TIMEOUT_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/TransactionInactiveError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/TransactionInactiveError.kt
index fd1dbcd..8464c4a 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/TransactionInactiveError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/TransactionInactiveError.kt
@@ -27,6 +27,6 @@
     DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_TRANSACTION_INACTIVE_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_TRANSACTION_INACTIVE_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_TRANSACTION_INACTIVE_ERROR"
+            "androidx.credentials.TYPE_TRANSACTION_INACTIVE_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/UnknownError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/UnknownError.kt
index 05f4998..beb4212 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/UnknownError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/UnknownError.kt
@@ -27,6 +27,6 @@
     DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_UNKNOWN_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_UNKNOWN_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_UNKNOWN_ERROR"
+            "androidx.credentials.TYPE_UNKNOWN_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/VersionError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/VersionError.kt
index 39b12f1..33d4975 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/VersionError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/VersionError.kt
@@ -27,6 +27,6 @@
     DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_VERSION_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_VERSION_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_VERSION_ERROR"
+            "androidx.credentials.TYPE_VERSION_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/WrongDocumentError.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/WrongDocumentError.kt
index 0040020..25fced2 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/WrongDocumentError.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/domerrors/WrongDocumentError.kt
@@ -27,6 +27,6 @@
     DomError(TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_WRONG_DOCUMENT_ERROR) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_WRONG_DOCUMENT_ERROR: String =
-            "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_WRONG_DOCUMENT_ERROR"
+            "androidx.credentials.TYPE_WRONG_DOCUMENT_ERROR"
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/publickeycredential/CreatePublicKeyCredentialDomException.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/publickeycredential/CreatePublicKeyCredentialDomException.kt
index 8741608..584247b 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/publickeycredential/CreatePublicKeyCredentialDomException.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/publickeycredential/CreatePublicKeyCredentialDomException.kt
@@ -16,7 +16,13 @@
 
 package androidx.credentials.exceptions.publickeycredential
 
+import androidx.annotation.RestrictTo
+import androidx.credentials.exceptions.CreateCredentialCustomException
+import androidx.credentials.exceptions.CreateCredentialException
 import androidx.credentials.exceptions.domerrors.DomError
+import androidx.credentials.exceptions.domerrors.UnknownError
+import androidx.credentials.exceptions.publickeycredential.DomExceptionUtils.Companion.SEPARATOR
+import androidx.credentials.internal.FrameworkClassParsingException
 
 /**
  * During the create-passkey flow, this is thrown when a DOM Exception is thrown,
@@ -32,10 +38,23 @@
     val domError: DomError,
     errorMessage: CharSequence? = null
 ) : CreatePublicKeyCredentialException(
-    TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION + domError.type,
+    TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION + SEPARATOR + domError.type,
     errorMessage) {
     internal companion object {
         internal const val TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION: String =
             "androidx.credentials.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION"
+        @JvmStatic
+        @RestrictTo(RestrictTo.Scope.LIBRARY) // used from java tests
+        fun createFrom(type: String, msg: String?): CreateCredentialException {
+            val prefix = "$TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION$SEPARATOR"
+            return try {
+                DomExceptionUtils.generateDomException(type, prefix, msg,
+                    CreatePublicKeyCredentialDomException(UnknownError()))
+            } catch (t: FrameworkClassParsingException) {
+                // Parsing failed but don't crash the process. Instead just output a response
+                // with the raw framework values.
+                CreateCredentialCustomException(type, msg)
+            }
+        }
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/publickeycredential/CreatePublicKeyCredentialException.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/publickeycredential/CreatePublicKeyCredentialException.kt
index 3874164..9b8367f 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/publickeycredential/CreatePublicKeyCredentialException.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/publickeycredential/CreatePublicKeyCredentialException.kt
@@ -18,7 +18,9 @@
 
 import androidx.annotation.RestrictTo
 import androidx.credentials.CredentialManager
+import androidx.credentials.exceptions.CreateCredentialCustomException
 import androidx.credentials.exceptions.CreateCredentialException
+import androidx.credentials.internal.FrameworkClassParsingException
 
 /**
  * A subclass of CreateCredentialException for unique exceptions thrown specific only to
@@ -36,4 +38,25 @@
     init {
         require(type.isNotEmpty()) { "type must not be empty" }
     }
+
+    internal companion object {
+        @JvmStatic
+        @RestrictTo(RestrictTo.Scope.LIBRARY) // used from java tests
+        fun createFrom(type: String, msg: String?): CreateCredentialException {
+            return try {
+                with(type) {
+                    when {
+                        contains(CreatePublicKeyCredentialDomException
+                            .TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION) ->
+                            CreatePublicKeyCredentialDomException.createFrom(type, msg)
+                        else -> { throw FrameworkClassParsingException() }
+                    }
+                }
+            } catch (t: FrameworkClassParsingException) {
+                // Parsing failed but don't crash the process. Instead just output a response
+                // with the raw framework values.
+                CreateCredentialCustomException(type, msg)
+            }
+        }
+    }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/publickeycredential/DomExceptionUtils.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/publickeycredential/DomExceptionUtils.kt
new file mode 100644
index 0000000..b9cf8ae
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/publickeycredential/DomExceptionUtils.kt
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.exceptions.publickeycredential
+
+import androidx.annotation.RestrictTo
+import androidx.credentials.exceptions.domerrors.AbortError
+import androidx.credentials.exceptions.domerrors.ConstraintError
+import androidx.credentials.exceptions.domerrors.DataCloneError
+import androidx.credentials.exceptions.domerrors.DataError
+import androidx.credentials.exceptions.domerrors.DomError
+import androidx.credentials.exceptions.domerrors.EncodingError
+import androidx.credentials.exceptions.domerrors.HierarchyRequestError
+import androidx.credentials.exceptions.domerrors.InUseAttributeError
+import androidx.credentials.exceptions.domerrors.InvalidCharacterError
+import androidx.credentials.exceptions.domerrors.InvalidModificationError
+import androidx.credentials.exceptions.domerrors.InvalidNodeTypeError
+import androidx.credentials.exceptions.domerrors.InvalidStateError
+import androidx.credentials.exceptions.domerrors.NamespaceError
+import androidx.credentials.exceptions.domerrors.NetworkError
+import androidx.credentials.exceptions.domerrors.NoModificationAllowedError
+import androidx.credentials.exceptions.domerrors.NotAllowedError
+import androidx.credentials.exceptions.domerrors.NotFoundError
+import androidx.credentials.exceptions.domerrors.NotReadableError
+import androidx.credentials.exceptions.domerrors.NotSupportedError
+import androidx.credentials.exceptions.domerrors.OperationError
+import androidx.credentials.exceptions.domerrors.OptOutError
+import androidx.credentials.exceptions.domerrors.QuotaExceededError
+import androidx.credentials.exceptions.domerrors.ReadOnlyError
+import androidx.credentials.exceptions.domerrors.SecurityError
+import androidx.credentials.exceptions.domerrors.SyntaxError
+import androidx.credentials.exceptions.domerrors.TimeoutError
+import androidx.credentials.exceptions.domerrors.TransactionInactiveError
+import androidx.credentials.exceptions.domerrors.UnknownError
+import androidx.credentials.exceptions.domerrors.VersionError
+import androidx.credentials.exceptions.domerrors.WrongDocumentError
+import androidx.credentials.internal.FrameworkClassParsingException
+
+/**
+ * An internal class that parses dom exceptions originating from providers.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+internal class DomExceptionUtils {
+    companion object {
+
+        const val SEPARATOR = "/"
+
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        internal inline fun <reified T>
+            generateDomException(type: String, prefix: String, msg: String?, t: T): T {
+
+            return when (type) {
+                prefix + AbortError.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_ABORT_ERROR ->
+                    generateException(AbortError(), msg, t)
+                prefix + ConstraintError.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_CONSTRAINT_ERROR ->
+                    generateException(ConstraintError(), msg, t)
+                prefix + DataCloneError.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_DATA_CLONE_ERROR ->
+                    generateException(DataCloneError(), msg, t)
+                prefix + DataError.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_DATA_ERROR ->
+                    generateException(DataError(), msg, t)
+                prefix + EncodingError.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_ENCODING_ERROR ->
+                    generateException(EncodingError(), msg, t)
+                prefix + HierarchyRequestError
+                    .TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_HIERARCHY_REQUEST_ERROR ->
+                    generateException(HierarchyRequestError(), msg, t)
+                prefix + InUseAttributeError
+                    .TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_IN_USE_ATTRIBUTE_ERROR ->
+                    generateException(InUseAttributeError(), msg, t)
+                prefix + InvalidCharacterError
+                    .TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_INVALID_CHARACTER_ERROR ->
+                    generateException(InvalidCharacterError(), msg, t)
+                prefix + InvalidModificationError
+                    .TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_INVALID_MODIFICATION_ERROR ->
+                    generateException(InvalidModificationError(), msg, t)
+                prefix + InvalidNodeTypeError
+                    .TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_INVALID_NODE_TYPE_ERROR ->
+                    generateException(InvalidNodeTypeError(), msg, t)
+                prefix + InvalidStateError
+                    .TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_INVALID_STATE_ERROR ->
+                    generateException(InvalidStateError(), msg, t)
+                prefix + NamespaceError.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_NAMESPACE_ERROR ->
+                    generateException(NamespaceError(), msg, t)
+                prefix + NetworkError.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_NETWORK_ERROR ->
+                    generateException(NetworkError(), msg, t)
+                prefix + NoModificationAllowedError
+                    .TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_NO_MODIFICATION_ALLOWED_ERROR ->
+                    generateException(NoModificationAllowedError(), msg, t)
+                prefix + NotAllowedError.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_NOT_ALLOWED_ERROR ->
+                    generateException(NotAllowedError(), msg, t)
+                prefix + NotFoundError.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_NOT_FOUND_ERROR ->
+                    generateException(NotFoundError(), msg, t)
+                prefix + NotReadableError
+                    .TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_NOT_READABLE_ERROR ->
+                    generateException(NotReadableError(), msg, t)
+                prefix + NotSupportedError
+                    .TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_NOT_SUPPORTED_ERROR ->
+                    generateException(NotSupportedError(), msg, t)
+                prefix + OperationError.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_OPERATION_ERROR ->
+                    generateException(OperationError(), msg, t)
+                prefix + OptOutError.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_OPT_OUT_ERROR ->
+                    generateException(OptOutError(), msg, t)
+                prefix + QuotaExceededError
+                    .TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_QUOTA_EXCEEDED_ERROR ->
+                    generateException(QuotaExceededError(), msg, t)
+                prefix + ReadOnlyError.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_READ_ONLY_ERROR ->
+                    generateException(ReadOnlyError(), msg, t)
+                prefix + SecurityError.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_SECURITY_ERROR ->
+                    generateException(SecurityError(), msg, t)
+                prefix + SyntaxError.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_SYNTAX_ERROR ->
+                    generateException(SyntaxError(), msg, t)
+                prefix + TimeoutError.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_TIMEOUT_ERROR ->
+                    generateException(TimeoutError(), msg, t)
+                prefix + TransactionInactiveError
+                    .TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_TRANSACTION_INACTIVE_ERROR ->
+                    generateException(TransactionInactiveError(), msg, t)
+                prefix + UnknownError.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_UNKNOWN_ERROR ->
+                    generateException(UnknownError(), msg, t)
+                prefix + VersionError.TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_VERSION_ERROR ->
+                    generateException(VersionError(), msg, t)
+                prefix + WrongDocumentError
+                    .TYPE_CREATE_PUBLIC_KEY_CREDENTIAL_WRONG_DOCUMENT_ERROR ->
+                    generateException(WrongDocumentError(), msg, t)
+                else -> throw FrameworkClassParsingException()
+            }
+        }
+
+        @Suppress("UNCHECKED_CAST") // Checked, worst case we throw a parsing exception
+        private fun <T> generateException(domError: DomError, msg: String?, t: T): T {
+            return when (t) {
+                is CreatePublicKeyCredentialDomException -> {
+                    CreatePublicKeyCredentialDomException(domError, msg) as T
+                }
+                is GetPublicKeyCredentialDomException -> {
+                    GetPublicKeyCredentialDomException(domError, msg) as T
+                }
+                else -> {
+                    throw FrameworkClassParsingException()
+                }
+            }
+        }
+    }
+}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/publickeycredential/GetPublicKeyCredentialDomException.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/publickeycredential/GetPublicKeyCredentialDomException.kt
index e2684ce..e49dd71 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/publickeycredential/GetPublicKeyCredentialDomException.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/publickeycredential/GetPublicKeyCredentialDomException.kt
@@ -16,7 +16,13 @@
 
 package androidx.credentials.exceptions.publickeycredential
 
+import androidx.annotation.RestrictTo
+import androidx.credentials.exceptions.GetCredentialCustomException
+import androidx.credentials.exceptions.GetCredentialException
 import androidx.credentials.exceptions.domerrors.DomError
+import androidx.credentials.exceptions.domerrors.UnknownError
+import androidx.credentials.exceptions.publickeycredential.DomExceptionUtils.Companion.SEPARATOR
+import androidx.credentials.internal.FrameworkClassParsingException
 
 /**
  * During the get-passkey flow, this is thrown when a DOM Exception is thrown,
@@ -32,10 +38,25 @@
     val domError: DomError,
     errorMessage: CharSequence? = null
 ) : GetPublicKeyCredentialException(
-    TYPE_GET_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION + domError.type,
+    TYPE_GET_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION + SEPARATOR + domError.type,
     errorMessage) {
     internal companion object {
         internal const val TYPE_GET_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION: String =
             "androidx.credentials.TYPE_GET_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION"
+
+        @JvmStatic
+        @RestrictTo(RestrictTo.Scope.LIBRARY) // used from java tests
+        fun createFrom(type: String, msg: String?): GetCredentialException {
+            val prefix =
+                "$TYPE_GET_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION$SEPARATOR"
+            return try {
+                DomExceptionUtils.generateDomException(type, prefix, msg,
+                    GetPublicKeyCredentialDomException(UnknownError()))
+            } catch (t: FrameworkClassParsingException) {
+                // Parsing failed but don't crash the process. Instead just output a response
+                // with the raw framework values.
+                GetCredentialCustomException(type, msg)
+            }
+        }
     }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/exceptions/publickeycredential/GetPublicKeyCredentialException.kt b/credentials/credentials/src/main/java/androidx/credentials/exceptions/publickeycredential/GetPublicKeyCredentialException.kt
index 3cb9e63..46258f6 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/exceptions/publickeycredential/GetPublicKeyCredentialException.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/exceptions/publickeycredential/GetPublicKeyCredentialException.kt
@@ -18,7 +18,9 @@
 
 import androidx.annotation.RestrictTo
 import androidx.credentials.CredentialManager
+import androidx.credentials.exceptions.GetCredentialCustomException
 import androidx.credentials.exceptions.GetCredentialException
+import androidx.credentials.internal.FrameworkClassParsingException
 
 /**
  * A subclass of CreateCredentialException for unique exceptions thrown specific only to
@@ -36,4 +38,25 @@
     init {
         require(type.isNotEmpty()) { "type must not be empty" }
     }
+
+    internal companion object {
+        @JvmStatic
+        @RestrictTo(RestrictTo.Scope.LIBRARY) // used from java tests
+        fun createFrom(type: String, msg: String?): GetCredentialException {
+            return try {
+               with(type) {
+                   when {
+                       startsWith(GetPublicKeyCredentialDomException
+                           .TYPE_GET_PUBLIC_KEY_CREDENTIAL_DOM_EXCEPTION) ->
+                           GetPublicKeyCredentialDomException.createFrom(type, msg)
+                       else -> { throw FrameworkClassParsingException() }
+                   }
+               }
+            } catch (t: FrameworkClassParsingException) {
+                // Parsing failed but don't crash the process. Instead just output a response
+                // with the raw framework values.
+                GetCredentialCustomException(type, msg)
+            }
+        }
+    }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/Action.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/Action.kt
index 7fa6e48..c314e36 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/provider/Action.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/Action.kt
@@ -57,8 +57,10 @@
  *
  * @param title the title of the entry
  * @param pendingIntent the [PendingIntent] that will get invoked when the user selects this
- * authentication entry on the UI, must be created with flag [PendingIntent.FLAG_MUTABLE] so
- * that the system can add the complete request to the extras of the associated intent
+ * entry, must be created with a unique request code per entry,
+ * with flag [PendingIntent.FLAG_MUTABLE] to allow the Android system to attach the
+ * final request, and NOT with flag [PendingIntent.FLAG_ONE_SHOT] as it can be invoked multiple
+ * times
  * @param subtitle the optional subtitle that is displayed on the entry
  *
  * @see android.service.credentials.BeginGetCredentialResponse for usage.
@@ -80,8 +82,11 @@
      * A builder for [Action]
      *
      * @param title the title of this action entry
-     * @param pendingIntent the [PendingIntent] that will be fired when the user selects
-     * this action entry
+     * @param pendingIntent the [PendingIntent] that will get invoked when the user selects this
+     * entry, must be created with a unique request code per entry,
+     * with flag [PendingIntent.FLAG_MUTABLE] to allow the Android system to attach the
+     * final request, and NOT with flag [PendingIntent.FLAG_ONE_SHOT] as it can be invoked multiple
+     * times
      */
     class Builder constructor(
         private val title: CharSequence,
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/AuthenticationAction.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/AuthenticationAction.kt
index beb3038..857735d 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/provider/AuthenticationAction.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/AuthenticationAction.kt
@@ -51,8 +51,10 @@
  *
  * @param title the title to be shown with this entry on the account selector UI
  * @param pendingIntent the [PendingIntent] that will get invoked when the user selects this
- * authentication entry on the UI, must be created with flag [PendingIntent.FLAG_MUTABLE] so
- * that the system can add the complete request to the extras of the associated intent
+ * entry, must be created with a unique request code per entry,
+ * with flag [PendingIntent.FLAG_MUTABLE] to allow the Android system to attach the
+ * final request, and NOT with flag [PendingIntent.FLAG_ONE_SHOT] as it can be invoked multiple
+ * times
  *
  * @see android.service.credentials.BeginGetCredentialResponse
  * for more usage details.
@@ -72,8 +74,11 @@
      * A builder for [AuthenticationAction]
      *
      * @param title the title to be displayed with this authentication action entry
-     * @param pendingIntent the [PendingIntent] that will be fired when the user selects
-     * this entry
+     * @param pendingIntent the [PendingIntent] that will get invoked when the user selects this
+     * entry, must be created with a unique request code per entry,
+     * with flag [PendingIntent.FLAG_MUTABLE] to allow the Android system to attach the
+     * final request, and NOT with flag [PendingIntent.FLAG_ONE_SHOT] as it can be invoked multiple
+     * times
      */
     class Builder constructor(
         private val title: CharSequence,
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/CallingAppInfo.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/CallingAppInfo.kt
index 6109c2f..38b38c7 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/provider/CallingAppInfo.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/CallingAppInfo.kt
@@ -16,9 +16,16 @@
 
 package androidx.credentials.provider
 
+import android.content.pm.Signature
 import android.content.pm.SigningInfo
+import android.os.Build
+import androidx.annotation.RequiresApi
 import androidx.annotation.RestrictTo
+import androidx.credentials.provider.utils.PrivilegedApp
 import androidx.credentials.provider.utils.RequestValidationUtil
+import java.security.MessageDigest
+import org.json.JSONException
+import org.json.JSONObject
 
 /**
  * Information pertaining to the calling application.
@@ -42,6 +49,9 @@
     @get:RestrictTo(RestrictTo.Scope.LIBRARY)
     val origin: String? = null
 ) {
+    internal companion object {
+        private const val TAG = "CallingAppInfo"
+    }
 
     /**
      * Returns the origin of the calling app. This is only non-null if a
@@ -51,21 +61,127 @@
      * Additionally, in order to get the origin, the credential provider must
      * provide an allowlist of privileged browsers/apps that it trusts.
      * This allowlist must be in the form of a valid, non-empty JSON. The
-     * origin will only be returned if the [packageName] and [signingInfo]
-     * match with an app allowlisted in [privilegedAllowlist].
+     * origin will only be returned if the [packageName] and the fingerprints of certificates
+     * obtained from the [signingInfo] match with that of an app allowlisted
+     * in [privilegedAllowlist]. The format of this JSON must adhere to the following sample.
+     *
+     * {"apps": [
+     *    {
+     *       "type": "android",
+     *       "info": {
+     *          "package_name": "com.example.myapp",
+     *          "signatures" : [
+     *          {"build": "release",
+     *              "cert_fingerprint_sha256": "59:0D:2D:7B:33:6A:BD:FB:54:CD:3D:8B:36:8C:5C:3A:
+     *              7D:22:67:5A:9A:85:9A:6A:65:47:FD:4C:8A:7C:30:32"
+     *          },
+     *          {"build": "userdebug",
+     *          "cert_fingerprint_sha256": "59:0D:2D:7B:33:6A:BD:FB:54:CD:3D:8B:36:8C:5C:3A:7D:
+     *          22:67:5A:9A:85:9A:6A:65:47:FD:4C:8A:7C:30:32"
+     *          }]
+     *       }
+     *     }
+     * ]}
+     *
+     * All keys in the JSON must be exactly as stated in the sample above. Note that if the build
+     * for a given fingerprint is specified as 'userdebug', that fingerprint will
+     * only be considered if the device is on a 'userdebug' build, as determined by [Build.TYPE].
      *
      * @throws IllegalArgumentException If [privilegedAllowlist] is empty, or an
-     * invalid JSON
+     * invalid JSON, or does not follow the format detailed above
+     * @throws IllegalStateException If the origin is non-null, but the [packageName] and
+     * [signingInfo] do not have a match in the [privilegedAllowlist]
      */
     fun getOrigin(privilegedAllowlist: String): String? {
         if (!RequestValidationUtil.isValidJSON(privilegedAllowlist)) {
-            throw IllegalArgumentException("privilegedAllowlist must not be " +
-                "empty, and must be a valid JSON")
+            throw IllegalArgumentException(
+                "privilegedAllowlist must not be " +
+                    "empty, and must be a valid JSON"
+            )
         }
-        return null
+        if (origin == null) {
+            // If origin is null, then this is not a privileged call
+            return origin
+        }
+        try {
+            if (isAppPrivileged(
+                    PrivilegedApp.extractPrivilegedApps(
+                        JSONObject(privilegedAllowlist)
+                    )
+                )
+            ) {
+                return origin
+            }
+        } catch (_: JSONException) {
+            throw IllegalArgumentException("privilegedAllowlist must be formatted properly")
+        }
+        throw IllegalStateException("Origin is not being returned as the calling app did not" +
+            "match the privileged allowlist")
+    }
+
+    /**
+     * Returns true if the [origin] is populated, and false otherwise.
+     *
+     * Note that the [origin] is only populated if a privileged app like a browser calls
+     * Credential Manager APIs on behalf of another application.
+     */
+    fun isOriginPopulated(): Boolean {
+        return origin != null
+    }
+
+    private fun isAppPrivileged(
+        candidateApps: List<PrivilegedApp>
+    ): Boolean {
+        for (app in candidateApps) {
+            if (app.packageName == packageName &&
+                !app.fingerprints.intersect(getSignatureFingerprints(signingInfo)).isEmpty()
+            ) {
+                return true
+            }
+        }
+        return false
+    }
+
+    private fun getSignatureFingerprints(signingInfo: SigningInfo): Set<String> {
+        val fingerprints = mutableSetOf<String>()
+        if (Build.VERSION.SDK_INT >= 28) {
+            return SignatureParserApi28(signingInfo).getSignatureFingerprints()
+        } else {
+            // TODO("Extend to <= 28 if needed")
+        }
+        return fingerprints
     }
 
     init {
         require(packageName.isNotEmpty()) { "packageName must not be empty" }
     }
+
+    @RequiresApi(28)
+    private class SignatureParserApi28(private val signingInfo: SigningInfo) {
+        fun getSignatureFingerprints(): Set<String> {
+            val fingerprints = mutableSetOf<String>()
+            if (signingInfo.hasMultipleSigners()) {
+                val signatures = signingInfo.apkContentsSigners
+                if (signatures != null) {
+                    fingerprints.addAll(convertToFingerprints(signatures))
+                }
+            } else {
+                val signatures = signingInfo.signingCertificateHistory
+                if (signatures != null) {
+                    fingerprints.addAll(convertToFingerprints(signatures))
+                }
+            }
+            return fingerprints
+        }
+
+        private fun convertToFingerprints(signatures: Array<Signature>): Set<String> {
+            val fingerprints = mutableSetOf<String>()
+            for (signature in signatures) {
+                val md = MessageDigest.getInstance("SHA-256")
+                val digest = md.digest(signature.toByteArray())
+                fingerprints.add(digest.joinToString(":") { "%02X".format(it) })
+            }
+            return fingerprints
+        }
+    }
 }
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/CreateEntry.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/CreateEntry.kt
index 0b4ff95..bf1e49a 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/provider/CreateEntry.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/CreateEntry.kt
@@ -60,8 +60,10 @@
      *
      * @param accountName the name of the account where the credential will be saved
      * @param pendingIntent the [PendingIntent] that will get invoked when the user selects this
-     * entry, must be created with flag [PendingIntent.FLAG_MUTABLE] to allow the Android
-     * system to attach the final request
+     * entry, must be created with a unique request code per entry,
+     * with flag [PendingIntent.FLAG_MUTABLE] to allow the Android system to attach the
+     * final request, and NOT with flag [PendingIntent.FLAG_ONE_SHOT] as it can be invoked multiple
+     * times
      * @param description the localized description shown on UI about where the credential is stored
      * @param icon the icon to be displayed with this entry on the UI, must be created using
      * [Icon.createWithResource] when possible, and especially not with [Icon.createWithBitmap] as
@@ -146,8 +148,11 @@
      * @constructor constructs an instance of [CreateEntry.Builder]
      *
      * @param accountName the name of the account where the credential will be registered
-     * @param pendingIntent the [PendingIntent] that will be fired when the user selects
-     * this entry
+     * @param pendingIntent the [PendingIntent] that will get invoked when the user selects this
+     * entry, must be created with a unique request code per entry,
+     * with flag [PendingIntent.FLAG_MUTABLE] to allow the Android system to attach the
+     * final request, and NOT with flag [PendingIntent.FLAG_ONE_SHOT] as it can be invoked multiple
+     * times
      */
     class Builder constructor(
         private val accountName: CharSequence,
@@ -444,6 +449,13 @@
 
         private const val REVISION_ID = 1
 
+        /**
+         * Converts an instance of [CreateEntry] to a [Slice].
+         *
+         * This method is only expected to be called on an API > 28
+         * impl, hence returning null for other levels as the
+         * visibility is only restricted to the library.
+         */
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         @JvmStatic
         fun toSlice(
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt
index a8a5951..a0351d8 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/CustomCredentialEntry.kt
@@ -331,6 +331,13 @@
 
         private const val REVISION_ID = 1
 
+        /**
+         * Converts an instance of [CustomCredentialEntry] to a [Slice].
+         *
+         * This method is only expected to be called on an API > 28
+         * impl, hence returning null for other levels as the
+         * visibility is only restricted to the library.
+         */
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         @JvmStatic
         fun toSlice(
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt
index 7700bdb0..fd4e96c 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/PasswordCredentialEntry.kt
@@ -54,9 +54,11 @@
  * [Icon.createWithResource] when possible, and especially not with [Icon.createWithBitmap] as
  * the latter consumes more memory and may cause undefined behavior due to memory implications
  * on internal transactions; defaulted to a fallback password credential icon if not provided
- * @property pendingIntent the [PendingIntent] that will get invoked when the user selects this
- * entry, must be created with flag [PendingIntent.FLAG_MUTABLE] to allow the Android
- * system to attach the final request
+ * @param pendingIntent the [PendingIntent] that will get invoked when the user selects this
+ * entry, must be created with a unique request code per entry,
+ * with flag [PendingIntent.FLAG_MUTABLE] to allow the Android system to attach the
+ * final request, and NOT with flag [PendingIntent.FLAG_ONE_SHOT] as it can be invoked multiple
+ * times
  * @property isAutoSelectAllowed whether this entry is allowed to be auto
  * selected if it is the only one on the UI. Note that setting this value
  * to true does not guarantee this behavior. The developer must also set this
@@ -333,6 +335,13 @@
 
         private const val REVISION_ID = 1
 
+        /**
+         * Converts an instance of [PasswordCredentialEntry] to a [Slice].
+         *
+         * This method is only expected to be called on an API > 28
+         * impl, hence returning null for other levels as the
+         * visibility is only restricted to the library.
+         */
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         @JvmStatic
         fun toSlice(
@@ -364,8 +373,10 @@
      * @param context the context of the calling app, required to retrieve fallback resources
      * @param username the username of the account holding the password credential
      * @param pendingIntent the [PendingIntent] that will get invoked when the user selects this
-     * entry, must be created with flag [PendingIntent.FLAG_MUTABLE] to allow the Android
-     * system to attach the final request
+     * entry, must be created with a unique request code per entry,
+     * with flag [PendingIntent.FLAG_MUTABLE] to allow the Android system to attach the
+     * final request, and NOT with flag [PendingIntent.FLAG_ONE_SHOT] as it can be invoked multiple
+     * times
      * @param beginGetPasswordOption the option from the original [BeginGetCredentialResponse],
      * for which this credential entry is being added
      *
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt
index 7f6f45e..0df1fe1 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/PublicKeyCredentialEntry.kt
@@ -54,9 +54,11 @@
  * [Icon.createWithResource] when possible, and especially not with [Icon.createWithBitmap] as
  * the latter consumes more memory and may cause undefined behavior due to memory implications
  * on internal transactions; defaulted to a fallback public key credential icon if not provided
- * @property pendingIntent the [PendingIntent] that will get invoked when the user selects this
- * authentication entry on the UI, must be created with flag [PendingIntent.FLAG_MUTABLE] so
- * that the system can add the complete request to the extras of the associated intent
+ * @param pendingIntent the [PendingIntent] that will get invoked when the user selects this
+ * entry, must be created with a unique request code per entry,
+ * with flag [PendingIntent.FLAG_MUTABLE] to allow the Android system to attach the
+ * final request, and NOT with flag [PendingIntent.FLAG_ONE_SHOT] as it can be invoked multiple
+ * times
  * @property isAutoSelectAllowed whether this entry is allowed to be auto
  * selected if it is the only one on the UI. Note that setting this value
  * to true does not guarantee this behavior. The developer must also set this
@@ -92,8 +94,10 @@
      * @param context the context of the calling app, required to retrieve fallback resources
      * @param username the username of the account holding the public key credential
      * @param pendingIntent the [PendingIntent] that will get invoked when the user selects this
-     * entry, must be created with flag [PendingIntent.FLAG_MUTABLE] to allow the Android
-     * system to attach the final request
+     * entry, must be created with a unique request code per entry,
+     * with flag [PendingIntent.FLAG_MUTABLE] to allow the Android system to attach the
+     * final request, and NOT with flag [PendingIntent.FLAG_ONE_SHOT] as it can be invoked multiple
+     * times
      * @param beginGetPublicKeyCredentialOption the option from the original
      * [BeginGetCredentialResponse], for which this credential entry is being added
      * @param displayName the displayName of the account holding the public key credential
@@ -332,6 +336,13 @@
 
         private const val REVISION_ID = 1
 
+        /**
+         * Converts an instance of [PublicKeyCredentialEntry] to a [Slice].
+         *
+         * This method is only expected to be called on an API > 28
+         * impl, hence returning null for other levels as the
+         * visibility is only restricted to the library.
+         */
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         @JvmStatic
         fun toSlice(
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/RemoteEntry.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/RemoteEntry.kt
index 84dac25..060aa4e 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/provider/RemoteEntry.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/RemoteEntry.kt
@@ -55,8 +55,10 @@
      * A builder for [RemoteEntry]
      *
      * @param pendingIntent the [PendingIntent] that will get invoked when the user selects this
-     * entry, must be created with flag [PendingIntent.FLAG_MUTABLE] to allow the Android
-     * system to attach the final request
+     * entry, must be created with a unique request code per entry,
+     * with flag [PendingIntent.FLAG_MUTABLE] to allow the Android system to attach the
+     * final request, and NOT with flag [PendingIntent.FLAG_ONE_SHOT] as it can be invoked multiple
+     * times
      */
     class Builder constructor(
         private val pendingIntent: PendingIntent
@@ -79,6 +81,13 @@
 
         private const val REVISION_ID = 1
 
+        /**
+         * Converts an instance of [RemoteEntry] to a [Slice].
+         *
+         * This method is only expected to be called on an API > 28
+         * impl, hence returning null for other levels as the
+         * visibility is only restricted to the library.
+         */
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         @RequiresApi(28)
         @JvmStatic
diff --git a/credentials/credentials/src/main/java/androidx/credentials/provider/utils/PrivilegedApp.kt b/credentials/credentials/src/main/java/androidx/credentials/provider/utils/PrivilegedApp.kt
new file mode 100644
index 0000000..11b401e8
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/provider/utils/PrivilegedApp.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials.provider.utils
+
+import android.os.Build
+import org.json.JSONObject
+
+internal data class PrivilegedApp(
+    val packageName: String,
+    val fingerprints: Set<String>
+) {
+    companion object {
+        private const val PACKAGE_NAME_KEY = "package_name"
+        private const val SIGNATURES_KEY = "signatures"
+        private const val FINGERPRINT_KEY = "cert_fingerprint_sha256"
+        private const val BUILD_KEY = "build"
+        private const val USER_DEBUG_KEY = "userdebug"
+        private const val TYPE_KEY = "type"
+        private const val APP_INFO_KEY = "info"
+        private const val ANDROID_TYPE_KEY = "android"
+        private const val USER_BUILD_TYPE = "userdebug"
+        private const val APPS_KEY = "apps"
+
+        @JvmStatic
+        internal fun extractPrivilegedApps(jsonObject: JSONObject): List<PrivilegedApp> {
+            val apps = mutableListOf<PrivilegedApp>()
+            val appsJsonArray = jsonObject.getJSONArray(APPS_KEY)
+            for (i in 0 until appsJsonArray.length()) {
+                val appJsonObject = appsJsonArray.getJSONObject(i)
+                if (appJsonObject.getString(TYPE_KEY) != ANDROID_TYPE_KEY) {
+                    continue
+                }
+                apps.add(
+                    createFromJSONObject(
+                        appJsonObject.getJSONObject(APP_INFO_KEY), filterUserDebug = true
+                    )
+                )
+            }
+            return apps
+        }
+
+        @JvmStatic
+        fun createFromJSONObject(
+            appInfoJsonObject: JSONObject,
+            filterUserDebug: Boolean
+        ): PrivilegedApp {
+            val signaturesJson = appInfoJsonObject.getJSONArray(SIGNATURES_KEY)
+            val fingerprints = mutableSetOf<String>()
+            for (j in 0 until signaturesJson.length()) {
+                if (filterUserDebug) {
+                    if (USER_DEBUG_KEY == signaturesJson.getJSONObject(j)
+                            .optString(BUILD_KEY) && USER_BUILD_TYPE != Build.TYPE
+                    ) {
+                        continue
+                    }
+                }
+                fingerprints.add(signaturesJson.getJSONObject(j).getString(FINGERPRINT_KEY))
+            }
+            return PrivilegedApp(
+                packageName = appInfoJsonObject.getString(PACKAGE_NAME_KEY),
+                fingerprints = fingerprints
+            )
+        }
+    }
+}
diff --git a/datastore/datastore-core/lint-baseline.xml b/datastore/datastore-core/lint-baseline.xml
new file mode 100644
index 0000000..b8e7866
--- /dev/null
+++ b/datastore/datastore-core/lint-baseline.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.1.0-beta05" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-beta05)" variant="all" version="8.1.0-beta05">
+
+    <issue
+        id="SyntheticAccessor"
+        message="Access to `private` method `getCoordinator` of class `DataStoreImpl` requires synthetic accessor"
+        errorLine1="                coordinator.lock {"
+        errorLine2="                ~~~~~~~~~~~">
+        <location
+            file="src/commonMain/kotlin/androidx/datastore/core/DataStoreImpl.kt"/>
+    </issue>
+
+    <issue
+        id="SyntheticAccessor"
+        message="Access to `private` method `getCoordinator` of class `DataStoreImpl` requires synthetic accessor"
+        errorLine1="                        version = coordinator.getVersion()"
+        errorLine2="                                  ~~~~~~~~~~~">
+        <location
+            file="src/commonMain/kotlin/androidx/datastore/core/DataStoreImpl.kt"/>
+    </issue>
+
+    <issue
+        id="SyntheticAccessor"
+        message="Access to `private` method `getCoordinator` of class `DataStoreImpl` requires synthetic accessor"
+        errorLine1="                    coordinator.updateNotifications.conflate().collect {"
+        errorLine2="                    ~~~~~~~~~~~">
+        <location
+            file="src/commonMain/kotlin/androidx/datastore/core/DataStoreImpl.kt"/>
+    </issue>
+
+    <issue
+        id="SyntheticAccessor"
+        message="Access to `private` method `readDataAndUpdateCache` of class `DataStoreImpl` requires synthetic accessor"
+        errorLine1="                            readDataAndUpdateCache(requireLock = true)"
+        errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/commonMain/kotlin/androidx/datastore/core/DataStoreImpl.kt"/>
+    </issue>
+
+    <issue
+        id="SyntheticAccessor"
+        message="Access to `private` method `getCoordinator` of class `DataStoreImpl` requires synthetic accessor"
+        errorLine1="                coordinator.lock { block() }"
+        errorLine2="                ~~~~~~~~~~~">
+        <location
+            file="src/commonMain/kotlin/androidx/datastore/core/DataStoreImpl.kt"/>
+    </issue>
+
+    <issue
+        id="SyntheticAccessor"
+        message="Access to `private` method `readDataFromFileOrDefault` of class `DataStoreImpl` requires synthetic accessor"
+        errorLine1="                    val data = readDataFromFileOrDefault()"
+        errorLine2="                               ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/commonMain/kotlin/androidx/datastore/core/DataStoreImpl.kt"/>
+    </issue>
+
+    <issue
+        id="SyntheticAccessor"
+        message="Access to `private` method `getCoordinator` of class `DataStoreImpl` requires synthetic accessor"
+        errorLine1="                    Data(data, data.hashCode(), version = coordinator.getVersion())"
+        errorLine2="                                                          ~~~~~~~~~~~">
+        <location
+            file="src/commonMain/kotlin/androidx/datastore/core/DataStoreImpl.kt"/>
+    </issue>
+
+    <issue
+        id="SyntheticAccessor"
+        message="Access to `private` method `getCoordinator` of class `DataStoreImpl` requires synthetic accessor"
+        errorLine1="                    val preLockVersion = coordinator.getVersion()"
+        errorLine2="                                         ~~~~~~~~~~~">
+        <location
+            file="src/commonMain/kotlin/androidx/datastore/core/DataStoreImpl.kt"/>
+    </issue>
+
+    <issue
+        id="SyntheticAccessor"
+        message="Access to `private` method `getCoordinator` of class `DataStoreImpl` requires synthetic accessor"
+        errorLine1="                    coordinator.tryLock { locked ->"
+        errorLine2="                    ~~~~~~~~~~~">
+        <location
+            file="src/commonMain/kotlin/androidx/datastore/core/DataStoreImpl.kt"/>
+    </issue>
+
+    <issue
+        id="SyntheticAccessor"
+        message="Access to `private` method `readDataFromFileOrDefault` of class `DataStoreImpl` requires synthetic accessor"
+        errorLine1="                        val data = readDataFromFileOrDefault()"
+        errorLine2="                                   ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/commonMain/kotlin/androidx/datastore/core/DataStoreImpl.kt"/>
+    </issue>
+
+    <issue
+        id="SyntheticAccessor"
+        message="Access to `private` method `getCoordinator` of class `DataStoreImpl` requires synthetic accessor"
+        errorLine1="                        val version = if (locked) coordinator.getVersion() else preLockVersion"
+        errorLine2="                                                  ~~~~~~~~~~~">
+        <location
+            file="src/commonMain/kotlin/androidx/datastore/core/DataStoreImpl.kt"/>
+    </issue>
+
+    <issue
+        id="SyntheticAccessor"
+        message="Access to `private` method `readDataFromFileOrDefault` of class `DataStoreImpl` requires synthetic accessor"
+        errorLine1="                            newData = readDataFromFileOrDefault()"
+        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="src/commonMain/kotlin/androidx/datastore/core/DataStoreImpl.kt"/>
+    </issue>
+
+    <issue
+        id="SyntheticAccessor"
+        message="Access to `private` method `getCoordinator` of class `DataStoreImpl` requires synthetic accessor"
+        errorLine1="                            version = coordinator.getVersion()"
+        errorLine2="                                      ~~~~~~~~~~~">
+        <location
+            file="src/commonMain/kotlin/androidx/datastore/core/DataStoreImpl.kt"/>
+    </issue>
+
+</issues>
diff --git a/datastore/datastore-core/src/androidMain/kotlin/androidx/datastore/core/MultiProcessCoordinator.kt b/datastore/datastore-core/src/androidMain/kotlin/androidx/datastore/core/MultiProcessCoordinator.kt
index 48f575b..f5314a2 100644
--- a/datastore/datastore-core/src/androidMain/kotlin/androidx/datastore/core/MultiProcessCoordinator.kt
+++ b/datastore/datastore-core/src/androidMain/kotlin/androidx/datastore/core/MultiProcessCoordinator.kt
@@ -22,6 +22,7 @@
 import java.io.FileOutputStream
 import java.io.IOException
 import java.nio.channels.FileLock
+import kotlin.contracts.ExperimentalContracts
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.channels.trySendBlocking
@@ -72,6 +73,7 @@
 
     // run block with an attempt to get the exclusive lock, still run even if
     // attempt fails. Pass a boolean to indicate if the attempt succeeds.
+    @OptIn(ExperimentalContracts::class) // withTryLock
     override suspend fun <T> tryLock(block: suspend (Boolean) -> T): T {
         inMemoryMutex.withTryLock<T> {
             if (it == false) {
diff --git a/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/DirectTestService.kt b/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/DirectTestService.kt
index aa9cdac..cc77d472 100644
--- a/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/DirectTestService.kt
+++ b/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/DirectTestService.kt
@@ -29,7 +29,7 @@
 import android.os.Message
 import android.os.Messenger
 import android.os.RemoteException
-import androidx.testing.TestMessageProto.FooProto
+import androidx.datastore.testing.TestMessageProto.FooProto
 import com.google.common.collect.ImmutableList
 import java.io.Serializable
 import java.util.concurrent.CountDownLatch
diff --git a/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/MultiProcessDataStoreMultiProcessTest.kt b/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/MultiProcessDataStoreMultiProcessTest.kt
index 6a5cbdb..5535964 100644
--- a/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/MultiProcessDataStoreMultiProcessTest.kt
+++ b/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/MultiProcessDataStoreMultiProcessTest.kt
@@ -22,8 +22,8 @@
 import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
 import androidx.datastore.core.okio.OkioSerializer
 import androidx.datastore.core.okio.OkioStorage
+import androidx.datastore.testing.TestMessageProto.FooProto
 import androidx.test.core.app.ApplicationProvider
-import androidx.testing.TestMessageProto.FooProto
 import com.google.common.truth.Truth.assertThat
 import com.google.protobuf.ExtensionRegistryLite
 import java.io.File
diff --git a/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/MultiProcessDataStoreSingleProcessFileTest.kt b/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/MultiProcessDataStoreSingleProcessFileTest.kt
index e51d906..2190e7f 100644
--- a/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/MultiProcessDataStoreSingleProcessFileTest.kt
+++ b/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/MultiProcessDataStoreSingleProcessFileTest.kt
@@ -27,10 +27,8 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.FlowPreview
 import kotlinx.coroutines.InternalCoroutinesApi
 import kotlinx.coroutines.Job
-import kotlinx.coroutines.ObsoleteCoroutinesApi
 import kotlinx.coroutines.async
 import kotlinx.coroutines.awaitAll
 import kotlinx.coroutines.cancelAndJoin
@@ -42,7 +40,7 @@
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 
-@OptIn(ExperimentalCoroutinesApi::class, ObsoleteCoroutinesApi::class, FlowPreview::class)
+@OptIn(ExperimentalCoroutinesApi::class)
 @InternalCoroutinesApi
 class MultiProcessDataStoreSingleProcessFileTest :
     MultiProcessDataStoreSingleProcessTest<JavaIOFile>(FileTestIO()) {
diff --git a/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/MultiProcessDataStoreSingleProcessTest.kt b/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/MultiProcessDataStoreSingleProcessTest.kt
index 0ffe3d0..952fc5d 100644
--- a/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/MultiProcessDataStoreSingleProcessTest.kt
+++ b/datastore/datastore-core/src/androidTest/java/androidx/datastore/core/MultiProcessDataStoreSingleProcessTest.kt
@@ -33,7 +33,6 @@
 import java.util.concurrent.atomic.AtomicInteger
 import kotlin.coroutines.AbstractCoroutineContextElement
 import kotlin.coroutines.CoroutineContext
-import kotlin.random.Random
 import kotlinx.coroutines.CancellationException
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.CoroutineScope
@@ -70,7 +69,7 @@
 @ExperimentalCoroutinesApi
 @LargeTest
 @RunWith(JUnit4::class)
-abstract class MultiProcessDataStoreSingleProcessTest<F : TestFile>(
+abstract class MultiProcessDataStoreSingleProcessTest<F : TestFile<F>>(
     protected val testIO: TestIO<F, *>
 ) {
     protected lateinit var store: DataStore<Byte>
@@ -82,7 +81,7 @@
     abstract fun getJavaFile(file: F): File
 
     private fun newDataStore(
-        file: TestFile = testFile,
+        file: F = testFile,
         scope: CoroutineScope = dataStoreScope,
         initTasksList: List<suspend (api: InitializerApi<Byte>) -> Unit> = listOf(),
         corruptionHandler: CorruptionHandler<Byte> = NoOpCorruptionHandler<Byte>()
@@ -105,8 +104,8 @@
     @Before
     fun setUp() {
         serializerConfig = TestingSerializerConfig()
-        tempFolder = testIO.tempDir()
-        testFile = testIO.newTempFile(tempFolder)
+        tempFolder = testIO.newTempFile().also { it.mkdirs() }
+        testFile = testIO.newTempFile(parentFile = tempFolder)
         dataStoreScope = TestScope(UnconfinedTestDispatcher() + Job())
         store = testIO.getStore(
             serializerConfig,
@@ -210,12 +209,10 @@
     @Test
     fun testWriteToNonExistentDir() = runBlocking {
         val fileInNonExistentDir = testIO.newTempFile(
-            testIO.tempDir(
-                "/this/does/not/exist",
-                makeDirs = false,
-                parentDir = testIO.tempDir()
-            )
+            relativePath = "/this/does/not/exist/ds.txt"
         )
+        assertThat(fileInNonExistentDir.exists()).isFalse()
+        assertThat(fileInNonExistentDir.parentFile()!!.exists()).isFalse()
         runTest {
             val newStore = newDataStore(fileInNonExistentDir, scope = backgroundScope)
 
@@ -232,16 +229,17 @@
 
     @Test
     fun testReadFromNonExistentFile() = runTest {
-        testFile.deleteIfExists()
         val newStore = newDataStore(testFile)
         assertThat(newStore.data.first()).isEqualTo(0)
     }
 
     @Test
     fun testWriteToDirFails() = runTest {
-        val directoryFile = testIO.tempDir("/this/is/a${Random.nextInt()}/directory")
+        val directoryFile = testIO.newTempFile(relativePath = "this/is/a/directory").also {
+            it.mkdirs()
+        }
 
-        assertThat(testIO.isDirectory(directoryFile))
+        assertThat(directoryFile.isDirectory()).isTrue()
 
         val newStore = newDataStore(directoryFile)
         assertThrows<IOException> { newStore.data.first() }
@@ -298,7 +296,7 @@
 
     @Test
     fun testWriteAfterTransientBadRead() = runTest {
-        testFile.createIfNotExists()
+        testFile.write("")
         assertThat(testFile.exists()).isTrue()
 
         serializerConfig.failingRead = true
@@ -313,7 +311,7 @@
 
     @Test
     fun testWriteWithBadReadFails() = runTest {
-        testFile.createIfNotExists()
+        testFile.write("")
         assertThat(testFile.exists()).isTrue()
 
         serializerConfig.failingRead = true
@@ -792,8 +790,6 @@
 
     @Test
     fun testDefaultValueUsedWhenNoDataOnDisk() = runTest {
-        testFile.deleteIfExists()
-
         val dataStore = testIO.getStore(
             TestingSerializerConfig(defaultValue = 99),
             dataStoreScope,
@@ -895,7 +891,7 @@
 
     @Test
     fun testCreateDuplicateActiveDataStore() = runTest {
-        val file = testIO.newTempFile(tempFolder)
+        val file = testIO.newTempFile(parentFile = tempFolder)
         val dataStore = newDataStore(file = file, scope = CoroutineScope(Job()))
 
         dataStore.data.first()
@@ -909,7 +905,7 @@
 
     @Test
     fun testCreateDataStore_withSameFileAsInactiveDataStore() = runTest {
-        val file = testIO.newTempFile(tempFolder)
+        val file = testIO.newTempFile(parentFile = tempFolder)
         val scope1 = CoroutineScope(Job())
         val dataStore1 = newDataStore(file = file, scope = scope1)
 
diff --git a/datastore/datastore-core/src/androidTest/proto/test.proto b/datastore/datastore-core/src/androidTest/proto/test.proto
index 0ad7b69..019bc60 100644
--- a/datastore/datastore-core/src/androidTest/proto/test.proto
+++ b/datastore/datastore-core/src/androidTest/proto/test.proto
@@ -17,9 +17,9 @@
 // Protos for use in tests
 syntax = "proto2";
 
-package androidx.testing;
+package androidx.datastore.testing;
 
-option java_package = "androidx.testing";
+option java_package = "androidx.datastore.testing";
 option java_outer_classname = "TestMessageProto";
 
 message FooProto {
diff --git a/datastore/datastore-core/src/commonMain/kotlin/androidx/datastore/core/MutexUtils.kt b/datastore/datastore-core/src/commonMain/kotlin/androidx/datastore/core/MutexUtils.kt
index 528fa08..4a5250a 100644
--- a/datastore/datastore-core/src/commonMain/kotlin/androidx/datastore/core/MutexUtils.kt
+++ b/datastore/datastore-core/src/commonMain/kotlin/androidx/datastore/core/MutexUtils.kt
@@ -28,7 +28,7 @@
  *
  * [block] is guaranteed to be called once and only once by this function.
  */
-@OptIn(ExperimentalContracts::class)
+@ExperimentalContracts
 internal inline fun <R> Mutex.withTryLock(owner: Any? = null, block: (Boolean) -> R): R {
     contract {
         callsInPlace(block, InvocationKind.EXACTLY_ONCE)
diff --git a/datastore/datastore-core/src/commonMain/kotlin/androidx/datastore/core/SingleProcessCoordinator.kt b/datastore/datastore-core/src/commonMain/kotlin/androidx/datastore/core/SingleProcessCoordinator.kt
index 68ce1c0..83e2072 100644
--- a/datastore/datastore-core/src/commonMain/kotlin/androidx/datastore/core/SingleProcessCoordinator.kt
+++ b/datastore/datastore-core/src/commonMain/kotlin/androidx/datastore/core/SingleProcessCoordinator.kt
@@ -16,6 +16,7 @@
 
 package androidx.datastore.core
 
+import kotlin.contracts.ExperimentalContracts
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.sync.Mutex
@@ -40,6 +41,7 @@
 
     // run block with an attempt to get the exclusive lock, still run even if
     // attempt fails. Pass a boolean to indicate if the attempt succeeds.
+    @OptIn(ExperimentalContracts::class) // withTryLock
     override suspend fun <T> tryLock(block: suspend (Boolean) -> T): T {
         return mutex.withTryLock {
             block(it)
diff --git a/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/CloseDownstreamOnCloseTest.kt b/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/CloseDownstreamOnCloseTest.kt
new file mode 100644
index 0000000..ded8cbc
--- /dev/null
+++ b/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/CloseDownstreamOnCloseTest.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.datastore.core
+
+import androidx.datastore.TestFile
+import androidx.datastore.TestIO
+import androidx.datastore.TestingSerializerConfig
+import androidx.kruth.assertThat
+import androidx.kruth.assertThrows
+import kotlin.test.BeforeTest
+import kotlin.test.Test
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.async
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+
+abstract class CloseDownstreamOnCloseTest<F : TestFile<F>>(private val testIO: TestIO<F, *>) {
+    private val dispatcher = StandardTestDispatcher()
+    private val testScope = TestScope(dispatcher)
+    private val datastoreScope = testScope.backgroundScope
+    private lateinit var store: DataStore<Byte>
+
+    @BeforeTest
+    fun createDataStore() {
+        store = testIO.getStore(
+            serializerConfig = TestingSerializerConfig(),
+            scope = datastoreScope,
+            coordinatorProducer = { createSingleProcessCoordinator() }
+        ) { testIO.newTempFile() }
+    }
+
+    @Test
+    fun closeWhileCollecting() = testScope.runTest {
+        val collector = async {
+            store.data.toList().map { it.toInt() }
+        }
+        store.updateData { 1 }
+        datastoreScope.cancel()
+        dispatcher.scheduler.advanceUntilIdle()
+        assertThat(collector.await()).isEqualTo(listOf(0, 1))
+    }
+
+    @Test
+    fun closeBeforeCollecting() = testScope.runTest {
+        datastoreScope.cancel()
+        assertThrows(CancellationException::class) {
+            store.data.toList()
+        }
+    }
+}
diff --git a/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/CommonTests.kt b/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/CommonTests.kt
index daf468c..1c7a877 100644
--- a/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/CommonTests.kt
+++ b/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/CommonTests.kt
@@ -18,17 +18,11 @@
 
 import androidx.datastore.OkioPath
 import androidx.datastore.OkioTestIO
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.FlowPreview
-import kotlinx.coroutines.InternalCoroutinesApi
-import kotlinx.coroutines.ObsoleteCoroutinesApi
 import okio.IOException
 
-@OptIn(ExperimentalCoroutinesApi::class, ObsoleteCoroutinesApi::class, FlowPreview::class)
-@InternalCoroutinesApi
 class DataMigrationInitializerTestOkioTest :
     DataMigrationInitializerTest<OkioPath, IOException>(OkioTestIO())
 
-@OptIn(ExperimentalCoroutinesApi::class, ObsoleteCoroutinesApi::class, FlowPreview::class)
-@InternalCoroutinesApi
 class SingleProcessDataStoreOkioTest : SingleProcessDataStoreTest<OkioPath>(OkioTestIO())
+
+class CloseDownstreamOnCloseOkioTest : CloseDownstreamOnCloseTest<OkioPath>(OkioTestIO())
diff --git a/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/DataMigrationInitializerTest.kt b/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/DataMigrationInitializerTest.kt
index 47d5165..4ac3c9e 100644
--- a/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/DataMigrationInitializerTest.kt
+++ b/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/DataMigrationInitializerTest.kt
@@ -33,7 +33,7 @@
 import kotlinx.coroutines.test.runTest
 
 @OptIn(ExperimentalCoroutinesApi::class)
-abstract class DataMigrationInitializerTest<F : TestFile, IOE : Throwable>
+abstract class DataMigrationInitializerTest<F : TestFile<F>, IOE : Throwable>
     (private val testIO: TestIO<F, IOE>) {
 
     private lateinit var storage: Storage<Byte>
diff --git a/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/SingleProcessDataStoreTest.kt b/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/SingleProcessDataStoreTest.kt
index dd3808f..7b33ed6 100644
--- a/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/SingleProcessDataStoreTest.kt
+++ b/datastore/datastore-core/src/commonTest/kotlin/androidx/datastore/core/SingleProcessDataStoreTest.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:Suppress("DEPRECATION") // b/220884658
-
 package androidx.datastore.core
 
 import androidx.datastore.TestFile
@@ -27,9 +25,9 @@
 import kotlin.coroutines.AbstractCoroutineContextElement
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.cancellation.CancellationException
-import kotlin.random.Random
 import kotlin.test.BeforeTest
 import kotlin.test.Test
+import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
@@ -54,7 +52,7 @@
 import kotlinx.coroutines.withContext
 
 @OptIn(ExperimentalCoroutinesApi::class)
-abstract class SingleProcessDataStoreTest<F : TestFile>(private val testIO: TestIO<F, *>) {
+abstract class SingleProcessDataStoreTest<F : TestFile<F>>(private val testIO: TestIO<F, *>) {
 
     protected lateinit var store: DataStore<Byte>
     private lateinit var serializerConfig: TestingSerializerConfig
@@ -65,8 +63,8 @@
     @BeforeTest
     fun setUp() {
         serializerConfig = TestingSerializerConfig()
-        tempFolder = testIO.tempDir()
-        testFile = testIO.newTempFile(tempFolder)
+        tempFolder = testIO.newTempFile().also { it.mkdirs() }
+        testFile = testIO.newTempFile(parentFile = tempFolder)
         dataStoreScope = TestScope(UnconfinedTestDispatcher())
         store = testIO.getStore(
             serializerConfig,
@@ -77,11 +75,12 @@
 
     fun doTest(initDataStore: Boolean = false, test: suspend TestScope.() -> Unit) {
         if (initDataStore) {
-            runTest(dispatchTimeoutMs = 10000) {
+            // running this separately to ensure the DS it is closed after initialization
+            runTest {
                 initDataStore(this)
             }
         }
-        runTest(dispatchTimeoutMs = 10000) {
+        runTest(timeout = 10.seconds) {
             test(this)
         }
     }
@@ -182,7 +181,7 @@
     @Test
     fun testWriteToNonExistentDir() = doTest {
         val fileInNonExistentDir = testIO.newTempFile(
-            testIO.tempDir("/this/does/not/exist", makeDirs = false)
+            relativePath = "this/does/not/exist"
         )
 
         coroutineScope {
@@ -201,17 +200,16 @@
 
     @Test
     fun testReadFromNonExistentFile() = doTest {
-        // TODO remove deleteIfExists after b/276983736
-        testFile.deleteIfExists()
         val newStore = newDataStore(testFile)
         assertThat(newStore.data.first()).isEqualTo(0)
     }
 
     @Test
     fun testWriteToDirFails() = doTest {
-        val directoryFile = testIO.tempDir("/this/is/a${Random.nextInt()}/directory")
-
-        assertThat(testIO.isDirectory(directoryFile))
+        val directoryFile = testIO.newTempFile(relativePath = "/this/is/a/directory").also {
+            it.mkdirs(mustCreate = true)
+        }
+        assertThat(directoryFile.isDirectory()).isTrue()
 
         val newStore = newDataStore(directoryFile)
         assertThrows(testIO.ioExceptionClass()) { newStore.data.first() }
@@ -341,14 +339,14 @@
 
     @Test
     fun testCanWriteFromInitTask() = doTest {
-        store = newDataStore(initTasksList = listOf<InitTaskList>({ api -> api.updateData { 1 } }))
+        store = newDataStore(initTasksList = listOf({ api -> api.updateData { 1 } }))
 
         assertThat(store.data.first()).isEqualTo(1)
     }
 
     @Test
     fun testInitTaskFailsFirstTimeDueToReadFail() = doTest(initDataStore = true) {
-        store = newDataStore(initTasksList = listOf<InitTaskList>({ api -> api.updateData { 1 } }))
+        store = newDataStore(initTasksList = listOf({ api -> api.updateData { 1 } }))
 
         serializerConfig.failingRead = true
         assertThrows(testIO.ioExceptionClass()) { store.updateData { 2 } }
@@ -400,7 +398,7 @@
         val continueInit = CompletableDeferred<Unit>()
 
         store = newDataStore(
-            initTasksList = listOf<InitTaskList>({ api ->
+            initTasksList = listOf({ api ->
                 continueInit.await()
                 api.updateData { 1 }
             })
@@ -424,7 +422,7 @@
         val continueInit = CompletableDeferred<Unit>()
 
         store = newDataStore(
-            initTasksList = listOf<InitTaskList>({ api ->
+            initTasksList = listOf({ api ->
                 continueInit.await()
                 api.updateData { 1 }
             })
@@ -614,7 +612,7 @@
         }
 
         coroutineScope {
-            val testingHandler: TestingCorruptionHandler = TestingCorruptionHandler()
+            val testingHandler = TestingCorruptionHandler()
             val newStore = newDataStore(corruptionHandler = testingHandler, file = testFile)
 
             newStore.updateData { 2 }
@@ -650,7 +648,7 @@
         }
 
         coroutineScope {
-            val testingHandler: TestingCorruptionHandler = TestingCorruptionHandler()
+            val testingHandler = TestingCorruptionHandler()
             serializerConfig.failReadWithCorruptionException = true
             val newStore = newDataStore(corruptionHandler = testingHandler, file = testFile)
 
@@ -670,7 +668,7 @@
         }
 
         coroutineScope {
-            val testingHandler: TestingCorruptionHandler = TestingCorruptionHandler()
+            val testingHandler = TestingCorruptionHandler()
             serializerConfig.failReadWithCorruptionException = true
             val newStore = newDataStore(corruptionHandler = testingHandler, file = testFile)
 
@@ -689,8 +687,7 @@
         }
 
         coroutineScope {
-            val testingHandler: TestingCorruptionHandler =
-                TestingCorruptionHandler(replaceWith = 10)
+            val testingHandler = TestingCorruptionHandler(replaceWith = 10)
             serializerConfig.failReadWithCorruptionException = true
             val newStore = newDataStore(
                 corruptionHandler = testingHandler, file = testFile,
@@ -703,7 +700,6 @@
 
     @Test
     fun testDefaultValueUsedWhenNoDataOnDisk() = doTest {
-        testFile.deleteIfExists()
         val dataStore = newDataStore(
             serializerConfig = TestingSerializerConfig(defaultValue = 99),
             scope = dataStoreScope
@@ -967,11 +963,11 @@
     }
 
     private fun newDataStore(
-        file: TestFile = testFile,
+        file: F = testFile,
         serializerConfig: TestingSerializerConfig = this.serializerConfig,
         scope: CoroutineScope = dataStoreScope,
         initTasksList: List<InitTaskList> = listOf(),
-        corruptionHandler: CorruptionHandler<Byte> = NoOpCorruptionHandler<Byte>()
+        corruptionHandler: CorruptionHandler<Byte> = NoOpCorruptionHandler()
     ): DataStore<Byte> {
         return DataStoreImpl(
             testIO.getStorage(serializerConfig, { createSingleProcessCoordinator() }) { file },
diff --git a/datastore/datastore-core/src/jvmTest/kotlin/androidx/datastore/core/JvmTests.kt b/datastore/datastore-core/src/jvmTest/kotlin/androidx/datastore/core/JvmTests.kt
index 4d41170..3db293c 100644
--- a/datastore/datastore-core/src/jvmTest/kotlin/androidx/datastore/core/JvmTests.kt
+++ b/datastore/datastore-core/src/jvmTest/kotlin/androidx/datastore/core/JvmTests.kt
@@ -23,19 +23,16 @@
 import java.io.IOException
 import java.io.InputStream
 import java.io.OutputStream
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.FlowPreview
 import kotlinx.coroutines.InternalCoroutinesApi
-import kotlinx.coroutines.ObsoleteCoroutinesApi
 import kotlinx.coroutines.flow.first
 import org.junit.Test
 
-@OptIn(ExperimentalCoroutinesApi::class, ObsoleteCoroutinesApi::class, FlowPreview::class)
 @InternalCoroutinesApi
 class DataMigrationInitializerTestFileTest :
     DataMigrationInitializerTest<JavaIOFile, IOException>(FileTestIO())
 
-@OptIn(ExperimentalCoroutinesApi::class)
+class CloseDownstreamOnCloseJavaTest : CloseDownstreamOnCloseTest<JavaIOFile>(FileTestIO())
+
 @InternalCoroutinesApi
 class SingleProcessDataStoreJavaTest : SingleProcessDataStoreTest<JavaIOFile>(FileTestIO()) {
 
diff --git a/datastore/datastore-preferences-core/src/jvmTest/kotlin/androidx/datastore/preferences/core/PreferencesSerializerJavaTest.kt b/datastore/datastore-preferences-core/src/jvmTest/kotlin/androidx/datastore/preferences/core/PreferencesSerializerJavaTest.kt
index 7bc705a..14601ae 100644
--- a/datastore/datastore-preferences-core/src/jvmTest/kotlin/androidx/datastore/preferences/core/PreferencesSerializerJavaTest.kt
+++ b/datastore/datastore-preferences-core/src/jvmTest/kotlin/androidx/datastore/preferences/core/PreferencesSerializerJavaTest.kt
@@ -44,7 +44,6 @@
     @BeforeTest
     fun setUp() {
         testFile = testIO.newTempFile()
-        fileSystem.createDirectories(testFile.path.parent!!)
     }
     fun doTest(test: suspend TestScope.() -> Unit) {
         runTest(timeout = 10000.milliseconds) {
diff --git a/datastore/datastore-preferences-core/src/nativeTest/kotlin/androidx/datastore/preferences/core/PreferenceDataStoreFactoryNativeTest.kt b/datastore/datastore-preferences-core/src/nativeTest/kotlin/androidx/datastore/preferences/core/PreferenceDataStoreFactoryNativeTest.kt
index 78a4666..59eae12b 100644
--- a/datastore/datastore-preferences-core/src/nativeTest/kotlin/androidx/datastore/preferences/core/PreferenceDataStoreFactoryNativeTest.kt
+++ b/datastore/datastore-preferences-core/src/nativeTest/kotlin/androidx/datastore/preferences/core/PreferenceDataStoreFactoryNativeTest.kt
@@ -30,7 +30,6 @@
 import kotlinx.coroutines.test.runTest
 import okio.FileSystem
 import okio.Path
-import okio.Path.Companion.toPath
 
 @OptIn(ExperimentalCoroutinesApi::class)
 class PreferenceDataStoreFactoryNativeTest {
@@ -45,7 +44,7 @@
     @BeforeTest
     fun setUp() {
         testIO = OkioTestIO()
-        testFile = testIO.tempDir().path / "test.preferences_pb".toPath()
+        testFile = testIO.newTempFile(relativePath = "test.preferences_pb").path
         dataStoreScope = TestScope(UnconfinedTestDispatcher())
     }
 
diff --git a/datastore/datastore-preferences-proto/src/main/proto/preferences.proto b/datastore/datastore-preferences-proto/src/main/proto/preferences.proto
index f9acb5a..157eb67 100644
--- a/datastore/datastore-preferences-proto/src/main/proto/preferences.proto
+++ b/datastore/datastore-preferences-proto/src/main/proto/preferences.proto
@@ -1,7 +1,7 @@
 // Proto for use by PreferencesSerializer
 syntax = "proto2";
 
-package androidx.testing;
+package androidx.datastore.testing;
 
 option java_package = "androidx.datastore.preferences";
 option java_outer_classname = "PreferencesProto";
diff --git a/datastore/datastore-proto/src/test/java/androidx/datastore/protos/ProtoSerializerTest.kt b/datastore/datastore-proto/src/test/java/androidx/datastore/protos/ProtoSerializerTest.kt
index f475e04..c6899f9 100644
--- a/datastore/datastore-proto/src/test/java/androidx/datastore/protos/ProtoSerializerTest.kt
+++ b/datastore/datastore-proto/src/test/java/androidx/datastore/protos/ProtoSerializerTest.kt
@@ -17,9 +17,9 @@
 package androidx.datastore.protos
 
 import androidx.datastore.core.CorruptionException
-import androidx.testing.TestMessageProto.ExtendableProto
-import androidx.testing.TestMessageProto.ExtensionProto
-import androidx.testing.TestMessageProto.FooProto
+import androidx.datastore.testing.TestMessageProto.ExtendableProto
+import androidx.datastore.testing.TestMessageProto.ExtensionProto
+import androidx.datastore.testing.TestMessageProto.FooProto
 import androidx.testutils.assertThrows
 import com.google.common.truth.Truth.assertThat
 import com.google.protobuf.ExtensionRegistryLite
diff --git a/datastore/datastore-proto/src/test/proto/test.proto b/datastore/datastore-proto/src/test/proto/test.proto
index 5a79dd2..36afc4c 100644
--- a/datastore/datastore-proto/src/test/proto/test.proto
+++ b/datastore/datastore-proto/src/test/proto/test.proto
@@ -1,9 +1,9 @@
 // Protos for use in tests
 syntax = "proto2";
 
-package androidx.testing;
+package androidx.datastore.testing;
 
-option java_package = "androidx.testing";
+option java_package = "androidx.datastore.testing";
 option java_outer_classname = "TestMessageProto";
 
 message FooProto {
diff --git a/datastore/datastore-rxjava2/src/androidTest/java/androidx/datastore/rxjava2/RxDataStoreBuilderTest.java b/datastore/datastore-rxjava2/src/androidTest/java/androidx/datastore/rxjava2/RxDataStoreBuilderTest.java
index 6986576..3702526 100644
--- a/datastore/datastore-rxjava2/src/androidTest/java/androidx/datastore/rxjava2/RxDataStoreBuilderTest.java
+++ b/datastore/datastore-rxjava2/src/androidTest/java/androidx/datastore/rxjava2/RxDataStoreBuilderTest.java
@@ -23,6 +23,11 @@
 import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler;
 import androidx.test.core.app.ApplicationProvider;
 
+import io.reactivex.Completable;
+import io.reactivex.Scheduler;
+import io.reactivex.Single;
+import io.reactivex.schedulers.Schedulers;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
@@ -31,11 +36,6 @@
 import java.util.concurrent.Executors;
 import java.util.concurrent.ThreadFactory;
 
-import io.reactivex.Completable;
-import io.reactivex.Scheduler;
-import io.reactivex.Single;
-import io.reactivex.schedulers.Schedulers;
-
 public class RxDataStoreBuilderTest {
     @Rule
     public TemporaryFolder tempFolder = new TemporaryFolder();
@@ -67,6 +67,8 @@
     public void testConstructWithContextAndName() throws Exception {
         Context context = ApplicationProvider.getApplicationContext();
         String name = "my_data_store";
+        // make sure we cleanup before starting the test, in case a previous test left it dirty.
+        new File(context.getFilesDir(), "datastore/" + name).delete();
         RxDataStore<Byte> dataStore =
                 new RxDataStoreBuilder<Byte>(context, name, new TestingSerializer())
                         .build();
@@ -91,6 +93,8 @@
                 )
                         .build();
         assertThat(dataStore.data().blockingFirst()).isEqualTo(1);
+        dataStore.dispose();
+        dataStore.shutdownComplete().blockingAwait();
     }
 
     @Test
@@ -167,4 +171,17 @@
                 .build();
         assertThat(dataStore.data().blockingFirst()).isEqualTo(99);
     }
+
+    @Test
+    public void isDisposed() {
+        TestingSerializer testingSerializer = new TestingSerializer();
+        testingSerializer.setFailReadWithCorruptionException(true);
+        RxDataStore<Byte> dataStore = new RxDataStoreBuilder<Byte>(
+                () -> tempFolder.newFile(),
+                testingSerializer)
+                .build();
+        assertThat(dataStore.isDisposed()).isFalse();
+        dataStore.dispose();
+        assertThat(dataStore.isDisposed()).isTrue();
+    }
 }
diff --git a/datastore/datastore-rxjava2/src/main/java/androidx/datastore/rxjava2/RxDataStore.kt b/datastore/datastore-rxjava2/src/main/java/androidx/datastore/rxjava2/RxDataStore.kt
index 34d85e6..b81d814 100644
--- a/datastore/datastore-rxjava2/src/main/java/androidx/datastore/rxjava2/RxDataStore.kt
+++ b/datastore/datastore-rxjava2/src/main/java/androidx/datastore/rxjava2/RxDataStore.kt
@@ -28,10 +28,10 @@
 import kotlinx.coroutines.SupervisorJob
 import kotlinx.coroutines.async
 import kotlinx.coroutines.job
+import kotlinx.coroutines.rx2.asCompletable
 import kotlinx.coroutines.rx2.asFlowable
 import kotlinx.coroutines.rx2.asSingle
 import kotlinx.coroutines.rx2.await
-import kotlinx.coroutines.rx2.rxCompletable
 
 /**
  * A DataStore that supports RxJava operations on DataStore.
@@ -55,7 +55,7 @@
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
         public fun <T : Any> create(delegateDs: DataStore<T>, scope: CoroutineScope):
             RxDataStore<T> {
-                return RxDataStore<T>(delegateDs, scope)
+                return RxDataStore(delegateDs, scope)
             }
     }
 
@@ -68,16 +68,16 @@
     /**
      * Returns whether this DataStore is closed
      */
-    override fun isDisposed(): Boolean = scope.coroutineContext.job.isActive
+    override fun isDisposed(): Boolean = !scope.coroutineContext.job.isActive
 
     /**
      * Returns a completable that completes when the DataStore is completed. It is not safe to
      * create a new DataStore with the same file name until this has completed.
      */
     public fun shutdownComplete(): Completable =
-        rxCompletable(scope.coroutineContext.minusKey(Job)) {
-            scope.coroutineContext.job.join()
-        }
+        scope.coroutineContext.job.asCompletable(
+            scope.coroutineContext.minusKey(Job)
+        )
 
     /**
      * Gets a reactivex.Flowable of the data from DataStore. See [DataStore.data] for more information.
diff --git a/datastore/datastore-rxjava3/src/androidTest/java/androidx/datastore/rxjava3/RxDataStoreBuilderTest.java b/datastore/datastore-rxjava3/src/androidTest/java/androidx/datastore/rxjava3/RxDataStoreBuilderTest.java
index 313d329..57c6ad9 100644
--- a/datastore/datastore-rxjava3/src/androidTest/java/androidx/datastore/rxjava3/RxDataStoreBuilderTest.java
+++ b/datastore/datastore-rxjava3/src/androidTest/java/androidx/datastore/rxjava3/RxDataStoreBuilderTest.java
@@ -23,6 +23,11 @@
 import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler;
 import androidx.test.core.app.ApplicationProvider;
 
+import io.reactivex.rxjava3.core.Completable;
+import io.reactivex.rxjava3.core.Scheduler;
+import io.reactivex.rxjava3.core.Single;
+import io.reactivex.rxjava3.schedulers.Schedulers;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
@@ -31,11 +36,6 @@
 import java.util.concurrent.Executors;
 import java.util.concurrent.ThreadFactory;
 
-import io.reactivex.rxjava3.core.Completable;
-import io.reactivex.rxjava3.core.Scheduler;
-import io.reactivex.rxjava3.core.Single;
-import io.reactivex.rxjava3.schedulers.Schedulers;
-
 public class RxDataStoreBuilderTest {
     @Rule
     public TemporaryFolder tempFolder = new TemporaryFolder();
@@ -167,4 +167,17 @@
                 .build();
         assertThat(dataStore.data().blockingFirst()).isEqualTo(99);
     }
+
+    @Test
+    public void isDisposed() {
+        TestingSerializer testingSerializer = new TestingSerializer();
+        testingSerializer.setFailReadWithCorruptionException(true);
+        RxDataStore<Byte> dataStore = new RxDataStoreBuilder<Byte>(
+                () -> tempFolder.newFile(),
+                testingSerializer)
+                .build();
+        assertThat(dataStore.isDisposed()).isFalse();
+        dataStore.dispose();
+        assertThat(dataStore.isDisposed()).isTrue();
+    }
 }
\ No newline at end of file
diff --git a/datastore/datastore-rxjava3/src/main/java/androidx/datastore/rxjava3/RxDataStore.kt b/datastore/datastore-rxjava3/src/main/java/androidx/datastore/rxjava3/RxDataStore.kt
index cec4b2c..7a7691d 100644
--- a/datastore/datastore-rxjava3/src/main/java/androidx/datastore/rxjava3/RxDataStore.kt
+++ b/datastore/datastore-rxjava3/src/main/java/androidx/datastore/rxjava3/RxDataStore.kt
@@ -30,10 +30,10 @@
 import kotlinx.coroutines.SupervisorJob
 import kotlinx.coroutines.async
 import kotlinx.coroutines.job
+import kotlinx.coroutines.rx3.asCompletable
 import kotlinx.coroutines.rx3.asFlowable
 import kotlinx.coroutines.rx3.asSingle
 import kotlinx.coroutines.rx3.await
-import kotlinx.coroutines.rx3.rxCompletable
 
 /**
  * A DataStore that supports RxJava operations on DataStore.
@@ -70,16 +70,16 @@
     /**
      * Returns whether this DataStore is closed
      */
-    override fun isDisposed(): Boolean = scope.coroutineContext.job.isActive
+    override fun isDisposed(): Boolean = !scope.coroutineContext.job.isActive
 
     /**
      * Returns a completable that completes when the DataStore is completed. It is not safe to
      * create a new DataStore with the same file name until this has completed.
      */
     public fun shutdownComplete(): Completable =
-        rxCompletable(scope.coroutineContext.minusKey(Job)) {
-            scope.coroutineContext.job.join()
-        }
+        scope.coroutineContext.job.asCompletable(
+            scope.coroutineContext.minusKey(Job)
+        )
 
     /**
      * Gets a reactivex.Flowable of the data from DataStore. See [DataStore.data] for more information.
diff --git a/development/diagnose-build-failure/diagnose-build-failure.sh b/development/diagnose-build-failure/diagnose-build-failure.sh
index 6baa715..36504c2 100755
--- a/development/diagnose-build-failure/diagnose-build-failure.sh
+++ b/development/diagnose-build-failure/diagnose-build-failure.sh
@@ -376,9 +376,9 @@
 # command for running a build
 buildCommand="$(getBuildCommand "./gradlew --no-daemon $gradleArgs")"
 # command for moving state, running build, and moving state back
-fullFiltererCommand="$(getTestStateCommand $buildCommand)"
+fullFiltererCommand="$(getTestStateCommand --invert $buildCommand)"
 
-if $supportRoot/development/file-utils/diff-filterer.py $timeoutArg --assume-input-states-are-correct --work-path $tempDir $successState $tempDir/prev "$fullFiltererCommand"; then
+if $supportRoot/development/file-utils/diff-filterer.py $timeoutArg --assume-input-states-are-correct --work-path $tempDir $tempDir/prev $successState "$fullFiltererCommand"; then
   echo >&2
   echo "There should be something wrong with the above file state" >&2
   echo "Hopefully the output from diff-filterer.py above is enough information for you to figure out what is wrong" >&2
diff --git a/development/importMaven/src/main/kotlin/androidx/build/importMaven/KonanPrebuiltsDownloader.kt b/development/importMaven/src/main/kotlin/androidx/build/importMaven/KonanPrebuiltsDownloader.kt
index 826e9f6..b7c05eb 100644
--- a/development/importMaven/src/main/kotlin/androidx/build/importMaven/KonanPrebuiltsDownloader.kt
+++ b/development/importMaven/src/main/kotlin/androidx/build/importMaven/KonanPrebuiltsDownloader.kt
@@ -30,7 +30,6 @@
 import okio.Path
 import org.apache.logging.log4j.kotlin.logger
 import org.jetbrains.kotlin.gradle.utils.NativeCompilerDownloader
-import org.jetbrains.kotlin.konan.CompilerVersion.Companion.fromString
 import org.jetbrains.kotlin.konan.properties.resolvablePropertyList
 import org.jetbrains.kotlin.konan.properties.resolvablePropertyString
 import org.jetbrains.kotlin.konan.target.Architecture
@@ -58,7 +57,7 @@
         val project = ProjectService.createProject()
         val compiler = NativeCompilerDownloader(
             project = project,
-            compilerVersion = fromString(compilerVersion)
+            compilerVersion = compilerVersion
         )
         // make sure we have the local compiler downloaded so we can find the konan.properties
         compiler.downloadIfNeeded()
diff --git a/development/plot-benchmarks/src/chart-transforms.ts b/development/plot-benchmarks/src/chart-transforms.ts
deleted file mode 100644
index f76049f..0000000
--- a/development/plot-benchmarks/src/chart-transforms.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import type { ChartDataset } from "chart.js";
-import type { Data } from "./transforms.js";
-
-export function chartData(container: Array<Data>) {
-  let xmax = 0;
-  let datasets = [];
-  for (let i = 0; i < container.length; i += 1) {
-    // update xmax
-    xmax = Math.max(xmax, container[i].data.length);
-    datasets.push(chartDataset(container[i]));
-  }
-  return {
-    labels: xrange(xmax),
-    datasets: datasets
-  };
-}
-
-function xrange(xmax: number): Array<number> {
-  let labels = [];
-  for (let i = 1; i <= xmax; i += 1) {
-    labels.push(i);
-  }
-  return labels;
-}
-
-function chartDataset(data: Data): ChartDataset<'line'> {
-  return {
-    label: data.name,
-    data: data.data,
-    tension: 0.3
-  };
-}
diff --git a/development/plot-benchmarks/src/files.ts b/development/plot-benchmarks/src/files.ts
index a5e26ef..6d45cd9 100644
--- a/development/plot-benchmarks/src/files.ts
+++ b/development/plot-benchmarks/src/files.ts
@@ -1,14 +1,4 @@
-import type { Benchmarks } from "./schema.js";
-
-export interface FileMetadata {
-  file: File;
-  enabled: boolean;
-  benchmarks: Benchmarks;
-}
-
-export interface FileMetadataEvent {
-  entries: Array<FileMetadata>;
-}
+import type { Benchmarks } from "./types/benchmark.js";
 
 export async function readBenchmarks(file: File): Promise<Benchmarks> {
   const contents = await readFile(file);
diff --git a/development/plot-benchmarks/src/lib/App.svelte b/development/plot-benchmarks/src/lib/App.svelte
index e50908c..ffa3e57 100644
--- a/development/plot-benchmarks/src/lib/App.svelte
+++ b/development/plot-benchmarks/src/lib/App.svelte
@@ -1,23 +1,14 @@
 <script lang="ts">
   import type { Writable } from "svelte/store";
-  import { derived, writable } from "svelte/store";
-  import type { FileMetadata } from "../files.js";
-  import type { Benchmarks } from "../schema.js";
-  import Chart from "./Chart.svelte";
-  import Files from "./Files.svelte";
+  import { writable } from "svelte/store";
+  import type { FileMetadata } from "../types/files.js";
+  import Session from "./Session.svelte";
 
-  let entries: Writable<Array<FileMetadata>> = writable([]);
-  let containers = derived(entries, ($entries) => {
-    const containers: Array<Benchmarks> = [];
-    for (let i = 0; i < $entries.length; i += 1) {
-      let entry = $entries[i];
-      containers.push(entry.benchmarks);
-    }
-    return containers;
-  });
+  // Stores
+  let entries: Writable<FileMetadata[]> = writable([]);
 
-  function onFileMetadataEvent(event) {
-    const detail: Array<FileMetadata> = event.detail;
+  function onFilesChanged(event) {
+    const detail: FileMetadata[] = event.detail;
     if (detail) {
       const enabled = detail.filter((metadata) => metadata.enabled === true);
       $entries = [...enabled];
@@ -31,8 +22,5 @@
 </details>
 
 <div class="container">
-  <Files on:entries={onFileMetadataEvent} />
-  {#if $containers.length > 0}
-    <Chart {containers} />
-  {/if}
+  <Session fileEntries={$entries} on:entries={onFilesChanged} />
 </div>
diff --git a/development/plot-benchmarks/src/lib/Chart.svelte b/development/plot-benchmarks/src/lib/Chart.svelte
index 257f228..7a063a8 100644
--- a/development/plot-benchmarks/src/lib/Chart.svelte
+++ b/development/plot-benchmarks/src/lib/Chart.svelte
@@ -1,42 +1,33 @@
 <script lang="ts">
-  import type { LegendItem } from "chart.js";
+  import type { ChartType, LegendItem } from "chart.js";
   import { Chart } from "chart.js/auto";
   import { onMount } from "svelte";
-  import type { Readable, Writable } from "svelte/store";
-  import { derived, writable } from "svelte/store";
-  import { chartData } from "../chart-transforms.js";
-  import { saveToClipboard } from "../clipboard.js";
+  import { writable, type Writable } from "svelte/store";
+  import type { Data } from "../types/chart.js";
   import { LegendPlugin } from "../plugins.js";
-  import { expressionFilter } from "../regexp.js";
-  import type { Benchmarks } from "../schema.js";
-  import { benchmarksDataset } from "../transforms.js";
-  type FilterFn = (label: string) => boolean;
+  import Legend from "./Legend.svelte";
+  import { saveToClipboard as save } from "../clipboard.js";
 
-  export let containers: Readable<Array<Benchmarks>>;
+  export let data: Data;
+  export let chartType: ChartType = "line";
 
-  let canvas: HTMLCanvasElement;
-  let filter: Writable<FilterFn> = writable((_: string) => true);
-
-  let data = derived([containers, filter], ([$containers, $filter]) => {
-    return chartData(benchmarksDataset($containers, $filter));
-  });
-
-  let chart: Writable<Chart | undefined> = writable(null);
-  let legendLabels: Writable<Array<LegendItem> | undefined> = writable(null);
-
-  data.subscribe(($data) => {
+  $: {
     if ($chart) {
-      $chart.data = $data;
+      $chart.data = data;
       $chart.update();
     }
-  });
+  }
 
+  // State
+  let element: HTMLCanvasElement;
+  let chart: Writable<Chart | null> = writable(null);
+  let items: Writable<LegendItem[] | null> = writable(null);
+
+  // Effects
   onMount(() => {
-    const onUpdate = (updated: Chart) => {
-      chart.set(updated);
-      legendLabels.set(
-        updated.options.plugins.legend.labels.generateLabels(updated)
-      );
+    const onUpdate = (chart: Chart) => {
+      $chart = chart;
+      $items = chart.options.plugins.legend.labels.generateLabels(chart);
     };
     const plugins = {
       legend: {
@@ -46,119 +37,56 @@
         onUpdate: onUpdate,
       },
     };
-    chart.set(
-      new Chart(canvas, {
-        type: "line",
-        data: $data,
-        plugins: [LegendPlugin],
-        options: {
-          plugins: plugins,
-        },
-      })
-    );
+    $chart = new Chart(element, {
+      data: data,
+      type: chartType,
+      plugins: [LegendPlugin],
+      options: {
+        plugins: plugins,
+      },
+    });
   });
 
-  function onItemClick(item: LegendItem) {
-    return (_: Event) => {
-      // https://www.chartjs.org/docs/latest/samples/legend/html.html
-      $chart.setDatasetVisibility(
-        item.datasetIndex,
-        !$chart.isDatasetVisible(item.datasetIndex)
-      );
-      // Update chart
-      $chart.update();
-    };
-  }
-
-  function onFilterChanged(event: Event) {
-    const target = event.currentTarget as HTMLInputElement;
-    const value = target.value;
-    $filter = expressionFilter(value);
-  }
-
-  async function onCopy(_: Event) {
+  // Copy to clip board
+  async function copy(event: Event) {
     if ($chart) {
-      await saveToClipboard($chart);
+      await save($chart);
     }
   }
 </script>
 
 <article>
-  <button
-    class="copy outline"
-    data-tooltip="Copy chart to clipboard."
-    on:click={onCopy}
-  >
-    ⎘
-  </button>
-  <canvas id="chart" class="chart" bind:this={canvas} />
+  <div class="toolbar">
+    <button
+      class="btn outline"
+      data-tooltip="Copy chart to clipboard."
+      on:click={copy}
+    >
+      ⎘
+    </button>
+  </div>
+  <canvas id="chart" class="chart" bind:this={element} />
 </article>
 
-{#if $legendLabels && $legendLabels.length >= 0}
-  <article>
-    <div class="filter">
-      <label for="metricFilter">
-        <input
-          type="text"
-          id="metricFilter"
-          name="metricFilter"
-          placeholder="Filter metrics (regular expressions)"
-          autocomplete="off"
-          on:input={onFilterChanged}
-        />
-      </label>
-    </div>
-    <div class="legend">
-      {#each $legendLabels as label, index}
-        <div
-          class="item"
-          on:dblclick={onItemClick(label)}
-          aria-label="legend"
-          role="listitem"
-        >
-          <span
-            class="box"
-            style="background: {label.fillStyle}; border-color: {label.strokeStyle}; border-width: {label.lineWidth}px;"
-          />
-          <span
-            class="label"
-            style="text-decoration: {label.hidden ? 'line-through' : ''};"
-          >
-            {label.text}
-          </span>
-        </div>
-      {/each}
-    </div>
-  </article>
+{#if $items}
+  <Legend chart={$chart} items={$items} />
 {/if}
 
 <style>
-  .copy {
-    width: 4%;
-    position: relative;
-    padding: 0;
-    border: none;
-    /* Looked okay on my machine. */
-    right: -52rem;
-    top: -3rem;
-  }
   .chart {
     width: 100%;
   }
-  .legend {
-    display: flex;
-    flex-direction: column;
-    row-gap: 3px;
-  }
-  .item {
+  .toolbar {
+    padding: 0;
     display: flex;
     flex-direction: row;
-    column-gap: 10px;
-    align-items: center;
+    justify-content: flex-end;
   }
-  .item .box {
-    display: inline-block;
-    width: 20px;
-    height: 20px;
+
+  .toolbar .btn {
+    width: auto;
+    height: auto;
+    border: none;
+    padding: 5px;
   }
 </style>
diff --git a/development/plot-benchmarks/src/lib/Dataset.svelte b/development/plot-benchmarks/src/lib/Dataset.svelte
new file mode 100644
index 0000000..ce37d45
--- /dev/null
+++ b/development/plot-benchmarks/src/lib/Dataset.svelte
@@ -0,0 +1,68 @@
+<script lang="ts">
+  import { Session, type IndexedWrapper } from "../wrappers/session.js";
+
+  export let name: string;
+  export let datasetGroup: IndexedWrapper[];
+
+  let sources: Set<string>;
+  let sampledMetrics: Set<string>;
+  let metrics: Set<string>;
+
+  $: {
+    sources = Session.sources(datasetGroup);
+
+    let labels = datasetGroup
+      .map((indexed) => indexed.value.metricLabels())
+      .flat();
+
+    let sampled = datasetGroup
+      .map((indexed) => indexed.value.sampledLabels())
+      .flat();
+
+    metrics = new Set<string>(labels);
+    sampledMetrics = new Set<string>(sampled);
+  }
+</script>
+
+<div class="dataset">
+  <hgroup>
+    <div>{name}</div>
+    <div class="details">
+      <div class="sources">
+        {#each sources as source (source)}
+          <div>📁 <small>{source}</small></div>
+        {/each}
+      </div>
+      {#if metrics.size > 0}
+        <div class="metrics">
+          {#each metrics as metric}
+            <div>📏 <small>{metric}</small></div>
+          {/each}
+        </div>
+      {/if}
+      {#if sampledMetrics.size > 0}
+        <div class="sampled">
+          {#each sampledMetrics as metric}
+            <div>📏 <small>{metric}</small></div>
+          {/each}
+        </div>
+      {/if}
+    </div>
+  </hgroup>
+</div>
+
+<style>
+  .dataset {
+    margin: 0.5rem;
+    padding-top: 1rem;
+    padding-left: 1rem;
+    border: 1px solid #b3cee5;
+  }
+  .details {
+    padding-left: 1rem;
+  }
+
+  .details > div {
+    margin-top: 0.25rem;
+  }
+</style>
diff --git a/development/plot-benchmarks/src/lib/Files.svelte b/development/plot-benchmarks/src/lib/Files.svelte
deleted file mode 100644
index ffe09d6..0000000
--- a/development/plot-benchmarks/src/lib/Files.svelte
+++ /dev/null
@@ -1,124 +0,0 @@
-<script lang="ts">
-  import { createEventDispatcher } from "svelte";
-  import type { Writable } from "svelte/store";
-  import { writable } from "svelte/store";
-  import type { FileMetadata, FileMetadataEvent } from "../files.js";
-  import { readBenchmarks } from "../files.js";
-
-  let active: Writable<boolean> = writable(false);
-  let entries: Writable<Array<FileMetadata>> = writable([]);
-  let dispatcher = createEventDispatcher<FileMetadataEvent>();
-
-  entries.subscribe(($entries) => {
-    // Dispatch an event anytime files change.
-    dispatcher("entries", [...$entries]);
-  });
-
-  function onDropFile(event: DragEvent) {
-    handleDropFile(event); // async
-    active.set(false);
-    event.preventDefault();
-  }
-
-  async function handleDropFile(event: DragEvent) {
-    const items = [...event.dataTransfer.items];
-    const metadata = [];
-    if (items) {
-      for (let i = 0; i < items.length; i += 1) {
-        if (items[i].kind === "file") {
-          const file = items[i].getAsFile();
-          if (file.name.endsWith(".json")) {
-            const benchmarks = await readBenchmarks(file);
-            const entry: FileMetadata = {
-              enabled: true,
-              file: file,
-              benchmarks: benchmarks,
-            };
-            metadata.push(entry);
-          }
-        }
-      }
-      // Deep copy
-      $entries = [...$entries, ...metadata];
-    }
-  }
-
-  function onDragOver(event: DragEvent) {
-    active.set(true);
-    event.preventDefault();
-  }
-
-  function onDragLeave(event: DragEvent) {
-    active.set(false);
-    event.preventDefault();
-  }
-
-  function onChecked(entries: Array<FileMetadata>, index: number) {
-    return (event: Event) => {
-      // Deep copy
-      const copied = [...entries];
-      copied[index].enabled = !copied[index].enabled;
-      $entries = copied;
-    };
-  }
-</script>
-
-<article
-  id="drop"
-  class="drop"
-  class:active={$active}
-  on:drop={onDropFile}
-  on:dragover={onDragOver}
-  on:dragleave={onDragLeave}
->
-  {#if $entries.length > 0}
-    <div class="files">
-      {#each $entries as entry, index}
-        <div class="file">
-          <input
-            type="checkbox"
-            checked={entry.enabled}
-            on:change={onChecked($entries, index)}
-          />
-          <div class="index">
-            <span>{index + 1}</span>
-          </div>
-          <span class="label">{entry.file.name}</span>
-        </div>
-      {/each}
-    </div>
-  {:else}
-    <h5>Drag and drop benchmark results to get started.</h5>
-  {/if}
-</article>
-
-<style>
-  .active {
-    background-color: #00bfff;
-    outline: gray;
-    outline-style: groove;
-  }
-
-  .files {
-    display: flex;
-    flex-direction: column;
-    row-gap: 3px;
-  }
-
-  .file {
-    display: flex;
-    flex-direction: row;
-    column-gap: 10px;
-    align-items: center;
-  }
-
-  .index {
-    width: 2rem;
-    height: 2rem;
-    display: flex;
-    flex-direction: row;
-    align-items: center;
-    justify-content: center;
-    border: 2px solid sandybrown;
-  }
-</style>
diff --git a/development/plot-benchmarks/src/lib/Group.svelte b/development/plot-benchmarks/src/lib/Group.svelte
new file mode 100644
index 0000000..328e606
--- /dev/null
+++ b/development/plot-benchmarks/src/lib/Group.svelte
@@ -0,0 +1,20 @@
+<script lang="ts">
+  import { Session, type IndexedWrapper } from "../wrappers/session.js";
+  import Dataset from "./Dataset.svelte";
+
+  export let className: string;
+  export let datasetGroup: IndexedWrapper[];
+  let datasetNames: Set<string>;
+  $: {
+    datasetNames = Session.datasetNames(datasetGroup);
+  }
+</script>
+
+<details open>
+  <summary>{className}</summary>
+  <div class="details">
+    {#each datasetNames as name (name)}
+      <Dataset {datasetGroup} {name} />
+    {/each}
+  </div>
+</details>
diff --git a/development/plot-benchmarks/src/lib/Legend.svelte b/development/plot-benchmarks/src/lib/Legend.svelte
new file mode 100644
index 0000000..ef239f5
--- /dev/null
+++ b/development/plot-benchmarks/src/lib/Legend.svelte
@@ -0,0 +1,60 @@
+<script lang="ts">
+  import type { Chart, LegendItem } from "chart.js";
+  export let chart: Chart;
+  export let items: LegendItem[];
+
+  const handlerFactory = (item: LegendItem) => {
+    return (event: Event) => {
+      // https://www.chartjs.org/docs/latest/samples/legend/html.html
+      chart.setDatasetVisibility(
+        item.datasetIndex,
+        !chart.isDatasetVisible(item.datasetIndex)
+      );
+      chart.update();
+    };
+  };
+</script>
+
+<div class="legend">
+  <h6>Legend</h6>
+  {#each items as item (item)}
+    <div
+      class="item"
+      on:dblclick={handlerFactory(item)}
+      aria-label="legend"
+      role="listitem"
+    >
+      <span
+        class="box"
+        style="background: {item.fillStyle}; border-color: {item.strokeStyle}; border-width: {item.lineWidth}px;"
+      />
+      <span
+        class="label"
+        style="text-decoration: {item.hidden ? 'line-through' : ''};"
+      >
+        {item.text}
+      </span>
+    </div>
+  {/each}
+</div>
+
+<style>
+  .legend {
+    padding: 4px;
+    display: flex;
+    flex-direction: column;
+    row-gap: 3px;
+  }
+
+  .item {
+    display: flex;
+    flex-direction: row;
+    column-gap: 10px;
+    align-items: center;
+  }
+  .item .box {
+    display: inline-block;
+    width: 20px;
+    height: 20px;
+  }
+</style>
diff --git a/development/plot-benchmarks/src/lib/Session.svelte b/development/plot-benchmarks/src/lib/Session.svelte
new file mode 100644
index 0000000..4b0cf57
--- /dev/null
+++ b/development/plot-benchmarks/src/lib/Session.svelte
@@ -0,0 +1,117 @@
+<script lang="ts">
+  import { createEventDispatcher } from "svelte";
+  import type { FileMetadata } from "../types/files.js";
+  import { Session, type IndexedWrapper } from "../wrappers/session.js";
+  import Group from "./Group.svelte";
+  import type { FileMetadataEvent } from "../types/events.js";
+  import { writable, type Writable } from "svelte/store";
+  import { readBenchmarks } from "../files.js";
+  import type { Metrics } from "../types/data.js";
+  import { Transforms } from "../transforms/metric-transforms.js";
+  import type { Data, Series } from "../types/chart.js";
+  import { ChartDataTransforms } from "../transforms/data-transforms.js";
+  import { STANDARD_MAPPER } from "../transforms/standard-mappers.js";
+  import Chart from "./Chart.svelte";
+
+  export let fileEntries: FileMetadata[];
+
+  // State
+  let eventDispatcher = createEventDispatcher<FileMetadataEvent>();
+  let session: Session;
+  let metrics: Metrics<number>;
+  let series: Series[];
+  let chartData: Data;
+  let classGroups: Record<string, IndexedWrapper[]>;
+  let size: number;
+
+  // Stores
+  let activeDragDrop: Writable<boolean> = writable(false);
+
+  $: {
+    session = new Session(fileEntries);
+    metrics = Transforms.buildMetrics(session);
+    series = ChartDataTransforms.mapToSeries(metrics, STANDARD_MAPPER);
+    chartData = ChartDataTransforms.mapToDataset(series);
+    classGroups = session.classGroups;
+    size = session.fileNames.size;
+  }
+
+  // Helpers
+
+  function onDropFile(event: DragEvent) {
+    handleFileDragDrop(event); // async
+    $activeDragDrop = false;
+    event.preventDefault();
+  }
+
+  function onDragOver(event: DragEvent) {
+    $activeDragDrop = true;
+    event.preventDefault();
+  }
+
+  function onDragLeave(event: DragEvent) {
+    $activeDragDrop = false;
+    event.preventDefault();
+  }
+
+  async function handleFileDragDrop(event: DragEvent) {
+    const items = [...event.dataTransfer.items];
+    const newFiles: FileMetadata[] = [];
+    if (items) {
+      for (let i = 0; i < items.length; i += 1) {
+        if (items[i].kind === "file") {
+          const file = items[i].getAsFile();
+          if (file.name.endsWith(".json")) {
+            const benchmarks = await readBenchmarks(file);
+            const entry: FileMetadata = {
+              enabled: true,
+              file: file,
+              container: benchmarks,
+            };
+            newFiles.push(entry);
+          }
+        }
+      }
+      // Deep copy & notify
+      eventDispatcher("entries", [...fileEntries, ...newFiles]);
+    }
+  }
+</script>
+
+{#if size <= 0}
+  <article
+    id="drop"
+    class="drop"
+    class:active={$activeDragDrop}
+    on:drop={onDropFile}
+    on:dragover={onDragOver}
+    on:dragleave={onDragLeave}
+  >
+    <h5>Drag and drop benchmark results to get started.</h5>
+  </article>
+{:else}
+  <article
+    id="drop"
+    class="drop"
+    class:active={$activeDragDrop}
+    on:drop={onDropFile}
+    on:dragover={onDragOver}
+    on:dragleave={onDragLeave}
+  >
+    <h5>Benchmarks</h5>
+    {#each Object.entries(classGroups) as [className, wrappers]}
+      <Group {className} datasetGroup={wrappers} />
+    {/each}
+  </article>
+
+  {#if series.length > 0}
+    <Chart data={chartData} />
+  {/if}
+{/if}
+
+<style>
+  .active {
+    outline: beige;
+    outline-style: dashed;
+  }
+</style>
diff --git a/development/plot-benchmarks/src/regexp.ts b/development/plot-benchmarks/src/regexp.ts
deleted file mode 100644
index b99513d..0000000
--- a/development/plot-benchmarks/src/regexp.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-/**
- * Supports regular expressions, while falling back gracefully to substring matching.
- */
-export function expressionFilter(expression: string) {
-  let regExp: RegExp | null = null;
-  try {
-    regExp = new RegExp(expression, 'g')
-  } catch (error) {
-    // Invalid regular expression.
-    // Falling back to substring matching.
-    console.warn(`Invalid regular expression ${expression}. Falling back to substring matching.`)
-  }
-  if (regExp) {
-    return (label: string) => regExp.test(label);
-  }
-  return (label: string) => label.indexOf(expression) >= 0;
-}
diff --git a/development/plot-benchmarks/src/transforms.ts b/development/plot-benchmarks/src/transforms.ts
deleted file mode 100644
index be74e71..0000000
--- a/development/plot-benchmarks/src/transforms.ts
+++ /dev/null
@@ -1,123 +0,0 @@
-import type { Point } from "chart.js";
-import type { Benchmark, Benchmarks, Metrics, MetricsCollection } from "./schema.js";
-
-/**
- * A container for the chart data type.
- */
-export interface Data {
-  name: string;
-  data: Array<Point>;
-}
-
-export function benchmarksDataset(containers: Array<Benchmarks>, filterFn: (label: string) => boolean): Array<Data> {
-  const datasets: Array<Data> = [];
-  for (let i = 0; i < containers.length; i += 1) {
-    let container = containers[i];
-    for (let j = 0; j < container.benchmarks.length; j += 1) {
-      const benchmark = container.benchmarks[j];
-      // 1 based index
-      const prefix = datasetName(i + 1, benchmark);
-      const labels = metricLabels(benchmark);
-      for (let k = 0; k < labels.length; k += 1) {
-        const name = `${prefix}_${labels[k]}`;
-        if (filterFn(name)) {
-          const metrics = metricsData(benchmark, labels[k]);
-          if (metrics) {
-            const sampled = metrics.runs && Array.isArray(metrics.runs[0]);
-            if (sampled) {
-              // This is always the case for single metrics
-              const runs = metrics.runs as number[][];
-              datasets.push({
-                name: name,
-                // Compute histograms
-                data: histogramPoints(runs)
-              });
-            } else {
-
-              const runs = metrics.runs as number[];
-              datasets.push({
-                name: name,
-                // Might want to make this compatible for scatter plots
-                data: singlePoints(runs)
-              });
-            }
-          }
-        }
-      }
-    }
-  }
-  return datasets;
-}
-
-function datasetName(index: number, benchmark: Benchmark): string {
-  const className = benchmark.className;
-  const parts = className.split('.');
-  const lastIndex = parts.length - 1;
-  return `${index}_${parts[lastIndex]}_${benchmark.name}`;
-}
-
-function metricsData(benchmark: Benchmark, label: string): Metrics | undefined {
-  let data = metricsDataFor(benchmark.metrics, label);
-  if (data) {
-    return data;
-  }
-  return metricsDataFor(benchmark.sampledMetrics, label);
-}
-
-function metricsDataFor(collection: MetricsCollection, label: string): Metrics | undefined {
-  for (const key in collection) {
-    if (collection.hasOwnProperty(key) && key == label) {
-      return collection[key];
-    }
-  }
-  return undefined;
-}
-
-function metricLabels(benchmark: Benchmark): Array<string> {
-  const labels = labelsFor(benchmark.metrics);
-  const sampled = labelsFor(benchmark.sampledMetrics);
-  return [...labels, ...sampled];
-}
-
-function labelsFor(collection: MetricsCollection): Array<string> {
-  const labels = [];
-  if (collection) {
-    for (const key in collection) {
-      if (collection.hasOwnProperty(key)) {
-        labels.push(key);
-      }
-    }
-  }
-  return labels;
-}
-
-function histogramPoints(runs: Array<number[]>, buckets: number = 10): Array<Point> {
-  const flattened = runs.flat();
-  // Default comparator coerces types to string !
-  flattened.sort((a, b) => a - b); // in-place
-  const min = flattened[0];
-  const max = flattened[flattened.length - 1];
-  const histogram = new Array(buckets).fill(0);
-  const slots = buckets - 1; // The actual number of slots in the histogram
-  for (let i = 0; i < flattened.length; i += 1) {
-    let n = normalize(flattened[i], min, max);
-    let index = Math.ceil(n * slots);
-    histogram[index] = histogram[index] + 1;
-  }
-  return singlePoints(histogram);
-}
-
-function normalize(n: number, min: number, max: number): number {
-  return (n - min) / (max - min + 1e-5);
-}
-
-function singlePoints(runs: Array<number>): Array<Point> {
-  const points: Array<Point> = [];
-  for (let i = 0; i < runs.length; i += 1) {
-    points.push({
-      x: i + 1, // 1 based index
-      y: runs[i]
-    });
-  }
-  return points;
-}
diff --git a/development/plot-benchmarks/src/transforms/data-transforms.ts b/development/plot-benchmarks/src/transforms/data-transforms.ts
new file mode 100644
index 0000000..daadebb
--- /dev/null
+++ b/development/plot-benchmarks/src/transforms/data-transforms.ts
@@ -0,0 +1,66 @@
+import type { ChartDataset, ChartType, Point } from "chart.js";
+import type { Data, Series } from "../types/chart.js";
+import type { Metric, Metrics } from "../types/data.js";
+
+export interface Mapper<T = number> {
+  standard: (value: Metric<T>) => Series[];
+  sampled: (value: Metric<T[]>) => Series[];
+}
+
+/**
+ * Converts `Metrics` to the corresponding chart data structures.
+ */
+export class ChartDataTransforms {
+
+  static mapToSeries(metrics: Metrics<number>, mapper: Mapper<number>): Series[] {
+    const series: Series[] = [];
+    const standard = metrics.standard;
+    const sampled = metrics.sampled;
+    if (standard) {
+      for (let i = 0; i < standard.length; i += 1) {
+        const metric = standard[i];
+        const mapped = mapper.standard(metric);
+        series.push(...mapped);
+      }
+    }
+    if (sampled) {
+      for (let i = 0; i < sampled.length; i += 1) {
+        const metric = sampled[i];
+        const mapped = mapper.sampled(metric);
+        series.push(...mapped);
+      }
+    }
+    return series;
+  }
+
+  static mapToDataset(series: Series[]): Data {
+    let xmax = 0;
+    let datasets: ChartDataset[] = [];
+    for (let i = 0; i < series.length; i += 1) {
+      xmax = Math.max(xmax, series[i].data.length);
+      datasets.push(ChartDataTransforms.chartDataset(series[i]));
+    }
+    const chartData: Data = {
+      labels: ChartDataTransforms.xrange(xmax),
+      datasets: datasets
+    };
+    return chartData;
+  }
+
+  private static chartDataset<T extends ChartType>(series: Series): ChartDataset {
+    return {
+      label: series.label,
+      type: series.type,
+      data: series.data,
+      ...series.options
+    };
+  }
+
+  private static xrange(xmax: number): number[] {
+    let range = [];
+    for (let i = 1; i <= xmax; i += 1) {
+      range.push(i);
+    }
+    return range;
+  }
+}
diff --git a/development/plot-benchmarks/src/transforms/metric-transforms.ts b/development/plot-benchmarks/src/transforms/metric-transforms.ts
new file mode 100644
index 0000000..56a8763
--- /dev/null
+++ b/development/plot-benchmarks/src/transforms/metric-transforms.ts
@@ -0,0 +1,115 @@
+import type { ChartData, Metric, Metrics } from "../types/data.js";
+import type { Session } from "../wrappers/session.js";
+
+/**
+ * Helps with transforming benchmark data into something that can be visualized easily.
+ */
+export class Transforms {
+  private constructor() {
+    // static helpers.
+  }
+
+  static buildMetrics(session: Session): Metrics<number> {
+    const classGroups = Object.entries(session.classGroups);
+    const standard: Metric<number>[] = [];
+    const sampled: Metric<number[]>[] = [];
+    for (let i = 0; i < classGroups.length; i += 1) {
+      const [className, wrappers] = classGroups[i];
+      for (let j = 0; j < wrappers.length; j += 1) {
+        const wrapper = wrappers[j];
+        const source = wrapper.source;
+        const testName = wrapper.value.testName();
+        // standard
+        let labels = wrapper.value.metricLabels();
+        for (let k = 0; k < labels.length; k += 1) {
+          const label = labels[k];
+          const metric = wrapper.value.metric(label);
+          const charData: ChartData<number> = {
+            values: metric.runs
+          };
+          Transforms.add<number>(
+            standard,
+            className,
+            testName,
+            label,
+            source,
+            charData
+          );
+        }
+        // sampled
+        labels = wrapper.value.sampledLabels();
+        for (let k = 0; k < labels.length; k += 1) {
+          const label = labels[k];
+          const metric = wrapper.value.sampled(label);
+          const charData: ChartData<number[]> = {
+            values: metric.runs
+          };
+          Transforms.add<number[]>(
+            sampled,
+            className,
+            testName,
+            label,
+            source,
+            charData
+          );
+        }
+      }
+    }
+    const metrics: Metrics<number> = {
+      standard: standard,
+      sampled: sampled
+    };
+    return metrics;
+  }
+
+  private static add<T>(
+    metrics: Metric<T>[],
+    className: string,
+    testName: string,
+    label: string,
+    source: string,
+    data: ChartData<T>
+  ) {
+    const metric = Transforms.getOrCreate<T>(metrics, className, testName, label);
+    metric.data[source] = data;
+  }
+
+  private static getOrCreate<T>(
+    metrics: Metric<T>[],
+    className: string,
+    testName: string,
+    label: string
+  ): Metric<T> {
+    let metric: Metric<T> | null = Transforms.find(metrics, className, testName, label);
+    if (metric == null) {
+      const data: Record<string, ChartData<T>> = {};
+      metric = {
+        class: className,
+        benchmark: testName,
+        label: label,
+        data: data
+      }
+      metrics.push(metric);
+    }
+    return metric;
+  }
+
+  private static find<T>(
+    metrics: Metric<T>[],
+    className: string,
+    testName: string,
+    label: string
+  ): Metric<T> | null {
+    for (let i = 0; i < metrics.length; i += 1) {
+      const metric = metrics[i];
+      if (
+        metric.class === className &&
+        metric.benchmark === testName &&
+        metric.label === label
+      ) {
+        return metric;
+      }
+    }
+    return null;
+  }
+}
diff --git a/development/plot-benchmarks/src/transforms/standard-mappers.ts b/development/plot-benchmarks/src/transforms/standard-mappers.ts
new file mode 100644
index 0000000..8b928cd
--- /dev/null
+++ b/development/plot-benchmarks/src/transforms/standard-mappers.ts
@@ -0,0 +1,90 @@
+import type { Point } from "chart.js";
+import type { Series } from "../types/chart.js";
+import type { ChartData, Metric } from "../types/data.js";
+import type { Mapper } from "./data-transforms.js";
+
+function sampledMapper(metric: Metric<number[]>): Series[] {
+  const series: Series[] = [];
+  const data: Record<string, ChartData<number[]>> = metric.data;
+  const entries = Object.entries(data);
+  for (let i = 0; i < entries.length; i += 1) {
+    const [source, chartData] = entries[i];
+    const label = labelFor(metric, source);
+    const points = histogramPoints(chartData.values);
+    series.push({
+      label: label,
+      type: "line",
+      data: points,
+      options: {
+        tension: 0.3
+      }
+    });
+  }
+  return series;
+}
+
+function standardMapper(metric: Metric<number>): Series[] {
+  const series: Series[] = [];
+  const data: Record<string, ChartData<number>> = metric.data;
+  const entries = Object.entries(data);
+  for (let i = 0; i < entries.length; i += 1) {
+    const [source, chartData] = entries[i];
+    const label = labelFor(metric, source);
+    const points = singlePoints(chartData.values);
+    series.push({
+      label: label,
+      type: "line",
+      data: points,
+      options: {
+        tension: 0.3
+      }
+    });
+  }
+  return series;
+}
+
+function histogramPoints(runs: number[][], buckets: number = 10): Point[] {
+  const flattened = runs.flat();
+  // Default comparator coerces types to string !
+  flattened.sort((a, b) => a - b); // in-place
+  const min = flattened[0];
+  const max = flattened[flattened.length - 1];
+  const histogram = new Array(buckets).fill(0);
+  const slots = buckets - 1; // The actual number of slots in the histogram
+  for (let i = 0; i < flattened.length; i += 1) {
+    let n = normalize(flattened[i], min, max);
+    let index = Math.ceil(n * slots);
+    histogram[index] = histogram[index] + 1;
+  }
+  return singlePoints(histogram);
+}
+
+function singlePoints(runs: number[]): Point[] {
+  const points: Point[] = [];
+  for (let i = 0; i < runs.length; i += 1) {
+    points.push({
+      x: i + 1, // 1 based index
+      y: runs[i]
+    });
+  }
+  return points;
+}
+
+function normalize(n: number, min: number, max: number): number {
+  return (n - min) / (max - min + 1e-5);
+}
+
+/**
+ * Generates a series label.
+ */
+function labelFor<T>(metric: Metric<T>, source: string): string {
+  return `${source}[${metric.class} ${metric.benchmark} ${metric.label}]`;
+}
+
+/**
+ * The standard mapper.
+ */
+export const STANDARD_MAPPER: Mapper = {
+  standard: standardMapper,
+  sampled: sampledMapper
+};
diff --git a/development/plot-benchmarks/src/schema.ts b/development/plot-benchmarks/src/types/benchmark.ts
similarity index 100%
rename from development/plot-benchmarks/src/schema.ts
rename to development/plot-benchmarks/src/types/benchmark.ts
diff --git a/development/plot-benchmarks/src/types/chart.ts b/development/plot-benchmarks/src/types/chart.ts
new file mode 100644
index 0000000..0d77b63
--- /dev/null
+++ b/development/plot-benchmarks/src/types/chart.ts
@@ -0,0 +1,27 @@
+import type { ChartDataset, ChartType, Point } from "chart.js";
+
+/**
+ * The chart data container.
+ *
+ * Has relevant default X-Axis labels & corresponding datasets.
+ */
+export interface Data {
+  // X-axis labels.
+  labels: number[];
+  // The corresponding datasets.
+  datasets: ChartDataset[];
+}
+
+/**
+ * A single data-series being rendered in the chart.
+ *
+ * Used by a Mapper for data transformations.
+ */
+export interface Series {
+  label: string;
+  type: ChartType;
+  data: Array<Point>;
+  // Additional series options
+  // For e.g. https://www.chartjs.org/docs/latest/charts/line.html
+  options: object;
+}
diff --git a/development/plot-benchmarks/src/types/data.ts b/development/plot-benchmarks/src/types/data.ts
new file mode 100644
index 0000000..6e6955c
--- /dev/null
+++ b/development/plot-benchmarks/src/types/data.ts
@@ -0,0 +1,28 @@
+/**
+ * A container for raw benchmark data.
+ *
+ * Can be extended to store descriptive statistics about the raw data.
+ */
+export interface ChartData<T> {
+  values: T[];
+}
+
+/**
+ * A container for a Metric.
+ *
+ * This metric has all relevant comparables, in the data keyed by the source.
+ */
+export interface Metric<T> {
+  class: string;
+  benchmark: string;
+  label: string;
+  data: Record<string, ChartData<T>>;
+}
+
+/**
+ * A container for standard and sampled `Metric` instances.
+ */
+export interface Metrics<T = number> {
+  standard?: Metric<T>[];
+  sampled?: Metric<T[]>[];
+}
diff --git a/development/plot-benchmarks/src/types/events.ts b/development/plot-benchmarks/src/types/events.ts
new file mode 100644
index 0000000..48c4777
--- /dev/null
+++ b/development/plot-benchmarks/src/types/events.ts
@@ -0,0 +1,5 @@
+import type { FileMetadata } from "./files.js";
+
+export interface FileMetadataEvent {
+  entries: Array<FileMetadata>;
+}
diff --git a/development/plot-benchmarks/src/types/files.ts b/development/plot-benchmarks/src/types/files.ts
new file mode 100644
index 0000000..930369e
--- /dev/null
+++ b/development/plot-benchmarks/src/types/files.ts
@@ -0,0 +1,10 @@
+import type { Benchmarks } from "./benchmark.js";
+
+/**
+ * File information + associated benchmarks.
+ */
+export interface FileMetadata {
+  file: File;
+  enabled: boolean;
+  container: Benchmarks;
+}
diff --git a/development/plot-benchmarks/src/wrappers/benchmarks.ts b/development/plot-benchmarks/src/wrappers/benchmarks.ts
new file mode 100644
index 0000000..9bbc015
--- /dev/null
+++ b/development/plot-benchmarks/src/wrappers/benchmarks.ts
@@ -0,0 +1,54 @@
+import type { Benchmark, MetricsCollection, Sampled, Standard } from "../types/benchmark.js";
+
+export class BenchmarkWrapper {
+  constructor(
+    private benchmark: Benchmark,
+    private separator: string = '_'
+  ) {
+
+  }
+
+  datasetName(): string {
+    return `${this.className()}${this.separator}${this.benchmark.name}`;
+  }
+
+  metric(label: string): Standard | undefined {
+    return this.benchmark?.metrics[label];
+  }
+
+  sampled(label: string): Sampled | undefined {
+    return this.benchmark?.sampledMetrics[label];
+  }
+
+  metricLabels(): string[] {
+    return BenchmarkWrapper.labels(this.benchmark.metrics);
+  }
+
+  sampledLabels(): string[] {
+    return BenchmarkWrapper.labels(this.benchmark.sampledMetrics);
+  }
+
+  className(): string {
+    const className = this.benchmark.className;
+    const parts = className.split('.');
+    const lastIndex = parts.length - 1;
+    return parts[lastIndex];
+  }
+
+  testName(): string {
+    return this.benchmark.name;
+  }
+
+  private static labels(collection: MetricsCollection | null): string[] {
+    const labels: string[] = [];
+    if (collection) {
+      for (const key in collection) {
+        if (collection.hasOwnProperty(key)) {
+          labels.push(key);
+        }
+      }
+    }
+    return labels;
+  }
+
+}
diff --git a/development/plot-benchmarks/src/wrappers/session.ts b/development/plot-benchmarks/src/wrappers/session.ts
new file mode 100644
index 0000000..2581f92
--- /dev/null
+++ b/development/plot-benchmarks/src/wrappers/session.ts
@@ -0,0 +1,72 @@
+import { type FileMetadata } from "../types/files.js";
+import { BenchmarkWrapper } from "./benchmarks.js";
+
+export interface Indexed<T> {
+  // The source of the benchmark.
+  source: string;
+  // `source` index.
+  index: number;
+  // The underlying type.
+  value: T;
+}
+
+export type IndexedWrapper = Indexed<BenchmarkWrapper>;
+
+/**
+ * A Benchmarking plotting session.
+ */
+export class Session {
+  // className -> BenchmarkWrapper[]
+  public classGroups: Record<string, IndexedWrapper[]>;
+  // BenchmarkWrapper[]
+  public benchmarks: IndexedWrapper[];
+  public fileNames: Set<string>;
+
+  constructor(public files: FileMetadata[]) {
+    this.initialize();
+  }
+
+  private initialize() {
+    this.classGroups = {};
+    this.fileNames = new Set();
+    this.benchmarks = [];
+
+    for (let i = 0; i < this.files.length; i += 1) {
+      const fileIndex = i;
+      const fileMeta = this.files[fileIndex];
+      for (let j = 0; j < fileMeta.container.benchmarks.length; j += 1) {
+        const wrapper = new BenchmarkWrapper(fileMeta.container.benchmarks[j]);
+        const fileGroupKey = wrapper.className();
+        const classGroup = this.classGroups[fileGroupKey] || [];
+        const item = {
+          source: fileMeta.file.name,
+          index: fileIndex,
+          value: wrapper
+        };
+        classGroup.push(item);
+        // Update
+        this.classGroups[fileGroupKey] = classGroup;
+        this.fileNames.add(fileMeta.file.name);
+        this.benchmarks.push(item);
+      }
+    }
+  }
+
+  static datasetNames(wrappers: IndexedWrapper[]): Set<string> {
+    const names = new Set<string>();
+    for (let i = 0; i < wrappers.length; i += 1) {
+      const wrapper = wrappers[i];
+      names.add(wrapper.value.datasetName());
+    }
+    return names;
+  }
+
+  static sources(wrappers: IndexedWrapper[]): Set<string> {
+    const sources = new Set<string>();
+    for (let i = 0; i < wrappers.length; i += 1) {
+      sources.add(wrappers[i].source);
+    }
+    return sources;
+  }
+
+}
diff --git a/development/project-creator/compose-template/groupId/artifactId/build.gradle b/development/project-creator/compose-template/groupId/artifactId/build.gradle
index b3b12f8..17cf909 100644
--- a/development/project-creator/compose-template/groupId/artifactId/build.gradle
+++ b/development/project-creator/compose-template/groupId/artifactId/build.gradle
@@ -92,7 +92,6 @@
         if (desktopEnabled) {
             desktopTest {
                 dependsOn(jvmTest)
-                dependsOn(desktopMain)
                 dependencies {
                 }
             }
diff --git a/development/referenceDocs/switcher.py b/development/referenceDocs/switcher.py
index 63195ae..562d433 100755
--- a/development/referenceDocs/switcher.py
+++ b/development/referenceDocs/switcher.py
@@ -81,14 +81,14 @@
       stub = doc.replace(java_source_abs_path, kotlin_source_abs_path)
       # Always add the switcher for java files, switch to the package summary if
       # the page itself doesn't exist in kotlin
-      slug1 = "sed -i 's/<\/h1>/{}/' {}".format("<\/h1>\\n{% setvar page_path %}_page_path_{% endsetvar %}\\n{% setvar can_switch %}1{% endsetvar %}\\n{% include \"reference\/_java_switcher2.md\" %}",doc)
+      slug1 = "sed -i 's/<div id=\"refdoc-switcher-placeholder\">/{}/' {}".format("\\n{% setvar page_path %}_page_path_{% endsetvar %}\\n{% setvar can_switch %}1{% endsetvar %}\\n{% include \"reference\/_java_switcher2.md\" %}",doc)
     else:
       file_path = doc[len(kotlin_ref_root)+1:]
       stub = doc.replace(kotlin_source_abs_path, java_source_abs_path)
       if (both):
-        slug1 = "sed -i 's/<\/h1>/{}/' {}".format("<\/h1>\\n{% setvar page_path %}_page_path_{% endsetvar %}\\n{% setvar can_switch %}1{% endsetvar %}\\n{% include \"reference\/_kotlin_switcher2.md\" %}",doc)
+        slug1 = "sed -i 's/<div id=\"refdoc-switcher-placeholder\">/{}/' {}".format("\\n{% setvar page_path %}_page_path_{% endsetvar %}\\n{% setvar can_switch %}1{% endsetvar %}\\n{% include \"reference\/_kotlin_switcher2.md\" %}",doc)
       else:
-        slug1 = "sed -i 's/<\/h1>/{}/' {}".format("<\/h1>\\n{% include \"reference\/_kotlin_switcher2.md\" %}",doc)
+        slug1 = "sed -i 's/<div id=\"refdoc-switcher-placeholder\">/{}/' {}".format("\\n{% include \"reference\/_kotlin_switcher2.md\" %}",doc)
 
     os.system(slug1)
     if both or java:
diff --git a/docs-public/build.gradle b/docs-public/build.gradle
index 13fedb2..1e62f7f 100644
--- a/docs-public/build.gradle
+++ b/docs-public/build.gradle
@@ -30,10 +30,10 @@
     docs("androidx.asynclayoutinflater:asynclayoutinflater:1.1.0-alpha01")
     docs("androidx.asynclayoutinflater:asynclayoutinflater-appcompat:1.1.0-alpha01")
     docs("androidx.autofill:autofill:1.3.0-alpha01")
-    docs("androidx.benchmark:benchmark-common:1.2.0-alpha16")
-    docs("androidx.benchmark:benchmark-junit4:1.2.0-alpha16")
-    docs("androidx.benchmark:benchmark-macro:1.2.0-alpha16")
-    docs("androidx.benchmark:benchmark-macro-junit4:1.2.0-alpha16")
+    docs("androidx.benchmark:benchmark-common:1.2.0-beta01")
+    docs("androidx.benchmark:benchmark-junit4:1.2.0-beta01")
+    docs("androidx.benchmark:benchmark-macro:1.2.0-beta01")
+    docs("androidx.benchmark:benchmark-macro-junit4:1.2.0-beta01")
     docs("androidx.biometric:biometric:1.2.0-alpha05")
     docs("androidx.biometric:biometric-ktx:1.2.0-alpha05")
     samples("androidx.biometric:biometric-ktx-samples:1.2.0-alpha05")
@@ -144,11 +144,11 @@
     docs("androidx.drawerlayout:drawerlayout:1.2.0")
     docs("androidx.dynamicanimation:dynamicanimation:1.1.0-alpha02")
     docs("androidx.dynamicanimation:dynamicanimation-ktx:1.0.0-alpha03")
-    docs("androidx.emoji2:emoji2:1.4.0-beta05")
-    docs("androidx.emoji2:emoji2-bundled:1.4.0-beta05")
-    docs("androidx.emoji2:emoji2-emojipicker:1.4.0-beta05")
-    docs("androidx.emoji2:emoji2-views:1.4.0-beta05")
-    docs("androidx.emoji2:emoji2-views-helper:1.4.0-beta05")
+    docs("androidx.emoji2:emoji2:1.4.0-rc01")
+    docs("androidx.emoji2:emoji2-bundled:1.4.0-rc01")
+    docs("androidx.emoji2:emoji2-emojipicker:1.4.0-rc01")
+    docs("androidx.emoji2:emoji2-views:1.4.0-rc01")
+    docs("androidx.emoji2:emoji2-views-helper:1.4.0-rc01")
     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")
@@ -340,8 +340,9 @@
     docs("androidx.textclassifier:textclassifier:1.0.0-alpha04")
     docs("androidx.tracing:tracing:1.3.0-alpha02")
     docs("androidx.tracing:tracing-ktx:1.3.0-alpha02")
-    docs("androidx.tracing:tracing-perfetto:1.0.0-alpha17")
+    docs("androidx.tracing:tracing-perfetto:1.0.0-beta01")
     docs("androidx.tracing:tracing-perfetto-common:1.0.0-alpha16") // TODO(243405142) clean-up
+    docs("androidx.tracing:tracing-perfetto-handshake:1.0.0-beta01")
     docs("androidx.transition:transition:1.5.0-alpha01")
     docs("androidx.transition:transition-ktx:1.5.0-alpha01")
     docs("androidx.tv:tv-foundation:1.0.0-alpha07")
diff --git a/docs/api_guidelines/modules.md b/docs/api_guidelines/modules.md
index c125cc5..edbead0 100644
--- a/docs/api_guidelines/modules.md
+++ b/docs/api_guidelines/modules.md
@@ -359,7 +359,25 @@
 external developers and should be considered a last-resort when backporting
 behavior is not feasible.
 
-### Extension libraries (`-ktx`, `-guava`, etc.) {#module-ktx}
+### Platform extension (sidecar JAR) libraries {#module-extension}
+
+Platform extension or "sidecar JAR" libraries ship as part of the Android system
+image and are made available to developers through the `<uses-library>` manifest
+tag.
+
+Interfaces for platform extension libraries *may* be defined in Jetpack, like
+`androidx.window.extensions`, but must be implemented in the Android platform
+via AOSP or by device manufacturers. See
+[WindowManager Extensions](https://source.android.com/docs/core/display/windowmanager-extensions)
+for more details on the platform-side implementation of extension libraries,
+including motivations for their use.
+
+See
+[Platform extension (sidecar JAR) dependencies](/company/teams/androidx/api_guidelines#dependencies-sidecar)
+for guidelines on depending on extension libraries defined externally or within
+Jetpack.
+
+### Framework- and language-specific libraries (`-ktx`, `-guava`, etc.) {#module-ktx}
 
 New libraries should prefer Kotlin sources with built-in Java compatibility via
 `@JvmName` and other affordances of the Kotlin language. They may optionally
diff --git a/docs/manual_prebuilts_dance.md b/docs/manual_prebuilts_dance.md
deleted file mode 100644
index 8b7e336..0000000
--- a/docs/manual_prebuilts_dance.md
+++ /dev/null
@@ -1,23 +0,0 @@
-# Manually updating prebuilts
-
-NOTE Most updates to internal AndroidX prebuilts should occur via the scripted
-[The Prebuilts Dance™](/company/teams/androidx/releasing_prebuilts_dance.md#the-prebuilts-dance™)
-process.
-
-Public-facing Jetpack library docs are built from prebuilts to reconcile our
-monolithic docs update process with our independently-versioned library release
-process.
-
-Submit the following changes in the same Gerrit topic so that they merge in the
-same build ID:
-
-1.  Commit your release artifact to the AndroidX AOSP checkout's local Maven
-    repository under `prebuilts/androidx/internal`.
-
-2.  Update the version for your library in the public docs configuration
-    ([docs-public/build.gradle](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:docs-public/build.gradle)).
-    If this is the first time that your library is being published, you will
-    need to add a new entry.
-
-Once both changes are, make sure to note the build ID where they landed. You
-will need to put this in your release request bug for Docs team.
diff --git a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerView.kt b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerView.kt
index b46c684..eb82c43 100644
--- a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerView.kt
+++ b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiPickerView.kt
@@ -212,6 +212,9 @@
                         }
                     }
                     bodyLayoutManager.scrollToPositionWithOffset(this, 0)
+                    // The scroll position change will not be reflected until the next layout call,
+                    // so force a new layout call here.
+                    invalidate()
                 }
             })
 
diff --git a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiViewHolder.kt b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiViewHolder.kt
index f63400d..431a080 100644
--- a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiViewHolder.kt
+++ b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/EmojiViewHolder.kt
@@ -21,9 +21,11 @@
 import android.view.LayoutInflater
 import android.view.View.OnLongClickListener
 import android.view.ViewGroup.LayoutParams
+import android.view.WindowManager
 import android.view.accessibility.AccessibilityEvent
 import android.widget.GridLayout
 import android.widget.PopupWindow
+import android.widget.Toast
 import androidx.recyclerview.widget.RecyclerView.ViewHolder
 import kotlin.math.roundToInt
 
@@ -111,12 +113,17 @@
                 emojiView.context.resources
                     .getDimensionPixelSize(R.dimen.emoji_picker_popup_view_elevation)
                     .toFloat()
-            showAtLocation(
-                popupView,
-                Gravity.NO_GRAVITY,
-                x.roundToInt(),
-                y
-            )
+            try {
+                showAtLocation(
+                    emojiView,
+                    Gravity.NO_GRAVITY,
+                    x.roundToInt(),
+                    y
+                )
+            } catch (e: WindowManager.BadTokenException) {
+                Toast.makeText(
+                    context, "Don't use EmojiPickerView inside a Popup", Toast.LENGTH_LONG).show()
+            }
         }
     }
 
diff --git a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/utils/FileCache.kt b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/utils/FileCache.kt
index 4042321..af21998 100644
--- a/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/utils/FileCache.kt
+++ b/emoji2/emoji2-emojipicker/src/main/java/androidx/emoji2/emojipicker/utils/FileCache.kt
@@ -19,12 +19,15 @@
 import android.content.Context
 import android.content.pm.PackageManager
 import android.os.Build
+import android.util.Log;
+import androidx.annotation.GuardedBy
 import androidx.annotation.RequiresApi
 import androidx.annotation.VisibleForTesting
 import androidx.core.content.ContextCompat
 import androidx.emoji2.emojipicker.BundledEmojiListLoader
 import androidx.emoji2.emojipicker.EmojiViewItem
 import java.io.File
+import java.io.IOException
 
 /**
  * A class that manages cache files for the emoji picker. All cache files are stored in DE
@@ -33,19 +36,21 @@
  *
  * Currently this class is only used by [BundledEmojiListLoader]. All renderable emojis will be
  * cached by categories under
- * /app.package.name/cache/emoji_picker/<osVersion|appVersion>
+ * /app.package.name/cache/emoji_picker/<osVersion.appVersion>
  * /emoji.<emojiPickerVersion>.<emojiCompatMetadataHashCode>.<categoryIndex>.<ifEmoji12Supported>
  */
 internal class FileCache(context: Context) {
 
     @VisibleForTesting
+    @GuardedBy("lock")
     internal val emojiPickerCacheDir: File
     private val currentProperty: String
+    private val lock = Any()
 
     init {
         val osVersion = "${Build.VERSION.SDK_INT}_${Build.TIME}"
         val appVersion = getVersionCode(context)
-        currentProperty = "$osVersion|$appVersion"
+        currentProperty = "$osVersion.$appVersion"
         emojiPickerCacheDir =
             File(getDeviceProtectedStorageContext(context).cacheDir, EMOJI_PICKER_FOLDER)
         if (!emojiPickerCacheDir.exists())
@@ -57,15 +62,17 @@
         key: String,
         defaultValue: () -> List<EmojiViewItem>
     ): List<EmojiViewItem> {
-        val targetDir = File(emojiPickerCacheDir, currentProperty)
-        // No matching cache folder for current property, clear stale cache directory if any
-        if (!targetDir.exists()) {
-            emojiPickerCacheDir.listFiles()?.forEach { it.deleteRecursively() }
-            targetDir.mkdirs()
-        }
+        synchronized(lock) {
+            val targetDir = File(emojiPickerCacheDir, currentProperty)
+            // No matching cache folder for current property, clear stale cache directory if any
+            if (!targetDir.exists()) {
+                emojiPickerCacheDir.listFiles()?.forEach { it.deleteRecursively() }
+                targetDir.mkdirs()
+            }
 
-        val targetFile = File(targetDir, key)
-        return readFrom(targetFile) ?: writeTo(targetFile, defaultValue)
+            val targetFile = File(targetDir, key)
+            return readFrom(targetFile) ?: writeTo(targetFile, defaultValue)
+        }
     }
 
     private fun readFrom(targetFile: File): List<EmojiViewItem>? {
@@ -82,6 +89,14 @@
         defaultValue: () -> List<EmojiViewItem>
     ): List<EmojiViewItem> {
         val data = defaultValue.invoke()
+        if (targetFile.exists()) {
+            if (!targetFile.delete()) {
+                Log.wtf(TAG, "Can't delete file: $targetFile");
+            }
+        }
+        if (!targetFile.createNewFile()) {
+            throw IOException("Can't create file: $targetFile")
+        }
         targetFile.bufferedWriter()
             .use { out ->
                 for (emoji in data) {
@@ -122,6 +137,7 @@
             }
 
         private const val EMOJI_PICKER_FOLDER = "emoji_picker"
+        private const val TAG = "emojipicker.FileCache";
     }
 
     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
diff --git a/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java b/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java
index 35c63ca..ca2a19f 100644
--- a/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java
+++ b/exifinterface/exifinterface/src/androidTest/java/androidx/exifinterface/media/ExifInterfaceTest.java
@@ -579,97 +579,36 @@
     }
 
     @Test
-    @LargeTest
-    @Ignore("b/290382533")
+    @SmallTest
     public void testDoNotFailOnCorruptedImage() throws Throwable {
-        // ExifInterface shouldn't raise any exceptions except an IOException when unable to open
-        // a file, even with a corrupted image. Generates randomly corrupted image stream for
-        // testing. Uses Epoch date count as random seed so that we can reproduce a broken test.
-        long seed = System.currentTimeMillis() / (86400 * 1000);
-        Log.d(TAG, "testDoNotFailOnCorruptedImage random seed: " + seed);
-        Random random = new Random(seed);
+        Random random = new Random(/* seed= */ 0);
         byte[] bytes = new byte[8096];
+        random.nextBytes(bytes);
+        // Overwrite the start of the random bytes with some JPEG-like data, so it starts like a
+        // plausible image with EXIF data.
         ByteBuffer buffer = ByteBuffer.wrap(bytes);
-        for (int i = 0; i < TEST_NUMBER_OF_CORRUPTED_IMAGE_STREAMS; i++) {
-            buffer.clear();
-            random.nextBytes(bytes);
-            if (!randomlyCorrupted(random)) {
-                buffer.put(ExifInterface.JPEG_SIGNATURE);
-            }
-            if (!randomlyCorrupted(random)) {
-                buffer.put(ExifInterface.MARKER_APP1);
-            }
-            buffer.putShort((short) (random.nextInt(100) + 300));
-            if (!randomlyCorrupted(random)) {
-                buffer.put(ExifInterface.IDENTIFIER_EXIF_APP1);
-            }
-            if (!randomlyCorrupted(random)) {
-                buffer.putShort(ExifInterface.BYTE_ALIGN_MM);
-            }
-            if (!randomlyCorrupted(random)) {
-                buffer.put((byte) 0);
-                buffer.put(ExifInterface.START_CODE);
-            }
-            buffer.putInt(8);
+        buffer.put(ExifInterface.JPEG_SIGNATURE);
+        buffer.put(ExifInterface.MARKER_APP1);
+        buffer.putShort((short) 350);
+        buffer.put(ExifInterface.IDENTIFIER_EXIF_APP1);
+        buffer.putShort(ExifInterface.BYTE_ALIGN_MM);
+        buffer.put((byte) 0);
+        buffer.put(ExifInterface.START_CODE);
+        buffer.putInt(8);
+        // Number of primary tag directories
+        buffer.putShort((short) 1);
+        // Corruption starts here
 
-            // Primary Tags
-            int numberOfDirectory = random.nextInt(8) + 1;
-            if (!randomlyCorrupted(random)) {
-                buffer.putShort((short) numberOfDirectory);
-            }
-            for (int j = 0; j < numberOfDirectory; j++) {
-                generateRandomExifTag(buffer, ExifInterface.IFD_TYPE_PRIMARY, random);
-            }
-            if (!randomlyCorrupted(random)) {
-                buffer.putInt(buffer.position() - 8);
-            }
-
-            // Thumbnail Tags
-            numberOfDirectory = random.nextInt(8) + 1;
-            if (!randomlyCorrupted(random)) {
-                buffer.putShort((short) numberOfDirectory);
-            }
-            for (int j = 0; j < numberOfDirectory; j++) {
-                generateRandomExifTag(buffer, ExifInterface.IFD_TYPE_THUMBNAIL, random);
-            }
-            if (!randomlyCorrupted(random)) {
-                buffer.putInt(buffer.position() - 8);
-            }
-
-            // Preview Tags
-            numberOfDirectory = random.nextInt(8) + 1;
-            if (!randomlyCorrupted(random)) {
-                buffer.putShort((short) numberOfDirectory);
-            }
-            for (int j = 0; j < numberOfDirectory; j++) {
-                generateRandomExifTag(buffer, ExifInterface.IFD_TYPE_PREVIEW, random);
-            }
-            if (!randomlyCorrupted(random)) {
-                buffer.putInt(buffer.position() - 8);
-            }
-
-            if (!randomlyCorrupted(random)) {
-                buffer.put(ExifInterface.MARKER);
-            }
-            if (!randomlyCorrupted(random)) {
-                buffer.put(ExifInterface.MARKER_EOI);
-            }
-
-            try {
-                new ExifInterface(new ByteArrayInputStream(bytes));
-                // Always success
-            } catch (IOException e) {
-                fail("Should not reach here!");
-            }
-        }
+        ExifInterface exifInterface = new ExifInterface(new ByteArrayInputStream(bytes));
+        exifInterface.getAttribute(ExifInterface.TAG_ARTIST);
+        // Test will fail if the ExifInterface constructor or getter throw an exception.
     }
 
     @Test
     @SmallTest
-    @Ignore("b/290382533")
     public void testSetGpsInfo() throws IOException {
         final String provider = "ExifInterfaceTest";
-        final long timestamp = System.currentTimeMillis();
+        final long timestamp = 1689328448000L; // 2023-07-14T09:54:32.000Z
         final float speedInMeterPerSec = 36.627533f;
         Location location = new Location(provider);
         location.setLatitude(TEST_LATITUDE_VALID_VALUES[TEST_LATITUDE_VALID_VALUES.length - 1]);
@@ -761,7 +700,6 @@
      */
     @Test
     @SmallTest
-    @Ignore("b/290382533")
     public void testGetSetDateTime() throws IOException {
         final long expectedGetDatetimeValue =
                 1454027547000L /* TAG_DATETIME value ("2016:01:29 18:32:27") converted to msec */
@@ -788,12 +726,12 @@
                 exif.getAttribute(ExifInterface.TAG_OFFSET_TIME_DIGITIZED));
 
         // Test setting datetime values
-        final long currentTimeStamp = System.currentTimeMillis();
+        final long newTimestamp = 1689328448000L; // 2023-07-14T09:54:32.000Z
         final long expectedDatetimeOffsetLongValue = 32400000L;
-        exif.setDateTime(currentTimeStamp);
+        exif.setDateTime(newTimestamp);
         exif.saveAttributes();
         exif = new ExifInterface(imageFile.getAbsolutePath());
-        assertEquals(currentTimeStamp - expectedDatetimeOffsetLongValue, (long) exif.getDateTime());
+        assertEquals(newTimestamp - expectedDatetimeOffsetLongValue, (long) exif.getDateTime());
 
         // Test that setting null throws NPE
         try {
@@ -1433,31 +1371,6 @@
         assertEquals(expectedValue.thumbnailHeight, thumbnailBitmap.getHeight());
     }
 
-    private void generateRandomExifTag(ByteBuffer buffer, int ifdType, Random random) {
-        ExifInterface.ExifTag[] tagGroup = ExifInterface.EXIF_TAGS[ifdType];
-        ExifInterface.ExifTag tag = tagGroup[random.nextInt(tagGroup.length)];
-        if (!randomlyCorrupted(random)) {
-            buffer.putShort((short) tag.number);
-        }
-        int dataFormat = random.nextInt(ExifInterface.IFD_FORMAT_NAMES.length);
-        if (!randomlyCorrupted(random)) {
-            buffer.putShort((short) dataFormat);
-        }
-        buffer.putInt(1);
-        int dataLength = ExifInterface.IFD_FORMAT_BYTES_PER_FORMAT[dataFormat];
-        if (dataLength > 4) {
-            buffer.putShort((short) random.nextInt(8096 - dataLength));
-            buffer.position(buffer.position() + 2);
-        } else {
-            buffer.position(buffer.position() + 4);
-        }
-    }
-
-    private boolean randomlyCorrupted(Random random) {
-        // Corrupts somewhere in a possibility of 1/500.
-        return random.nextInt(500) == 0;
-    }
-
     private void closeQuietly(Closeable closeable) {
         if (closeable != null) {
             try {
diff --git a/fragment/fragment-ktx/build.gradle b/fragment/fragment-ktx/build.gradle
index 2c3859d..05b9bce 100644
--- a/fragment/fragment-ktx/build.gradle
+++ b/fragment/fragment-ktx/build.gradle
@@ -25,7 +25,7 @@
 
 dependencies {
     api(project(":fragment:fragment"))
-    api(projectOrArtifact(":activity:activity-ktx")) {
+    api("androidx.activity:activity-ktx:1.8.0-alpha06") {
         because "Mirror fragment dependency graph for -ktx artifacts"
     }
     api("androidx.core:core-ktx:1.2.0") {
diff --git a/fragment/fragment/build.gradle b/fragment/fragment/build.gradle
index ed38f46..c3738bc 100644
--- a/fragment/fragment/build.gradle
+++ b/fragment/fragment/build.gradle
@@ -29,7 +29,7 @@
     api("androidx.collection:collection:1.1.0")
     api("androidx.viewpager:viewpager:1.0.0")
     api("androidx.loader:loader:1.0.0")
-    api(projectOrArtifact(":activity:activity"))
+    api("androidx.activity:activity:1.8.0-alpha06")
     api("androidx.lifecycle:lifecycle-runtime:2.6.1")
     api("androidx.lifecycle:lifecycle-livedata-core:2.6.1")
     api("androidx.lifecycle:lifecycle-viewmodel:2.6.1")
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/OnBackStackChangedListenerTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/OnBackStackChangedListenerTest.kt
index 8c0a822..846297a 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/OnBackStackChangedListenerTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/OnBackStackChangedListenerTest.kt
@@ -498,7 +498,11 @@
                 executePendingTransactions()
             }
 
-            assertThat(startedCount).isEqualTo(1)
+            if (FragmentManager.USE_PREDICTIVE_BACK) {
+                assertThat(startedCount).isEqualTo(1)
+            } else {
+                assertThat(startedCount).isEqualTo(0)
+            }
             assertThat(committedCount).isEqualTo(0)
 
             withActivity {
@@ -560,14 +564,22 @@
                 executePendingTransactions()
             }
 
-            assertThat(startedCount).isEqualTo(1)
+            if (FragmentManager.USE_PREDICTIVE_BACK) {
+                assertThat(startedCount).isEqualTo(1)
+            } else {
+                assertThat(startedCount).isEqualTo(0)
+            }
             assertThat(committedCount).isEqualTo(0)
 
             withActivity {
                 onBackPressedDispatcher.dispatchOnBackCancelled()
             }
 
-            assertThat(startedCount).isEqualTo(1)
+            if (FragmentManager.USE_PREDICTIVE_BACK) {
+                assertThat(startedCount).isEqualTo(1)
+            } else {
+                assertThat(startedCount).isEqualTo(0)
+            }
             assertThat(committedCount).isEqualTo(0)
 
             assertThat(fragment2).isSameInstanceAs(fragmentManager.findFragmentById(R.id.content))
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/DefaultSpecialEffectsController.kt b/fragment/fragment/src/main/java/androidx/fragment/app/DefaultSpecialEffectsController.kt
index c83eb7b..c8954a0 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/DefaultSpecialEffectsController.kt
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/DefaultSpecialEffectsController.kt
@@ -69,7 +69,6 @@
         // and transitions that need to be executed
         val animations = mutableListOf<AnimationInfo>()
         val transitions = mutableListOf<TransitionInfo>()
-        val awaitingContainerChanges = operations.toMutableList()
 
         // sync animations together before we start loading them.
         syncAnimations(operations)
@@ -90,31 +89,45 @@
             // Ensure that if the Operation is synchronously complete, we still
             // apply the container changes before the Operation completes
             operation.addCompletionListener {
-                if (awaitingContainerChanges.contains(operation)) {
-                    awaitingContainerChanges.remove(operation)
-                    applyContainerChanges(operation)
-                }
+                applyContainerChangesToOperations { operations }
             }
         }
 
         // Start transition special effects
-        val startedTransitions = startTransitions(transitions, awaitingContainerChanges, isPop,
-            firstOut, lastIn)
+        val startedTransitions = startTransitions(transitions, isPop, firstOut, lastIn)
         val startedAnyTransition = startedTransitions.containsValue(true)
 
-        // Start animation special effects
-        startAnimations(animations, awaitingContainerChanges, startedAnyTransition,
-            startedTransitions)
-        for (operation: Operation in awaitingContainerChanges) {
-            applyContainerChanges(operation)
+        // Collect Animation and Animator Effects
+        startAnimations(animations, startedAnyTransition, startedTransitions)
+
+        // Run all of the Animation, Animator, and NoOp Effects we have collected
+        for (i in operations.indices) {
+            val operation = operations[i]
+            for (j in operation.effects.indices) {
+                val effect = operation.effects[j]
+                effect.onStart(container)
+                effect.onCommit(container)
+            }
         }
-        awaitingContainerChanges.clear()
+
+        applyContainerChangesToOperations { operations }
         if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
             Log.v(FragmentManager.TAG,
                 "Completed executing operations from $firstOut to $lastIn")
         }
     }
 
+    private inline fun applyContainerChangesToOperations(operationsInput: () -> List<Operation>) {
+        val operations = operationsInput.invoke()
+        for (i in operations.indices) {
+            val currentOperation = operations[i]
+            if (currentOperation.isAwaitingContainerChanges) {
+                applyContainerChanges(currentOperation)
+                currentOperation.isAwaitingContainerChanges = false
+            }
+        }
+    }
+
     /**
      * Syncs the animations of all other operations with the animations of the last operation.
      */
@@ -135,36 +148,35 @@
     @SuppressLint("NewApi", "PrereleaseSdkCoreDependency")
     private fun startAnimations(
         animationInfos: List<AnimationInfo>,
-        awaitingContainerChanges: MutableList<Operation>,
         startedAnyTransition: Boolean,
         startedTransitions: Map<Operation, Boolean>
     ) {
-        val context = container.context
         val animationsToRun = mutableListOf<AnimationInfo>()
-
-        // First run Animators
         var startedAnyAnimator = false
-        for (animationInfo: AnimationInfo in animationInfos) {
-            if (animationInfo.isVisibilityUnchanged) {
+        // Find all Animators and add the effect to the operation
+        for (animatorInfo: AnimationInfo in animationInfos) {
+            val context = container.context
+            val operation: Operation = animatorInfo.operation
+            if (animatorInfo.isVisibilityUnchanged) {
                 // No change in visibility, so we can immediately complete the animation
-                animationInfo.completeSpecialEffect()
+                operation.effects.add(NoOpEffect(animatorInfo))
                 continue
             }
-            val anim = animationInfo.getAnimation(context)
+            val anim = animatorInfo.getAnimation(context)
             if (anim == null) {
                 // No Animator or Animation, so we can immediately complete the animation
-                animationInfo.completeSpecialEffect()
+                operation.effects.add(NoOpEffect(animatorInfo))
                 continue
             }
             val animator = anim.animator
             if (animator == null) {
                 // We must have an Animation to run. Save those for a second pass
-                animationsToRun.add(animationInfo)
+                animationsToRun.add(animatorInfo)
                 continue
             }
 
             // First make sure we haven't already started a Transition for this Operation
-            val operation: Operation = animationInfo.operation
+
             val fragment = operation.fragment
             val startedTransition = startedTransitions[operation] == true
             if (startedTransition) {
@@ -173,105 +185,22 @@
                         "Ignoring Animator set on $fragment as this Fragment was involved " +
                             "in a Transition.")
                 }
-                animationInfo.completeSpecialEffect()
+                operation.effects.add(NoOpEffect(animatorInfo))
                 continue
             }
-
-            // Okay, let's run the Animator!
             startedAnyAnimator = true
             val isHideOperation = operation.finalState === Operation.State.GONE
             if (isHideOperation) {
                 // We don't want to immediately applyState() to hide operations as that
                 // immediately stops the Animator. Instead we'll applyState() manually
                 // when the Animator ends.
-                awaitingContainerChanges.remove(operation)
+                operation.isAwaitingContainerChanges = false
             }
-            val viewToAnimate = fragment.mView
-            container.startViewTransition(viewToAnimate)
-            animator.addListener(object : AnimatorListenerAdapter() {
-                override fun onAnimationEnd(anim: Animator) {
-                    container.endViewTransition(viewToAnimate)
-                    if (isHideOperation) {
-                        // Specifically for hide operations with Animator, we can't
-                        // applyState until the Animator finishes
-                        operation.finalState.applyState(viewToAnimate)
-                    }
-                    animationInfo.completeSpecialEffect()
-                    if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
-                        Log.v(FragmentManager.TAG,
-                            "Animator from operation $operation has ended.")
-                    }
-                }
-            })
-            animator.setTarget(viewToAnimate)
-            if (Build.VERSION.SDK_INT >= 34 && operation.fragment.mTransitioning) {
-                val animatorSet = animationInfo.getAnimation(container.context)?.animator
-                if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
-                    Log.v(
-                        FragmentManager.TAG,
-                        "Adding BackProgressCallbacks for Animators to operation $operation"
-                    )
-                }
-                operation.addBackProgressCallbacks({ backEvent ->
-                    if (animatorSet != null) {
-                        val totalDuration = Api24Impl.totalDuration(animatorSet)
-                        var time = (backEvent.progress * totalDuration).toLong()
-                        // We cannot let the time get to 0 or the totalDuration to avoid
-                        // completing the operation accidentally.
-                        if (time == 0L) {
-                            time = 1L
-                        }
-                        if (time == totalDuration) {
-                            time = totalDuration - 1
-                        }
-                        if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
-                            Log.v(
-                                FragmentManager.TAG,
-                                "Setting currentPlayTime to $time for Animator $animatorSet on " +
-                                    "operation $operation"
-                            )
-                        }
-                        Api26Impl.setCurrentPlayTime(animatorSet, time)
-                    }
-                }) {
-                    if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
-                        Log.v(
-                            FragmentManager.TAG,
-                            "Back Progress Callback Animator has been started."
-                        )
-                    }
-                    animatorSet?.start()
-                }
-            } else {
-                animator.start()
-            }
-            if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
-                Log.v(FragmentManager.TAG,
-                    "Animator from operation $operation has started.")
-            }
-            // Listen for cancellation and use that to cancel the Animator
-            val signal: CancellationSignal = animationInfo.signal
-            signal.setOnCancelListener {
-                if (operation.isSeeking) {
-                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-                        Api26Impl.reverse(animator)
-                    }
-                } else {
-                    animator.end()
-                }
-                if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
-                    Log.v(
-                        FragmentManager.TAG,
-                        "Animator from operation $operation has been canceled" +
-                            "${if (operation.isSeeking) " with seeking." else "."} "
-                    )
-                }
-            }
+            operation.effects.add(AnimatorEffect(animatorInfo))
         }
 
-        // Now run Animations
+        // Find all Animations and add the effect to the operation
         for (animationInfo: AnimationInfo in animationsToRun) {
-            // First make sure we haven't already started any Transition
             val operation: Operation = animationInfo.operation
             val fragment = operation.fragment
             if (startedAnyTransition) {
@@ -280,7 +209,7 @@
                         "Ignoring Animation set on $fragment as Animations cannot " +
                             "run alongside Transitions.")
                 }
-                animationInfo.completeSpecialEffect()
+                animationInfo.operation.effects.add(NoOpEffect(animationInfo))
                 continue
             }
             // Then make sure we haven't already started any Animator
@@ -290,79 +219,21 @@
                         "Ignoring Animation set on $fragment as Animations cannot " +
                             "run alongside Animators.")
                 }
-                animationInfo.completeSpecialEffect()
+                animationInfo.operation.effects.add(NoOpEffect(animationInfo))
                 continue
             }
-
-            // Okay, let's run the Animation!
-            val viewToAnimate = fragment.mView
-            val anim = checkNotNull(checkNotNull(animationInfo.getAnimation(context)).animation)
-            val finalState = operation.finalState
-            if (finalState !== Operation.State.REMOVED) {
-                // If the operation does not remove the view, we can't use a
-                // AnimationSet due that causing the introduction of visual artifacts (b/163084315).
-                viewToAnimate.startAnimation(anim)
-                // This means we can't use setAnimationListener() without overriding
-                // any listener that the Fragment has set themselves, so we
-                // just mark the special effect as complete immediately.
-                animationInfo.completeSpecialEffect()
-            } else {
-                container.startViewTransition(viewToAnimate)
-                val animation: Animation = FragmentAnim.EndViewTransitionAnimation(anim,
-                    container, viewToAnimate)
-                animation.setAnimationListener(object : Animation.AnimationListener {
-                    override fun onAnimationStart(animation: Animation) {
-                        if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
-                            Log.v(FragmentManager.TAG,
-                                "Animation from operation $operation has reached " +
-                                    "onAnimationStart.")
-                        }
-                    }
-
-                    override fun onAnimationEnd(animation: Animation) {
-                        // onAnimationEnd() comes during draw(), so there can still be some
-                        // draw events happening after this call. We don't want to complete the
-                        // animation until after the onAnimationEnd()
-                        container.post {
-                            container.endViewTransition(viewToAnimate)
-                            animationInfo.completeSpecialEffect()
-                        }
-                        if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
-                            Log.v(FragmentManager.TAG,
-                                "Animation from operation $operation has ended.")
-                        }
-                    }
-
-                    override fun onAnimationRepeat(animation: Animation) {}
-                })
-                viewToAnimate.startAnimation(animation)
-                if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
-                    Log.v(FragmentManager.TAG,
-                        "Animation from operation $operation has started.")
-                }
-            }
-            // Listen for cancellation and use that to cancel the Animation
-            val signal: CancellationSignal = animationInfo.signal
-            signal.setOnCancelListener {
-                viewToAnimate.clearAnimation()
-                container.endViewTransition(viewToAnimate)
-                animationInfo.completeSpecialEffect()
-                if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
-                    Log.v(FragmentManager.TAG,
-                        "Animation from operation $operation has been cancelled.")
-                }
-            }
+            operation.effects.add(AnimationEffect(animationInfo))
         }
     }
 
     private fun startTransitions(
         transitionInfos: List<TransitionInfo>,
-        awaitingContainerChanges: MutableList<Operation>,
         isPop: Boolean,
         firstOut: Operation?,
         lastIn: Operation?
     ): Map<Operation, Boolean> {
         val startedTransitions = mutableMapOf<Operation, Boolean>()
+
         // First verify that we can run all transitions together
         val transitionImpl = transitionInfos.filterNot { transitionInfo ->
             // If there is no change in visibility, we can skip the TransitionInfo
@@ -383,430 +254,21 @@
             // There were no transitions at all so we can just complete all of them
             for (transitionInfo: TransitionInfo in transitionInfos) {
                 startedTransitions[transitionInfo.operation] = false
-                transitionInfo.completeSpecialEffect()
+                transitionInfo.operation.effects.add(NoOpEffect(transitionInfo))
             }
             return startedTransitions
         }
 
-        // Every transition needs to target at least one View so that they
-        // don't interfere with one another. This is the view we use
-        // in cases where there are no real views to target
-        val nonExistentView = View(container.context)
+        val transitionEffect =
+            TransitionEffect(transitionInfos, isPop, firstOut, lastIn, transitionImpl,
+                startedTransitions)
 
-        // Now find the shared element transition if it exists
-        var sharedElementTransition: Any? = null
-        var firstOutEpicenterView: View? = null
-        var hasLastInEpicenter = false
-        val lastInEpicenterRect = Rect()
-        val sharedElementFirstOutViews = ArrayList<View>()
-        val sharedElementLastInViews = ArrayList<View>()
-        val sharedElementNameMapping = ArrayMap<String, String>()
-        for (transitionInfo: TransitionInfo in transitionInfos) {
-            val hasSharedElementTransition = transitionInfo.hasSharedElementTransition()
-            // Compute the shared element transition between the firstOut and lastIn Fragments
-            if (hasSharedElementTransition && (firstOut != null) && (lastIn != null)) {
-                // swapSharedElementTargets requires wrapping this in a TransitionSet
-                sharedElementTransition = transitionImpl.wrapTransitionInSet(
-                    transitionImpl.cloneTransition(transitionInfo.sharedElementTransition))
-                // The exiting shared elements default to the source names from the
-                // last in fragment
-                val exitingNames = lastIn.fragment.sharedElementSourceNames
-                // But if we're doing multiple transactions, we may need to re-map
-                // the names from the first out fragment
-                val firstOutSourceNames = firstOut.fragment.sharedElementSourceNames
-                val firstOutTargetNames = firstOut.fragment.sharedElementTargetNames
-                // We do this by iterating through each first out target,
-                // seeing if there is a match from the last in sources
-                for (index in firstOutTargetNames.indices) {
-                    val nameIndex = exitingNames.indexOf(firstOutTargetNames[index])
-                    if (nameIndex != -1) {
-                        // If we found a match, replace the last in source name
-                        // with the first out source name
-                        exitingNames[nameIndex] = firstOutSourceNames[index]
-                    }
-                }
-                val enteringNames = lastIn.fragment.sharedElementTargetNames
-                val (exitingCallback, enteringCallback) = if (!isPop) {
-                    // Forward transitions have firstOut fragment exiting and the
-                    // lastIn fragment entering
-                    firstOut.fragment.exitTransitionCallback to
-                        lastIn.fragment.enterTransitionCallback
-                } else {
-                    // A pop is the reverse: the firstOut fragment is entering and the
-                    // lastIn fragment is exiting
-                    firstOut.fragment.enterTransitionCallback to
-                        lastIn.fragment.exitTransitionCallback
-                }
-                val numSharedElements = exitingNames.size
-                for (i in 0 until numSharedElements) {
-                    val exitingName = exitingNames[i]
-                    val enteringName = enteringNames[i]
-                    sharedElementNameMapping[exitingName] = enteringName
-                }
-                if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
-                    Log.v(FragmentManager.TAG, ">>> entering view names <<<")
-                    for (name: String? in enteringNames) {
-                        Log.v(FragmentManager.TAG, "Name: $name")
-                    }
-                    Log.v(FragmentManager.TAG, ">>> exiting view names <<<")
-                    for (name: String? in exitingNames) {
-                        Log.v(FragmentManager.TAG, "Name: $name")
-                    }
-                }
+        transitionEffect.onStart(container)
+        transitionEffect.onCommit(container)
 
-                // Find all of the Views from the firstOut fragment that are
-                // part of the shared element transition
-                val firstOutViews = ArrayMap<String, View>()
-                findNamedViews(firstOutViews, firstOut.fragment.mView)
-                firstOutViews.retainAll(exitingNames)
-                if (exitingCallback != null) {
-                    if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
-                        Log.v(FragmentManager.TAG,
-                            "Executing exit callback for operation $firstOut")
-                    }
-                    // Give the SharedElementCallback a chance to override the default mapping
-                    exitingCallback.onMapSharedElements(exitingNames, firstOutViews)
-                    for (i in exitingNames.indices.reversed()) {
-                        val name = exitingNames[i]
-                        val view = firstOutViews[name]
-                        if (view == null) {
-                            sharedElementNameMapping.remove(name)
-                        } else if (name != ViewCompat.getTransitionName(view)) {
-                            val targetValue = sharedElementNameMapping.remove(name)
-                            sharedElementNameMapping[ViewCompat.getTransitionName(view)] =
-                                targetValue
-                        }
-                    }
-                } else {
-                    // Only keep the mapping of elements that were found in the firstOut Fragment
-                    sharedElementNameMapping.retainAll(firstOutViews.keys)
-                }
-
-                // Find all of the Views from the lastIn fragment that are
-                // part of the shared element transition
-                val lastInViews = ArrayMap<String, View>()
-                findNamedViews(lastInViews, lastIn.fragment.mView)
-                lastInViews.retainAll(enteringNames)
-                lastInViews.retainAll(sharedElementNameMapping.values)
-                if (enteringCallback != null) {
-                    if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
-                        Log.v(FragmentManager.TAG,
-                            "Executing enter callback for operation $lastIn")
-                    }
-                    // Give the SharedElementCallback a chance to override the default mapping
-                    enteringCallback.onMapSharedElements(enteringNames, lastInViews)
-                    for (i in enteringNames.indices.reversed()) {
-                        val name = enteringNames[i]
-                        val view = lastInViews[name]
-                        if (view == null) {
-                            val key = sharedElementNameMapping.findKeyForValue(name)
-                            if (key != null) {
-                                sharedElementNameMapping.remove(key)
-                            }
-                        } else if (name != ViewCompat.getTransitionName(view)) {
-                            val key = sharedElementNameMapping.findKeyForValue(name)
-                            if (key != null) {
-                                sharedElementNameMapping[key] = ViewCompat.getTransitionName(view)
-                            }
-                        }
-                    }
-                } else {
-                    // Only keep the mapping of elements that were found in the lastIn Fragment
-                    sharedElementNameMapping.retainValues(lastInViews)
-                }
-
-                // Now make a final pass through the Views list to ensure they
-                // don't still have elements that were removed from the mapping
-                firstOutViews.retainMatchingViews(sharedElementNameMapping.keys)
-                lastInViews.retainMatchingViews(sharedElementNameMapping.values)
-                @Suppress("UsePropertyAccessSyntax") /* Collection 1.3.X requires isEmpty() */
-                if (sharedElementNameMapping.isEmpty()) {
-                    // We couldn't find any valid shared element mappings, so clear out
-                    // the shared element transition information entirely
-                    sharedElementTransition = null
-                    sharedElementFirstOutViews.clear()
-                    sharedElementLastInViews.clear()
-                } else {
-                    // Call through to onSharedElementStart() before capturing the
-                    // starting values for the shared element transition
-                    callSharedElementStartEnd(lastIn.fragment, firstOut.fragment, isPop,
-                        firstOutViews, true)
-                    // Trigger the onSharedElementEnd callback in the next frame after
-                    // the starting values are captured and before capturing the end states
-                    OneShotPreDrawListener.add(container) {
-                        callSharedElementStartEnd(lastIn.fragment, firstOut.fragment, isPop,
-                            lastInViews, false)
-                    }
-                    sharedElementFirstOutViews.addAll(firstOutViews.values)
-
-                    // Compute the epicenter of the firstOut transition
-                    if (exitingNames.isNotEmpty()) {
-                        val epicenterViewName = exitingNames[0]
-                        firstOutEpicenterView = firstOutViews[epicenterViewName]
-                        transitionImpl.setEpicenter(sharedElementTransition, firstOutEpicenterView)
-                    }
-                    sharedElementLastInViews.addAll(lastInViews.values)
-
-                    // Compute the epicenter of the lastIn transition
-                    if (enteringNames.isNotEmpty()) {
-                        val epicenterViewName = enteringNames[0]
-                        val lastInEpicenterView = lastInViews[epicenterViewName]
-                        if (lastInEpicenterView != null) {
-                            hasLastInEpicenter = true
-                            // We can't set the epicenter here directly since the View might
-                            // not have been laid out as of yet, so instead we set a Rect as
-                            // the epicenter and compute the bounds one frame later
-                            val impl: FragmentTransitionImpl = transitionImpl
-                            OneShotPreDrawListener.add(container) {
-                                impl.getBoundsOnScreen(lastInEpicenterView, lastInEpicenterRect)
-                            }
-                        }
-                    }
-
-                    // Now set the transition's targets to only the firstOut Fragment's views
-                    // It'll be swapped to the lastIn Fragment's views after the
-                    // transition is started
-                    transitionImpl.setSharedElementTargets(sharedElementTransition,
-                        nonExistentView, sharedElementFirstOutViews)
-                    // After the swap to the lastIn Fragment's view (done below), we
-                    // need to clean up those targets. We schedule this here so that it
-                    // runs directly after the swap
-                    transitionImpl.scheduleRemoveTargets(sharedElementTransition, null, null,
-                        null, null, sharedElementTransition, sharedElementLastInViews)
-                    // Both the firstOut and lastIn Operations are now associated
-                    // with a Transition
-                    startedTransitions[firstOut] = true
-                    startedTransitions[lastIn] = true
-                }
-            }
-        }
-        val enteringViews = ArrayList<View>()
-        // These transitions run together, overlapping one another
-        var mergedTransition: Any? = null
-        // These transitions run only after all of the other transitions complete
-        var mergedNonOverlappingTransition: Any? = null
-        // Now iterate through the set of transitions and merge them together
-        for (transitionInfo: TransitionInfo in transitionInfos) {
-            if (transitionInfo.isVisibilityUnchanged) {
-                // No change in visibility, so we can immediately complete the transition
-                startedTransitions[transitionInfo.operation] = false
-                transitionInfo.completeSpecialEffect()
-                continue
-            }
-            val transition = transitionImpl.cloneTransition(transitionInfo.transition)
-            val operation: Operation = transitionInfo.operation
-            val involvedInSharedElementTransition = (sharedElementTransition != null &&
-                (operation === firstOut || operation === lastIn))
-            if (transition == null) {
-                // Nothing more to do if the transition is null
-                if (!involvedInSharedElementTransition) {
-                    // Only complete the transition if this fragment isn't involved
-                    // in the shared element transition (as otherwise we need to wait
-                    // for that to finish)
-                    startedTransitions[operation] = false
-                    transitionInfo.completeSpecialEffect()
-                }
-            } else {
-                // Target the Transition to *only* the set of transitioning views
-                val transitioningViews = ArrayList<View>()
-                captureTransitioningViews(transitioningViews, operation.fragment.mView)
-                if (involvedInSharedElementTransition) {
-                    // Remove all of the shared element views from the transition
-                    if (operation === firstOut) {
-                        transitioningViews.removeAll(sharedElementFirstOutViews.toSet())
-                    } else {
-                        transitioningViews.removeAll(sharedElementLastInViews.toSet())
-                    }
-                }
-                if (transitioningViews.isEmpty()) {
-                    transitionImpl.addTarget(transition, nonExistentView)
-                } else {
-                    transitionImpl.addTargets(transition, transitioningViews)
-                    transitionImpl.scheduleRemoveTargets(transition, transition,
-                        transitioningViews, null, null, null, null)
-                    if (operation.finalState === Operation.State.GONE) {
-                        // We're hiding the Fragment. This requires a bit of extra work
-                        // First, we need to avoid immediately applying the container change as
-                        // that will stop the Transition from occurring.
-                        awaitingContainerChanges.remove(operation)
-                        // Then schedule the actual hide of the fragment's view,
-                        // essentially doing what applyState() would do for us
-                        val transitioningViewsToHide = ArrayList(transitioningViews)
-                        transitioningViewsToHide.remove(operation.fragment.mView)
-                        transitionImpl.scheduleHideFragmentView(transition,
-                            operation.fragment.mView, transitioningViewsToHide)
-                        // This OneShotPreDrawListener gets fired before the delayed start of
-                        // the Transition and changes the visibility of any exiting child views
-                        // that *ARE NOT* shared element transitions. The TransitionManager then
-                        // properly considers exiting views and marks them as disappearing,
-                        // applying a transition and a listener to take proper actions once the
-                        // transition is complete.
-                        OneShotPreDrawListener.add(container) {
-                            setViewVisibility(transitioningViews, View.INVISIBLE)
-                        }
-                    }
-                }
-                if (operation.finalState === Operation.State.VISIBLE) {
-                    enteringViews.addAll(transitioningViews)
-                    if (hasLastInEpicenter) {
-                        transitionImpl.setEpicenter(transition, lastInEpicenterRect)
-                    }
-                } else {
-                    transitionImpl.setEpicenter(transition, firstOutEpicenterView)
-                }
-                startedTransitions[operation] = true
-                // Now determine how this transition should be merged together
-                if (transitionInfo.isOverlapAllowed) {
-                    // Overlap is allowed, so add them to the mergeTransition set
-                    mergedTransition = transitionImpl.mergeTransitionsTogether(
-                        mergedTransition, transition, null)
-                } else {
-                    // Overlap is not allowed, add them to the mergedNonOverlappingTransition
-                    mergedNonOverlappingTransition = transitionImpl.mergeTransitionsTogether(
-                        mergedNonOverlappingTransition, transition, null)
-                }
-            }
-        }
-
-        // Make sure that the mergedNonOverlappingTransition set
-        // runs after the mergedTransition set is complete
-        mergedTransition = transitionImpl.mergeTransitionsInSequence(mergedTransition,
-            mergedNonOverlappingTransition, sharedElementTransition)
-
-        // If there's no transitions playing together, no non-overlapping transitions,
-        // and no shared element transitions, mergedTransition will be null and
-        // there's nothing else we need to do
-        if (mergedTransition == null) {
-            return startedTransitions
-        }
-
-        // Now set up our completion signal on the completely merged transition set
-        transitionInfos.filterNot { transitionInfo ->
-            // If there's change in visibility, we've already completed the transition
-            transitionInfo.isVisibilityUnchanged
-        }.forEach { transitionInfo: TransitionInfo ->
-            val transition: Any? = transitionInfo.transition
-            val operation: Operation = transitionInfo.operation
-            val involvedInSharedElementTransition = sharedElementTransition != null &&
-                (operation === firstOut || operation === lastIn)
-            if (transition != null || involvedInSharedElementTransition) {
-                // If the container has never been laid out, transitions will not start so
-                // so lets instantly complete them.
-                if (!ViewCompat.isLaidOut(container)) {
-                    if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
-                        Log.v(FragmentManager.TAG,
-                            "SpecialEffectsController: Container $container has not been " +
-                                "laid out. Completing operation $operation")
-                    }
-                    transitionInfo.completeSpecialEffect()
-                } else {
-                    transitionImpl.setListenerForTransitionEnd(
-                        transitionInfo.operation.fragment,
-                        mergedTransition,
-                        transitionInfo.signal,
-                        Runnable {
-                            transitionInfo.completeSpecialEffect()
-                            if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
-                                Log.v(FragmentManager.TAG,
-                                    "Transition for operation $operation has completed")
-                            }
-                        })
-                }
-            }
-        }
-        // Transitions won't run if the container isn't laid out so
-        // we can return early here to avoid doing unnecessary work.
-        if (!ViewCompat.isLaidOut(container)) {
-            return startedTransitions
-        }
-        // First, hide all of the entering views so they're in
-        // the correct initial state
-        setViewVisibility(enteringViews, View.INVISIBLE)
-        val inNames = transitionImpl.prepareSetNameOverridesReordered(sharedElementLastInViews)
-        if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
-            Log.v(FragmentManager.TAG, ">>>>> Beginning transition <<<<<")
-            Log.v(FragmentManager.TAG, ">>>>> SharedElementFirstOutViews <<<<<")
-            for (view: View in sharedElementFirstOutViews) {
-                Log.v(FragmentManager.TAG,
-                    "View: $view Name: ${ViewCompat.getTransitionName(view)}")
-            }
-            Log.v(FragmentManager.TAG, ">>>>> SharedElementLastInViews <<<<<")
-            for (view: View in sharedElementLastInViews) {
-                Log.v(FragmentManager.TAG,
-                    "View: $view Name: ${ViewCompat.getTransitionName(view)}")
-            }
-        }
-        // Now actually start the transition
-        transitionImpl.beginDelayedTransition(container, mergedTransition)
-        transitionImpl.setNameOverridesReordered(container, sharedElementFirstOutViews,
-            sharedElementLastInViews, inNames, sharedElementNameMapping)
-        // Then, show all of the entering views, putting them into
-        // the correct final state
-        setViewVisibility(enteringViews, View.VISIBLE)
-        transitionImpl.swapSharedElementTargets(sharedElementTransition,
-            sharedElementFirstOutViews, sharedElementLastInViews)
         return startedTransitions
     }
 
-    /**
-     * Retain only the views that have a transition name that is in the set of [names].
-     */
-    private fun ArrayMap<String, View>.retainMatchingViews(names: Collection<String>) {
-        entries.retainAll { entry ->
-            names.contains(ViewCompat.getTransitionName(entry.value))
-        }
-    }
-
-    /**
-     * Gets the Views in the hierarchy affected by entering and exiting transitions.
-     *
-     * @param transitioningViews This View will be added to transitioningViews if it has a
-     * transition name, is VISIBLE and a normal View, or a ViewGroup with
-     * [android.view.ViewGroup.isTransitionGroup] true.
-     * @param view The base of the view hierarchy to look in.
-     */
-    private fun captureTransitioningViews(transitioningViews: ArrayList<View>, view: View) {
-        if (view is ViewGroup) {
-            if (ViewGroupCompat.isTransitionGroup(view)) {
-                if (!transitioningViews.contains(view)) {
-                    transitioningViews.add(view)
-                }
-            } else {
-                val count = view.childCount
-                for (i in 0 until count) {
-                    val child = view.getChildAt(i)
-                    if (child.visibility == View.VISIBLE) {
-                        captureTransitioningViews(transitioningViews, child)
-                    }
-                }
-            }
-        } else {
-            if (!transitioningViews.contains(view)) {
-                transitioningViews.add(view)
-            }
-        }
-    }
-
-    /**
-     * Finds all views that have transition names in the hierarchy under the given view and
-     * stores them in [namedViews] map with the name as the key.
-     */
-    private fun findNamedViews(namedViews: MutableMap<String, View>, view: View) {
-        val transitionName = ViewCompat.getTransitionName(view)
-        if (transitionName != null) {
-            namedViews[transitionName] = view
-        }
-        if (view is ViewGroup) {
-            val count = view.childCount
-            for (i in 0 until count) {
-                val child = view.getChildAt(i)
-                if (child.visibility == View.VISIBLE) {
-                    findNamedViews(namedViews, child)
-                }
-            }
-        }
-    }
-
     private fun applyContainerChanges(operation: Operation) {
         val view = operation.fragment.mView
         operation.finalState.applyState(view)
@@ -930,6 +392,614 @@
         }
     }
 
+    private class AnimationEffect(val animationInfo: AnimationInfo) : Effect() {
+        override fun onCommit(container: ViewGroup) {
+            val context = container.context
+            val operation: Operation = animationInfo.operation
+            val fragment = operation.fragment
+
+            // Okay, let's run the Animation!
+            val viewToAnimate = fragment.mView
+            val anim = checkNotNull(checkNotNull(animationInfo.getAnimation(context)).animation)
+            val finalState = operation.finalState
+            if (finalState !== Operation.State.REMOVED) {
+                // If the operation does not remove the view, we can't use a
+                // AnimationSet due that causing the introduction of visual artifacts (b/163084315).
+                viewToAnimate.startAnimation(anim)
+                // This means we can't use setAnimationListener() without overriding
+                // any listener that the Fragment has set themselves, so we
+                // just mark the special effect as complete immediately.
+                operation.effects.add(NoOpEffect(animationInfo))
+            } else {
+                container.startViewTransition(viewToAnimate)
+                val animation: Animation = FragmentAnim.EndViewTransitionAnimation(anim,
+                    container, viewToAnimate)
+                animation.setAnimationListener(object : Animation.AnimationListener {
+                    override fun onAnimationStart(animation: Animation) {
+                        if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
+                            Log.v(FragmentManager.TAG,
+                                "Animation from operation $operation has reached " +
+                                    "onAnimationStart.")
+                        }
+                    }
+
+                    override fun onAnimationEnd(animation: Animation) {
+                        // onAnimationEnd() comes during draw(), so there can still be some
+                        // draw events happening after this call. We don't want to complete the
+                        // animation until after the onAnimationEnd()
+                        container.post {
+                            container.endViewTransition(viewToAnimate)
+                            animationInfo.completeSpecialEffect()
+                        }
+                        if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
+                            Log.v(FragmentManager.TAG,
+                                "Animation from operation $operation has ended.")
+                        }
+                    }
+
+                    override fun onAnimationRepeat(animation: Animation) {}
+                })
+                viewToAnimate.startAnimation(animation)
+                if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
+                    Log.v(FragmentManager.TAG,
+                        "Animation from operation $operation has started.")
+                }
+            }
+            // Listen for cancellation and use that to cancel the Animation
+            val signal: CancellationSignal = animationInfo.signal
+            signal.setOnCancelListener {
+                viewToAnimate.clearAnimation()
+                container.endViewTransition(viewToAnimate)
+                animationInfo.completeSpecialEffect()
+                if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
+                    Log.v(FragmentManager.TAG,
+                        "Animation from operation $operation has been cancelled.")
+                }
+            }
+        }
+    }
+
+    private class AnimatorEffect(val animatorInfo: AnimationInfo) : Effect() {
+        override fun onStart(container: ViewGroup) {
+            val context = container.context
+            val animator = animatorInfo.getAnimation(context)?.animator
+            val operation: Operation = animatorInfo.operation
+            val fragment = operation.fragment
+
+            // Okay, let's run the Animator!
+            val isHideOperation = operation.finalState === Operation.State.GONE
+            val viewToAnimate = fragment.mView
+            container.startViewTransition(viewToAnimate)
+            animator?.addListener(object : AnimatorListenerAdapter() {
+                override fun onAnimationEnd(anim: Animator) {
+                    container.endViewTransition(viewToAnimate)
+                    if (isHideOperation) {
+                        // Specifically for hide operations with Animator, we can't
+                        // applyState until the Animator finishes
+                        operation.finalState.applyState(viewToAnimate)
+                    }
+                    animatorInfo.completeSpecialEffect()
+                    if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
+                        Log.v(FragmentManager.TAG,
+                            "Animator from operation $operation has ended.")
+                    }
+                }
+            })
+            animator?.setTarget(viewToAnimate)
+            // Listen for cancellation and use that to cancel the Animation
+            animatorInfo.signal.setOnCancelListener {
+                onCancel(container)
+            }
+        }
+
+        override fun onCommit(container: ViewGroup) {
+            val operation = animatorInfo.operation
+            val animatorSet = animatorInfo.getAnimation(container.context)?.animator
+            if (animatorSet != null &&
+                Build.VERSION.SDK_INT >= 34 && operation.fragment.mTransitioning
+                ) {
+                if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
+                    Log.v(
+                        FragmentManager.TAG,
+                        "Adding BackProgressCallbacks for Animators to operation $operation"
+                    )
+                }
+                operation.addBackProgressCallbacks({ backEvent ->
+                    val totalDuration = Api24Impl.totalDuration(animatorSet)
+                    var time = (backEvent.progress * totalDuration).toLong()
+                    // We cannot let the time get to 0 or the totalDuration to avoid
+                    // completing the operation accidentally.
+                    if (time == 0L) {
+                        time = 1L
+                    }
+                    if (time == totalDuration) {
+                        time = totalDuration - 1
+                    }
+                    if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
+                        Log.v(
+                            FragmentManager.TAG,
+                            "Setting currentPlayTime to $time for Animator $animatorSet on " +
+                                "operation $operation"
+                        )
+                    }
+                    Api26Impl.setCurrentPlayTime(animatorSet, time)
+                }) {
+                    if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
+                        Log.v(
+                            FragmentManager.TAG,
+                            "Back Progress Callback Animator has been started."
+                        )
+                    }
+                    animatorSet.start()
+                }
+            } else {
+                animatorSet?.start()
+            }
+            if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
+                Log.v(FragmentManager.TAG,
+                    "Animator from operation $operation has started.")
+            }
+        }
+
+        override fun onCancel(container: ViewGroup) {
+            val animator = animatorInfo.getAnimation(container.context)?.animator
+            if (animator != null) {
+                val operation = animatorInfo.operation
+                if (operation.isSeeking) {
+                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+                        Api26Impl.reverse(animator)
+                    }
+                } else {
+                    animator.end()
+                }
+                if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
+                    Log.v(
+                        FragmentManager.TAG,
+                        "Animator from operation $operation has been canceled" +
+                            "${if (operation.isSeeking) " with seeking." else "."} "
+                    )
+                }
+            }
+        }
+    }
+
+    private class TransitionEffect(
+        val transitionInfos: List<TransitionInfo>,
+        val isPop: Boolean,
+        val firstOut: Operation?,
+        val lastIn: Operation?,
+        val transitionImpl: FragmentTransitionImpl,
+        val startedTransitions: MutableMap<Operation, Boolean>
+    ) : Effect() {
+        override fun onCommit(container: ViewGroup) {
+            // Every transition needs to target at least one View so that they
+            // don't interfere with one another. This is the view we use
+            // in cases where there are no real views to target
+            val nonExistentView = View(container.context)
+
+            // Now find the shared element transition if it exists
+            var sharedElementTransition: Any? = null
+            var firstOutEpicenterView: View? = null
+            var hasLastInEpicenter = false
+            val lastInEpicenterRect = Rect()
+            val sharedElementFirstOutViews = ArrayList<View>()
+            val sharedElementLastInViews = ArrayList<View>()
+            val sharedElementNameMapping = ArrayMap<String, String>()
+            for (transitionInfo: TransitionInfo in transitionInfos) {
+                val hasSharedElementTransition = transitionInfo.hasSharedElementTransition()
+                // Compute the shared element transition between the firstOut and lastIn Fragments
+                if (hasSharedElementTransition && (firstOut != null) && (lastIn != null)) {
+                    // swapSharedElementTargets requires wrapping this in a TransitionSet
+                    sharedElementTransition = transitionImpl.wrapTransitionInSet(
+                        transitionImpl.cloneTransition(transitionInfo.sharedElementTransition))
+                    // The exiting shared elements default to the source names from the
+                    // last in fragment
+                    val exitingNames = lastIn.fragment.sharedElementSourceNames
+                    // But if we're doing multiple transactions, we may need to re-map
+                    // the names from the first out fragment
+                    val firstOutSourceNames = firstOut.fragment.sharedElementSourceNames
+                    val firstOutTargetNames = firstOut.fragment.sharedElementTargetNames
+                    // We do this by iterating through each first out target,
+                    // seeing if there is a match from the last in sources
+                    for (index in firstOutTargetNames.indices) {
+                        val nameIndex = exitingNames.indexOf(firstOutTargetNames[index])
+                        if (nameIndex != -1) {
+                            // If we found a match, replace the last in source name
+                            // with the first out source name
+                            exitingNames[nameIndex] = firstOutSourceNames[index]
+                        }
+                    }
+                    val enteringNames = lastIn.fragment.sharedElementTargetNames
+                    val (exitingCallback, enteringCallback) = if (!isPop) {
+                        // Forward transitions have firstOut fragment exiting and the
+                        // lastIn fragment entering
+                        firstOut.fragment.exitTransitionCallback to
+                            lastIn.fragment.enterTransitionCallback
+                    } else {
+                        // A pop is the reverse: the firstOut fragment is entering and the
+                        // lastIn fragment is exiting
+                        firstOut.fragment.enterTransitionCallback to
+                            lastIn.fragment.exitTransitionCallback
+                    }
+                    val numSharedElements = exitingNames.size
+                    for (i in 0 until numSharedElements) {
+                        val exitingName = exitingNames[i]
+                        val enteringName = enteringNames[i]
+                        sharedElementNameMapping[exitingName] = enteringName
+                    }
+                    if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
+                        Log.v(FragmentManager.TAG, ">>> entering view names <<<")
+                        for (name: String? in enteringNames) {
+                            Log.v(FragmentManager.TAG, "Name: $name")
+                        }
+                        Log.v(FragmentManager.TAG, ">>> exiting view names <<<")
+                        for (name: String? in exitingNames) {
+                            Log.v(FragmentManager.TAG, "Name: $name")
+                        }
+                    }
+
+                    // Find all of the Views from the firstOut fragment that are
+                    // part of the shared element transition
+                    val firstOutViews = ArrayMap<String, View>()
+                    findNamedViews(firstOutViews, firstOut.fragment.mView)
+                    firstOutViews.retainAll(exitingNames)
+                    if (exitingCallback != null) {
+                        if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
+                            Log.v(FragmentManager.TAG,
+                                "Executing exit callback for operation $firstOut")
+                        }
+                        // Give the SharedElementCallback a chance to override the default mapping
+                        exitingCallback.onMapSharedElements(exitingNames, firstOutViews)
+                        for (i in exitingNames.indices.reversed()) {
+                            val name = exitingNames[i]
+                            val view = firstOutViews[name]
+                            if (view == null) {
+                                sharedElementNameMapping.remove(name)
+                            } else if (name != ViewCompat.getTransitionName(view)) {
+                                val targetValue = sharedElementNameMapping.remove(name)
+                                sharedElementNameMapping[ViewCompat.getTransitionName(view)] =
+                                    targetValue
+                            }
+                        }
+                    } else {
+                        // Only keep the mapping of elements that were found in the firstOut Fragment
+                        sharedElementNameMapping.retainAll(firstOutViews.keys)
+                    }
+
+                    // Find all of the Views from the lastIn fragment that are
+                    // part of the shared element transition
+                    val lastInViews = ArrayMap<String, View>()
+                    findNamedViews(lastInViews, lastIn.fragment.mView)
+                    lastInViews.retainAll(enteringNames)
+                    lastInViews.retainAll(sharedElementNameMapping.values)
+                    if (enteringCallback != null) {
+                        if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
+                            Log.v(FragmentManager.TAG,
+                                "Executing enter callback for operation $lastIn")
+                        }
+                        // Give the SharedElementCallback a chance to override the default mapping
+                        enteringCallback.onMapSharedElements(enteringNames, lastInViews)
+                        for (i in enteringNames.indices.reversed()) {
+                            val name = enteringNames[i]
+                            val view = lastInViews[name]
+                            if (view == null) {
+                                val key = sharedElementNameMapping.findKeyForValue(name)
+                                if (key != null) {
+                                    sharedElementNameMapping.remove(key)
+                                }
+                            } else if (name != ViewCompat.getTransitionName(view)) {
+                                val key = sharedElementNameMapping.findKeyForValue(name)
+                                if (key != null) {
+                                    sharedElementNameMapping[key] =
+                                        ViewCompat.getTransitionName(view)
+                                }
+                            }
+                        }
+                    } else {
+                        // Only keep the mapping of elements that were found in the lastIn Fragment
+                        sharedElementNameMapping.retainValues(lastInViews)
+                    }
+
+                    // Now make a final pass through the Views list to ensure they
+                    // don't still have elements that were removed from the mapping
+                    firstOutViews.retainMatchingViews(sharedElementNameMapping.keys)
+                    lastInViews.retainMatchingViews(sharedElementNameMapping.values)
+                    @Suppress("UsePropertyAccessSyntax") /* Collection 1.3.X requires isEmpty() */
+                    if (sharedElementNameMapping.isEmpty()) {
+                        // We couldn't find any valid shared element mappings, so clear out
+                        // the shared element transition information entirely
+                        sharedElementTransition = null
+                        sharedElementFirstOutViews.clear()
+                        sharedElementLastInViews.clear()
+                    } else {
+                        // Call through to onSharedElementStart() before capturing the
+                        // starting values for the shared element transition
+                        callSharedElementStartEnd(lastIn.fragment, firstOut.fragment, isPop,
+                            firstOutViews, true)
+                        // Trigger the onSharedElementEnd callback in the next frame after
+                        // the starting values are captured and before capturing the end states
+                        OneShotPreDrawListener.add(container) {
+                            callSharedElementStartEnd(lastIn.fragment, firstOut.fragment, isPop,
+                                lastInViews, false)
+                        }
+                        sharedElementFirstOutViews.addAll(firstOutViews.values)
+
+                        // Compute the epicenter of the firstOut transition
+                        if (exitingNames.isNotEmpty()) {
+                            val epicenterViewName = exitingNames[0]
+                            firstOutEpicenterView = firstOutViews[epicenterViewName]
+                            transitionImpl.setEpicenter(
+                                sharedElementTransition, firstOutEpicenterView
+                            )
+                        }
+                        sharedElementLastInViews.addAll(lastInViews.values)
+
+                        // Compute the epicenter of the lastIn transition
+                        if (enteringNames.isNotEmpty()) {
+                            val epicenterViewName = enteringNames[0]
+                            val lastInEpicenterView = lastInViews[epicenterViewName]
+                            if (lastInEpicenterView != null) {
+                                hasLastInEpicenter = true
+                                // We can't set the epicenter here directly since the View might
+                                // not have been laid out as of yet, so instead we set a Rect as
+                                // the epicenter and compute the bounds one frame later
+                                val impl: FragmentTransitionImpl = transitionImpl
+                                OneShotPreDrawListener.add(container) {
+                                    impl.getBoundsOnScreen(lastInEpicenterView, lastInEpicenterRect)
+                                }
+                            }
+                        }
+
+                        // Now set the transition's targets to only the firstOut Fragment's views
+                        // It'll be swapped to the lastIn Fragment's views after the
+                        // transition is started
+                        transitionImpl.setSharedElementTargets(sharedElementTransition,
+                            nonExistentView, sharedElementFirstOutViews)
+                        // After the swap to the lastIn Fragment's view (done below), we
+                        // need to clean up those targets. We schedule this here so that it
+                        // runs directly after the swap
+                        transitionImpl.scheduleRemoveTargets(sharedElementTransition, null, null,
+                            null, null, sharedElementTransition, sharedElementLastInViews)
+                        // Both the firstOut and lastIn Operations are now associated
+                        // with a Transition
+                        startedTransitions[firstOut] = true
+                        startedTransitions[lastIn] = true
+                    }
+                }
+            }
+            val enteringViews = ArrayList<View>()
+            // These transitions run together, overlapping one another
+            var mergedTransition: Any? = null
+            // These transitions run only after all of the other transitions complete
+            var mergedNonOverlappingTransition: Any? = null
+            // Now iterate through the set of transitions and merge them together
+            for (transitionInfo: TransitionInfo in transitionInfos) {
+                val operation: Operation = transitionInfo.operation
+                if (transitionInfo.isVisibilityUnchanged) {
+                    // No change in visibility, so we can immediately complete the transition
+                    startedTransitions[transitionInfo.operation] = false
+                    operation.effects.add(NoOpEffect(transitionInfo))
+                    continue
+                }
+                val transition = transitionImpl.cloneTransition(transitionInfo.transition)
+                val involvedInSharedElementTransition = (sharedElementTransition != null &&
+                    (operation === firstOut || operation === lastIn))
+                if (transition == null) {
+                    // Nothing more to do if the transition is null
+                    if (!involvedInSharedElementTransition) {
+                        // Only complete the transition if this fragment isn't involved
+                        // in the shared element transition (as otherwise we need to wait
+                        // for that to finish)
+                        startedTransitions[operation] = false
+                        operation.effects.add(NoOpEffect(transitionInfo))
+                    }
+                } else {
+                    // Target the Transition to *only* the set of transitioning views
+                    val transitioningViews = ArrayList<View>()
+                    captureTransitioningViews(transitioningViews, operation.fragment.mView)
+                    if (involvedInSharedElementTransition) {
+                        // Remove all of the shared element views from the transition
+                        if (operation === firstOut) {
+                            transitioningViews.removeAll(sharedElementFirstOutViews.toSet())
+                        } else {
+                            transitioningViews.removeAll(sharedElementLastInViews.toSet())
+                        }
+                    }
+                    if (transitioningViews.isEmpty()) {
+                        transitionImpl.addTarget(transition, nonExistentView)
+                    } else {
+                        transitionImpl.addTargets(transition, transitioningViews)
+                        transitionImpl.scheduleRemoveTargets(transition, transition,
+                            transitioningViews, null, null, null, null)
+                        if (operation.finalState === Operation.State.GONE) {
+                            // We're hiding the Fragment. This requires a bit of extra work
+                            // First, we need to avoid immediately applying the container change as
+                            // that will stop the Transition from occurring.
+                            operation.isAwaitingContainerChanges = false
+                            // Then schedule the actual hide of the fragment's view,
+                            // essentially doing what applyState() would do for us
+                            val transitioningViewsToHide = ArrayList(transitioningViews)
+                            transitioningViewsToHide.remove(operation.fragment.mView)
+                            transitionImpl.scheduleHideFragmentView(transition,
+                                operation.fragment.mView, transitioningViewsToHide)
+                            // This OneShotPreDrawListener gets fired before the delayed start of
+                            // the Transition and changes the visibility of any exiting child views
+                            // that *ARE NOT* shared element transitions. The TransitionManager then
+                            // properly considers exiting views and marks them as disappearing,
+                            // applying a transition and a listener to take proper actions once the
+                            // transition is complete.
+                            OneShotPreDrawListener.add(container) {
+                                setViewVisibility(transitioningViews, View.INVISIBLE)
+                            }
+                        }
+                    }
+                    if (operation.finalState === Operation.State.VISIBLE) {
+                        enteringViews.addAll(transitioningViews)
+                        if (hasLastInEpicenter) {
+                            transitionImpl.setEpicenter(transition, lastInEpicenterRect)
+                        }
+                    } else {
+                        transitionImpl.setEpicenter(transition, firstOutEpicenterView)
+                    }
+                    startedTransitions[operation] = true
+                    // Now determine how this transition should be merged together
+                    if (transitionInfo.isOverlapAllowed) {
+                        // Overlap is allowed, so add them to the mergeTransition set
+                        mergedTransition = transitionImpl.mergeTransitionsTogether(
+                            mergedTransition, transition, null)
+                    } else {
+                        // Overlap is not allowed, add them to the mergedNonOverlappingTransition
+                        mergedNonOverlappingTransition = transitionImpl.mergeTransitionsTogether(
+                            mergedNonOverlappingTransition, transition, null)
+                    }
+                }
+            }
+
+            // Make sure that the mergedNonOverlappingTransition set
+            // runs after the mergedTransition set is complete
+            mergedTransition = transitionImpl.mergeTransitionsInSequence(mergedTransition,
+                mergedNonOverlappingTransition, sharedElementTransition)
+
+            // If there's no transitions playing together, no non-overlapping transitions,
+            // and no shared element transitions, mergedTransition will be null and
+            // there's nothing else we need to do
+            if (mergedTransition == null) {
+                return
+            }
+
+            // Now set up our completion signal on the completely merged transition set
+            transitionInfos.filterNot { transitionInfo ->
+                // If there's change in visibility, we've already completed the transition
+                transitionInfo.isVisibilityUnchanged
+            }.forEach { transitionInfo: TransitionInfo ->
+                val transition: Any? = transitionInfo.transition
+                val operation: Operation = transitionInfo.operation
+                val involvedInSharedElementTransition = sharedElementTransition != null &&
+                    (operation === firstOut || operation === lastIn)
+                if (transition != null || involvedInSharedElementTransition) {
+                    // If the container has never been laid out, transitions will not start so
+                    // so lets instantly complete them.
+                    if (!ViewCompat.isLaidOut(container)) {
+                        if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
+                            Log.v(FragmentManager.TAG,
+                                "SpecialEffectsController: Container $container has not been " +
+                                    "laid out. Completing operation $operation")
+                        }
+                        operation.effects.add(NoOpEffect(transitionInfo))
+                    } else {
+                        transitionImpl.setListenerForTransitionEnd(
+                            transitionInfo.operation.fragment,
+                            mergedTransition,
+                            transitionInfo.signal,
+                            Runnable {
+                                transitionInfo.completeSpecialEffect()
+                                if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
+                                    Log.v(FragmentManager.TAG,
+                                        "Transition for operation $operation has completed")
+                                }
+                            })
+                    }
+                }
+            }
+            // Transitions won't run if the container isn't laid out so
+            // we can return early here to avoid doing unnecessary work.
+            if (!ViewCompat.isLaidOut(container)) {
+                return
+            }
+            // First, hide all of the entering views so they're in
+            // the correct initial state
+            setViewVisibility(enteringViews, View.INVISIBLE)
+            val inNames = transitionImpl.prepareSetNameOverridesReordered(sharedElementLastInViews)
+            if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
+                Log.v(FragmentManager.TAG, ">>>>> Beginning transition <<<<<")
+                Log.v(FragmentManager.TAG, ">>>>> SharedElementFirstOutViews <<<<<")
+                for (view: View in sharedElementFirstOutViews) {
+                    Log.v(FragmentManager.TAG,
+                        "View: $view Name: ${ViewCompat.getTransitionName(view)}")
+                }
+                Log.v(FragmentManager.TAG, ">>>>> SharedElementLastInViews <<<<<")
+                for (view: View in sharedElementLastInViews) {
+                    Log.v(FragmentManager.TAG,
+                        "View: $view Name: ${ViewCompat.getTransitionName(view)}")
+                }
+            }
+            // Now actually start the transition
+            transitionImpl.beginDelayedTransition(container, mergedTransition)
+            transitionImpl.setNameOverridesReordered(container, sharedElementFirstOutViews,
+                sharedElementLastInViews, inNames, sharedElementNameMapping)
+            // Then, show all of the entering views, putting them into
+            // the correct final state
+            setViewVisibility(enteringViews, View.VISIBLE)
+            transitionImpl.swapSharedElementTargets(sharedElementTransition,
+                sharedElementFirstOutViews, sharedElementLastInViews)
+        }
+
+        /**
+         * Retain only the views that have a transition name that is in the set of [names].
+         */
+        private fun ArrayMap<String, View>.retainMatchingViews(names: Collection<String>) {
+            entries.retainAll { entry ->
+                names.contains(ViewCompat.getTransitionName(entry.value))
+            }
+        }
+
+        /**
+         * Gets the Views in the hierarchy affected by entering and exiting transitions.
+         *
+         * @param transitioningViews This View will be added to transitioningViews if it has a
+         * transition name, is VISIBLE and a normal View, or a ViewGroup with
+         * [android.view.ViewGroup.isTransitionGroup] true.
+         * @param view The base of the view hierarchy to look in.
+         */
+        private fun captureTransitioningViews(transitioningViews: ArrayList<View>, view: View) {
+            if (view is ViewGroup) {
+                if (ViewGroupCompat.isTransitionGroup(view)) {
+                    if (!transitioningViews.contains(view)) {
+                        transitioningViews.add(view)
+                    }
+                } else {
+                    val count = view.childCount
+                    for (i in 0 until count) {
+                        val child = view.getChildAt(i)
+                        if (child.visibility == View.VISIBLE) {
+                            captureTransitioningViews(transitioningViews, child)
+                        }
+                    }
+                }
+            } else {
+                if (!transitioningViews.contains(view)) {
+                    transitioningViews.add(view)
+                }
+            }
+        }
+
+        /**
+         * Finds all views that have transition names in the hierarchy under the given view and
+         * stores them in [namedViews] map with the name as the key.
+         */
+        private fun findNamedViews(namedViews: MutableMap<String, View>, view: View) {
+            val transitionName = ViewCompat.getTransitionName(view)
+            if (transitionName != null) {
+                namedViews[transitionName] = view
+            }
+            if (view is ViewGroup) {
+                val count = view.childCount
+                for (i in 0 until count) {
+                    val child = view.getChildAt(i)
+                    if (child.visibility == View.VISIBLE) {
+                        findNamedViews(namedViews, child)
+                    }
+                }
+            }
+        }
+    }
+
+    private class NoOpEffect(val info: SpecialEffectsInfo) : Effect() {
+        override fun onCommit(container: ViewGroup) {
+            info.completeSpecialEffect()
+        }
+    }
+
     @RequiresApi(24)
     internal object Api24Impl {
         @DoNotInline
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/SpecialEffectsController.kt b/fragment/fragment/src/main/java/androidx/fragment/app/SpecialEffectsController.kt
index 04f5674..ad6ee5d 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/SpecialEffectsController.kt
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/SpecialEffectsController.kt
@@ -530,6 +530,10 @@
         var isStarted = false
             private set
 
+        var isAwaitingContainerChanges = true
+
+        val effects = mutableListOf<Effect>()
+
         init {
             // Connect the CancellationSignal to our own
             cancellationSignal.setOnCancelListener { cancel() }
@@ -748,6 +752,14 @@
         }
     }
 
+    internal open class Effect {
+        open fun onStart(container: ViewGroup) { }
+
+        open fun onCommit(container: ViewGroup) { }
+
+        open fun onCancel(container: ViewGroup) { }
+    }
+
     companion object {
         /**
          * Get the [SpecialEffectsController] for a given container if it already exists
diff --git a/glance/glance-appwidget/build.gradle b/glance/glance-appwidget/build.gradle
index 4b59ee3..a8e7d1c 100644
--- a/glance/glance-appwidget/build.gradle
+++ b/glance/glance-appwidget/build.gradle
@@ -117,7 +117,6 @@
 
 LayoutGeneratorTask.registerLayoutGenerator(
         project,
-        android,
         /* containerLayoutDirectory= */ file("src/main/layoutTemplates"),
         /* childLayoutDirectory= */ file("src/main/res/layout")
 )
diff --git a/glance/glance-appwidget/glance-layout-generator/src/main/kotlin/androidx/glance/appwidget/layoutgenerator/gradle/LayoutGeneratorTask.kt b/glance/glance-appwidget/glance-layout-generator/src/main/kotlin/androidx/glance/appwidget/layoutgenerator/gradle/LayoutGeneratorTask.kt
index 75f6129..a54cc8a 100644
--- a/glance/glance-appwidget/glance-layout-generator/src/main/kotlin/androidx/glance/appwidget/layoutgenerator/gradle/LayoutGeneratorTask.kt
+++ b/glance/glance-appwidget/glance-layout-generator/src/main/kotlin/androidx/glance/appwidget/layoutgenerator/gradle/LayoutGeneratorTask.kt
@@ -20,7 +20,7 @@
 import androidx.glance.appwidget.layoutgenerator.LayoutGenerator
 import androidx.glance.appwidget.layoutgenerator.cleanResources
 import androidx.glance.appwidget.layoutgenerator.generateRegistry
-import com.android.build.gradle.LibraryExtension
+import com.android.build.api.variant.AndroidComponentsExtension
 import java.io.File
 import org.gradle.api.DefaultTask
 import org.gradle.api.Project
@@ -57,6 +57,10 @@
 
     @TaskAction
     fun execute() {
+
+        outputSourceDir.asFile.get().mkdirs()
+        outputResourcesDir.asFile.get().mkdirs()
+
         val generatedLayouts = LayoutGenerator().generateAllFiles(
             checkNotNull(containerLayoutDirectory.get().asFile.listFiles()).asList(),
             checkNotNull(childLayoutDirectory.get().asFile.listFiles()).asList(),
@@ -70,54 +74,51 @@
             outputSourceDir = outputSourceDir.get().asFile
         )
         cleanResources(
-            outputResourcesDir.get().asFile,
-            generatedLayouts.extractGeneratedFiles()
+            outputResourcesDir.get().asFile, generatedLayouts.extractGeneratedFiles()
         )
     }
 
     private fun GeneratedFiles.extractGeneratedFiles(): Set<File> =
         generatedContainers.values.flatMap { container ->
             container.map { it.generatedFile }
-        }.toSet() +
-        generatedBoxChildren.values.flatMap { child ->
+        }.toSet() + generatedBoxChildren.values.flatMap { child ->
             child.map { it.generatedFile }
-        }.toSet() +
-        generatedRowColumnChildren.values.flatMap { child ->
+        }.toSet() + generatedRowColumnChildren.values.flatMap { child ->
             child.map { it.generatedFile }
-        }.toSet() +
-        extraFiles
+        }.toSet() + extraFiles
 
     companion object {
         /**
-         * Registers [LayoutGeneratorTask] in [project] for all variants in [libraryExtension].
+         * Registers [LayoutGeneratorTask] in [project] for all variants.
          */
         @JvmStatic
         fun registerLayoutGenerator(
             project: Project,
-            libraryExtension: LibraryExtension,
             containerLayoutDirectory: File,
             childLayoutDirectory: File,
         ) {
-            libraryExtension.libraryVariants.all { variant ->
-                val variantName = variant.name
-                val outputDirectory = project.buildDir.resolve("generatedLayouts/$variantName")
-                val outputResourcesDir = outputDirectory.resolve("res/layouts")
-                val outputSourceDir = outputDirectory.resolve("kotlin")
-                val taskName =
-                    "generateLayouts" + variantName.replaceFirstChar { it.uppercaseChar() }
-                outputResourcesDir.mkdirs()
-                outputSourceDir.mkdirs()
-                val task = project.tasks.register(taskName, LayoutGeneratorTask::class.java) {
-                    it.containerLayoutDirectory.set(containerLayoutDirectory)
-                    it.childLayoutDirectory.set(childLayoutDirectory)
-                    it.outputResourcesDir.set(outputResourcesDir)
-                    it.outputSourceDir.set(outputSourceDir)
-                }
-                variant.registerGeneratedResFolders(
-                    project.files(outputResourcesDir).builtBy(task)
-                )
-                variant.registerJavaGeneratingTask(task, outputSourceDir)
+
+            val outputDirectory = "generatedLayouts"
+            val buildDirectory = project.layout.buildDirectory
+
+            val taskName = "generateLayouts"
+
+            val task = project.tasks.register(taskName, LayoutGeneratorTask::class.java) {
+                it.containerLayoutDirectory.set(containerLayoutDirectory)
+                it.childLayoutDirectory.set(childLayoutDirectory)
+                it.outputSourceDir.set(buildDirectory.dir("$outputDirectory/kotlin"))
+                it.outputResourcesDir.set(buildDirectory.dir("$outputDirectory/res/layouts"))
             }
+
+            project.extensions.getByType(AndroidComponentsExtension::class.java)
+                .onVariants { variant ->
+                    variant.sources.java?.addGeneratedSourceDirectory(
+                        task, LayoutGeneratorTask::outputSourceDir
+                    )
+                    variant.sources.res?.addGeneratedSourceDirectory(
+                        task, LayoutGeneratorTask::outputResourcesDir
+                    )
+                }
         }
     }
 }
diff --git a/glance/glance-appwidget/samples/build.gradle b/glance/glance-appwidget/samples/build.gradle
index e5279ee..559a527 100644
--- a/glance/glance-appwidget/samples/build.gradle
+++ b/glance/glance-appwidget/samples/build.gradle
@@ -28,7 +28,8 @@
 
     implementation(libs.kotlinStdlib)
     implementation("androidx.core:core:1.5.0")
-
+    implementation("androidx.work:work-runtime:2.8.1")
+    implementation("androidx.work:work-runtime-ktx:2.8.1")
     compileOnly(project(":annotation:annotation-sampled"))
 
     implementation(project(":glance:glance"))
diff --git a/glance/glance-appwidget/samples/src/main/java/androidx/glance/appwidget/samples/GlanceAppWidgetSamples.kt b/glance/glance-appwidget/samples/src/main/java/androidx/glance/appwidget/samples/GlanceAppWidgetSamples.kt
index 2b0f690..af41276 100644
--- a/glance/glance-appwidget/samples/src/main/java/androidx/glance/appwidget/samples/GlanceAppWidgetSamples.kt
+++ b/glance/glance-appwidget/samples/src/main/java/androidx/glance/appwidget/samples/GlanceAppWidgetSamples.kt
@@ -23,6 +23,9 @@
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.rememberCoroutineScope
+import androidx.datastore.core.DataStore
+import androidx.datastore.preferences.core.Preferences
+import androidx.datastore.preferences.core.intPreferencesKey
 import androidx.datastore.preferences.core.stringPreferencesKey
 import androidx.datastore.preferences.preferencesDataStore
 import androidx.glance.ExperimentalGlanceApi
@@ -33,9 +36,23 @@
 import androidx.glance.appwidget.provideContent
 import androidx.glance.appwidget.updateAll
 import androidx.glance.text.Text
+import androidx.work.CoroutineWorker
+import androidx.work.ExistingPeriodicWorkPolicy
+import androidx.work.PeriodicWorkRequest
+import androidx.work.WorkManager
+import androidx.work.WorkerParameters
+import kotlin.random.Random
+import kotlin.time.Duration.Companion.minutes
+import kotlin.time.toJavaDuration
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
 
+/**
+ *  This sample demonstrates how to do create a simple [GlanceAppWidget] and update the widget.
+ */
 @Sampled
 fun provideGlanceSample() {
     class MyWidget : GlanceAppWidget() {
@@ -79,3 +96,78 @@
         }
     }
 }
+
+// Without this declaration, the class reference to WeatherWidgetWorker::class.java below does not
+// work because it is defined in that function after it is referenced. This will not show up in the
+// sample.
+class WeatherWidgetWorker(
+    appContext: Context,
+    params: WorkerParameters
+) : CoroutineWorker(appContext, params) {
+    override suspend fun doWork() = Result.success()
+}
+
+/**
+ *  This sample demonstrates how to do periodic updates using a unique periodic [CoroutineWorker].
+ */
+@Sampled
+fun provideGlancePeriodicWorkSample() {
+    class WeatherWidget : GlanceAppWidget() {
+
+        val Context.weatherWidgetStore by preferencesDataStore("WeatherWidget")
+        val CurrentDegrees = intPreferencesKey("currentDegrees")
+
+        suspend fun DataStore<Preferences>.loadWeather() {
+            updateData { prefs ->
+                prefs.toMutablePreferences().apply {
+                    this[CurrentDegrees] = Random.Default.nextInt()
+                }
+            }
+        }
+
+        override suspend fun provideGlance(context: Context, id: GlanceId) {
+            coroutineScope {
+                val store = context.weatherWidgetStore
+                val currentDegrees = store.data
+                    .map { prefs -> prefs[CurrentDegrees] }
+                    .stateIn(this@coroutineScope)
+
+                // Load the current weather if there is not a current value present.
+                if (currentDegrees.value == null) store.loadWeather()
+
+                // Create unique periodic work to keep this widget updated at a regular interval.
+                WorkManager.getInstance(context).enqueueUniquePeriodicWork(
+                    "weatherWidgetWorker",
+                    ExistingPeriodicWorkPolicy.KEEP,
+                    PeriodicWorkRequest.Builder(
+                        WeatherWidgetWorker::class.java,
+                        15.minutes.toJavaDuration()
+                    ).setInitialDelay(15.minutes.toJavaDuration()).build()
+                )
+
+                // Note: you can also set `android:updatePeriodMillis` to control how often the
+                // launcher requests an update, but this does not support periods less than
+                // 30 minutes.
+
+                provideContent {
+                    val degrees by currentDegrees.collectAsState()
+                    Text("Current weather: $degrees °F")
+                }
+            }
+        }
+    }
+
+    class WeatherWidgetWorker(
+        appContext: Context,
+        params: WorkerParameters
+    ) : CoroutineWorker(appContext, params) {
+        override suspend fun doWork(): Result {
+            WeatherWidget().apply {
+                applicationContext.weatherWidgetStore.loadWeather()
+                // Call update/updateAll in case a Worker for the widget is not currently running.
+                updateAll(applicationContext)
+            }
+            return Result.success()
+        }
+    }
+}
diff --git a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/CheckBox.kt b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/CheckBox.kt
index 0ec5659..f9a902b 100644
--- a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/CheckBox.kt
+++ b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/CheckBox.kt
@@ -19,6 +19,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.graphics.Color
 import androidx.glance.Emittable
+import androidx.glance.EmittableCheckable
 import androidx.glance.ExperimentalGlanceApi
 import androidx.glance.GlanceModifier
 import androidx.glance.GlanceNode
@@ -229,12 +230,8 @@
 
 internal class EmittableCheckBox(
     var colors: CheckBoxColors
-) : Emittable {
+) : EmittableCheckable() {
     override var modifier: GlanceModifier = GlanceModifier
-    var checked: Boolean = false
-    var text: String = ""
-    var style: TextStyle? = null
-    var maxLines: Int = Int.MAX_VALUE
 
     override fun copy(): Emittable = EmittableCheckBox(colors = colors).also {
         it.modifier = modifier
diff --git a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/GlanceAppWidget.kt b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/GlanceAppWidget.kt
index 5d69b49..f667413 100644
--- a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/GlanceAppWidget.kt
+++ b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/GlanceAppWidget.kt
@@ -73,6 +73,8 @@
      * Worker for this widget is not currently running.
      *
      * @sample androidx.glance.appwidget.samples.provideGlanceSample
+     *
+     * @sample androidx.glance.appwidget.samples.provideGlancePeriodicWorkSample
      */
     abstract suspend fun provideGlance(
         context: Context,
diff --git a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/RadioButton.kt b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/RadioButton.kt
index 63e99e3..aca8172 100644
--- a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/RadioButton.kt
+++ b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/RadioButton.kt
@@ -19,6 +19,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.graphics.Color
 import androidx.glance.Emittable
+import androidx.glance.EmittableCheckable
 import androidx.glance.ExperimentalGlanceApi
 import androidx.glance.GlanceModifier
 import androidx.glance.GlanceNode
@@ -39,13 +40,9 @@
 
 internal class EmittableRadioButton(
     var colors: RadioButtonColors
-) : Emittable {
+) : EmittableCheckable() {
     override var modifier: GlanceModifier = GlanceModifier
-    var checked: Boolean = false
     var enabled: Boolean = true
-    var text: String = ""
-    var style: TextStyle? = null
-    var maxLines: Int = Int.MAX_VALUE
 
     override fun copy(): Emittable = EmittableRadioButton(colors = colors).also {
         it.modifier = modifier
diff --git a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/Switch.kt b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/Switch.kt
index 2eb8a9f..dab1746 100644
--- a/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/Switch.kt
+++ b/glance/glance-appwidget/src/main/java/androidx/glance/appwidget/Switch.kt
@@ -18,6 +18,7 @@
 
 import androidx.compose.runtime.Composable
 import androidx.glance.Emittable
+import androidx.glance.EmittableCheckable
 import androidx.glance.ExperimentalGlanceApi
 import androidx.glance.GlanceModifier
 import androidx.glance.GlanceNode
@@ -226,12 +227,8 @@
 
 internal class EmittableSwitch(
     var colors: SwitchColors
-) : Emittable {
+) : EmittableCheckable() {
     override var modifier: GlanceModifier = GlanceModifier
-    var checked: Boolean = false
-    var text: String = ""
-    var style: TextStyle? = null
-    var maxLines: Int = Int.MAX_VALUE
 
     override fun copy(): Emittable = EmittableSwitch(colors = colors).also {
         it.modifier = modifier
diff --git a/glance/glance/api/current.txt b/glance/glance/api/current.txt
index d63e993..8f1b4c1 100644
--- a/glance/glance/api/current.txt
+++ b/glance/glance/api/current.txt
@@ -405,13 +405,17 @@
 
   public final class SemanticsProperties {
     method public androidx.glance.semantics.SemanticsPropertyKey<java.util.List<java.lang.String>> getContentDescription();
+    method public androidx.glance.semantics.SemanticsPropertyKey<java.lang.String> getTestTag();
     property public final androidx.glance.semantics.SemanticsPropertyKey<java.util.List<java.lang.String>> ContentDescription;
+    property public final androidx.glance.semantics.SemanticsPropertyKey<java.lang.String> TestTag;
     field public static final androidx.glance.semantics.SemanticsProperties INSTANCE;
   }
 
   public final class SemanticsPropertiesKt {
     method public static String getContentDescription(androidx.glance.semantics.SemanticsPropertyReceiver);
+    method public static String getTestTag(androidx.glance.semantics.SemanticsPropertyReceiver);
     method public static void setContentDescription(androidx.glance.semantics.SemanticsPropertyReceiver, String);
+    method public static void setTestTag(androidx.glance.semantics.SemanticsPropertyReceiver, String);
   }
 
   public final class SemanticsPropertyKey<T> {
diff --git a/glance/glance/api/restricted_current.txt b/glance/glance/api/restricted_current.txt
index d63e993..8f1b4c1 100644
--- a/glance/glance/api/restricted_current.txt
+++ b/glance/glance/api/restricted_current.txt
@@ -405,13 +405,17 @@
 
   public final class SemanticsProperties {
     method public androidx.glance.semantics.SemanticsPropertyKey<java.util.List<java.lang.String>> getContentDescription();
+    method public androidx.glance.semantics.SemanticsPropertyKey<java.lang.String> getTestTag();
     property public final androidx.glance.semantics.SemanticsPropertyKey<java.util.List<java.lang.String>> ContentDescription;
+    property public final androidx.glance.semantics.SemanticsPropertyKey<java.lang.String> TestTag;
     field public static final androidx.glance.semantics.SemanticsProperties INSTANCE;
   }
 
   public final class SemanticsPropertiesKt {
     method public static String getContentDescription(androidx.glance.semantics.SemanticsPropertyReceiver);
+    method public static String getTestTag(androidx.glance.semantics.SemanticsPropertyReceiver);
     method public static void setContentDescription(androidx.glance.semantics.SemanticsPropertyReceiver, String);
+    method public static void setTestTag(androidx.glance.semantics.SemanticsPropertyReceiver, String);
   }
 
   public final class SemanticsPropertyKey<T> {
diff --git a/glance/glance/src/main/java/androidx/glance/Emittables.kt b/glance/glance/src/main/java/androidx/glance/Emittables.kt
index 52a538a..5b03dbf 100644
--- a/glance/glance/src/main/java/androidx/glance/Emittables.kt
+++ b/glance/glance/src/main/java/androidx/glance/Emittables.kt
@@ -18,6 +18,7 @@
 
 import androidx.annotation.RestrictTo
 import androidx.glance.layout.Alignment
+import androidx.glance.text.TextStyle
 
 /** @suppress */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@@ -43,3 +44,17 @@
 abstract class EmittableLazyItemWithChildren : EmittableWithChildren() {
     var alignment: Alignment = Alignment.CenterStart
 }
+
+/** @suppress */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+abstract class EmittableWithText : Emittable {
+    var text: String = ""
+    var style: TextStyle? = null
+    var maxLines: Int = Int.MAX_VALUE
+}
+
+/** @suppress */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+abstract class EmittableCheckable : EmittableWithText() {
+    var checked: Boolean = false
+}
diff --git a/glance/glance/src/main/java/androidx/glance/semantics/SemanticsProperties.kt b/glance/glance/src/main/java/androidx/glance/semantics/SemanticsProperties.kt
index e40a921..e6ca8a1 100644
--- a/glance/glance/src/main/java/androidx/glance/semantics/SemanticsProperties.kt
+++ b/glance/glance/src/main/java/androidx/glance/semantics/SemanticsProperties.kt
@@ -32,6 +32,17 @@
             parentValue?.toMutableList()?.also { it.addAll(childValue) } ?: childValue
         }
     )
+
+    /**
+     * @see SemanticsPropertyReceiver.testTag
+     */
+    val TestTag = SemanticsPropertyKey<String>(
+        name = "TestTag",
+        mergePolicy = { parentValue, _ ->
+            // No merge
+            parentValue
+        }
+    )
 }
 
 /**
@@ -76,6 +87,24 @@
     set(value) { set(SemanticsProperties.ContentDescription, listOf(value)) }
 
 /**
+ * Test tag attached to this Glance composable node.
+ *
+ * This is a free form String and can be used to find nodes in testing frameworks.
+ */
+var SemanticsPropertyReceiver.testTag: String
+    /**
+     * Throws [UnsupportedOperationException]. Should not be called.
+     */
+    get() {
+        throw UnsupportedOperationException(
+            "You cannot retrieve a semantics property directly"
+        )
+    }
+    set(value) {
+        set(SemanticsProperties.TestTag, value)
+    }
+
+/**
  * Describes the semantics information associated with the owning component.
  */
 class SemanticsConfiguration : SemanticsPropertyReceiver {
diff --git a/glance/glance/src/main/java/androidx/glance/text/Text.kt b/glance/glance/src/main/java/androidx/glance/text/Text.kt
index ace0cfb..2f9974a 100644
--- a/glance/glance/src/main/java/androidx/glance/text/Text.kt
+++ b/glance/glance/src/main/java/androidx/glance/text/Text.kt
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2021 The Android Open Source Project
  *
@@ -21,6 +20,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.graphics.Color
 import androidx.glance.Emittable
+import androidx.glance.EmittableWithText
 import androidx.glance.GlanceModifier
 import androidx.glance.GlanceNode
 import androidx.glance.text.TextDefaults.defaultTextStyle
@@ -60,11 +60,8 @@
 
 /** @suppress */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-class EmittableText : Emittable {
+class EmittableText : EmittableWithText() {
     override var modifier: GlanceModifier = GlanceModifier
-    var text: String = ""
-    var style: TextStyle? = null
-    var maxLines: Int = Int.MAX_VALUE
 
     override fun copy(): Emittable = EmittableText().also {
         it.modifier = modifier
diff --git a/glance/glance/src/test/kotlin/androidx/glance/semantics/SemanticsTest.kt b/glance/glance/src/test/kotlin/androidx/glance/semantics/SemanticsTest.kt
index b336b0b..1c6caa6 100644
--- a/glance/glance/src/test/kotlin/androidx/glance/semantics/SemanticsTest.kt
+++ b/glance/glance/src/test/kotlin/androidx/glance/semantics/SemanticsTest.kt
@@ -16,20 +16,59 @@
 
 package androidx.glance.semantics
 
+import androidx.compose.ui.unit.dp
 import androidx.glance.GlanceModifier
 import androidx.glance.findModifier
+import androidx.glance.layout.size
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 
 class SemanticsTest {
     @Test
-    fun testModifier() {
-        val modifiers = GlanceModifier.semantics({ contentDescription = "test_description" })
+    fun contentDescription() {
+        val modifiers = GlanceModifier.semantics { contentDescription = "test_description" }
 
         val semanticsModifier = checkNotNull(modifiers.findModifier<SemanticsModifier>())
         assertThat(
-            semanticsModifier.configuration
-                .get(SemanticsProperties.ContentDescription).joinToString())
-            .isEqualTo("test_description")
+            semanticsModifier.configuration.getOrNull(SemanticsProperties.ContentDescription)
+                ?.joinToString()
+        ).isEqualTo("test_description")
+    }
+
+    @Test
+    fun noContentDescription() {
+        val modifiers = GlanceModifier.semantics { testTag = "test" }
+
+        val semanticsModifier = checkNotNull(modifiers.findModifier<SemanticsModifier>())
+        assertThat(
+            semanticsModifier.configuration.getOrNull(SemanticsProperties.ContentDescription)
+        ).isNull()
+    }
+
+    @Test
+    fun testTag() {
+        val modifiers = GlanceModifier.semantics { testTag = "test_tag" }
+
+        val semanticsModifier = checkNotNull(modifiers.findModifier<SemanticsModifier>())
+        assertThat(
+            semanticsModifier.configuration.getOrNull(SemanticsProperties.TestTag)
+        ).isEqualTo("test_tag")
+    }
+
+    @Test
+    fun noTestTag() {
+        val modifiers = GlanceModifier.semantics { contentDescription = "desc" }
+
+        val semanticsModifier = checkNotNull(modifiers.findModifier<SemanticsModifier>())
+        assertThat(
+            semanticsModifier.configuration.getOrNull(SemanticsProperties.TestTag)
+        ).isNull()
+    }
+
+    @Test
+    fun noSemantics() {
+        val modifiers = GlanceModifier.size(10.dp)
+
+        assertThat(modifiers.findModifier<SemanticsModifier>()).isNull()
     }
 }
diff --git a/gradle.properties b/gradle.properties
index c2a465b9..842f536 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -11,8 +11,7 @@
 org.gradle.dependency.verification=off
 org.gradle.dependency.verification.console=verbose
 org.gradle.unsafe.configuration-cache=true
-org.gradle.unsafe.configuration-cache-problems=warn
-org.gradle.unsafe.configuration-cache.max-problems=4000
+org.gradle.unsafe.configuration-cache-problems=fail
 
 android.lint.printStackTrace=true
 android.builder.sdkDownload=false
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 28f0bff..a4025c9 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -26,26 +26,26 @@
 byteBuddy = "1.12.10"
 asm = "9.3"
 cmake = "3.22.1"
-dagger = "2.44"
+dagger = "2.46.1"
 dexmaker = "2.28.3"
 dokka = "1.8.20-dev-214"
 espresso = "3.6.0-alpha01"
 espressoDevice = "1.0.0-alpha05"
 grpc = "1.52.0"
 guavaJre = "31.1-jre"
-hilt = "2.44"
+hilt = "2.46.1"
 incap = "0.2"
 jcodec = "0.2.5"
 kotlin17 = "1.7.10"
 kotlin18 = "1.8.22"
-kotlin19 = "1.9.0-Beta"
-kotlin = "1.8.22"
+kotlin19 = "1.9.0"
+kotlin = "1.9.0"
 kotlinBenchmark = "0.4.8"
-kotlinNative = "1.8.22"
+kotlinNative = "1.9.0"
 kotlinCompileTesting = "1.4.9"
 kotlinCoroutines = "1.7.1"
 kotlinSerialization = "1.3.3"
-ksp = "1.8.20-1.0.11"
+ksp = "1.9.0-1.0.11"
 ktfmt = "0.44"
 ktlint = "0.49.1"
 leakcanary = "2.8.1"
@@ -104,7 +104,7 @@
 checkerframework = { module = "org.checkerframework:checker-qual", version = "2.5.3" }
 checkmark = { module = "net.saff.checkmark:checkmark", version = "0.1.6" }
 constraintLayout = { module = "androidx.constraintlayout:constraintlayout", version = "2.0.1"}
-dackka = { module = "com.google.devsite:dackka", version = "1.3.4" }
+dackka = { module = "com.google.devsite:dackka", version = "1.3.5" }
 dagger = { module = "com.google.dagger:dagger", version.ref = "dagger" }
 daggerCompiler = { module = "com.google.dagger:dagger-compiler", version.ref = "dagger" }
 dexmakerMockito = { module = "com.linkedin.dexmaker:dexmaker-mockito", version.ref = "dexmaker" }
@@ -194,8 +194,8 @@
 kotlinTestJunit = { module = "org.jetbrains.kotlin:kotlin-test-junit" }
 kotlinTestJs = { module = "org.jetbrains.kotlin:kotlin-test-js" }
 kotlinReflect = { module = "org.jetbrains.kotlin:kotlin-reflect" }
-kotlinPoet = { module = "com.squareup:kotlinpoet", version = "1.12.0" }
-kotlinPoetJavaPoet = { module = "com.squareup:kotlinpoet-javapoet", version = "1.12.0" }
+kotlinPoet = { module = "com.squareup:kotlinpoet", version = "1.14.2" }
+kotlinPoetJavaPoet = { module = "com.squareup:kotlinpoet-javapoet", version = "1.14.2" }
 kotlinXHtml = { module = "org.jetbrains.kotlinx:kotlinx-html-jvm", version = "0.7.3" }
 ksp = { module = "com.google.devtools.ksp:symbol-processing", version.ref = "ksp" }
 kspApi = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" }
@@ -244,7 +244,7 @@
 paparazziNativeMacOsX64 = { module = "app.cash.paparazzi:layoutlib-native-macosx", version.ref = "paparazziNative" }
 protobuf = { module = "com.google.protobuf:protobuf-java", version.ref = "protobuf" }
 protobufCompiler = { module = "com.google.protobuf:protoc", version.ref = "protobuf" }
-protobufGradlePluginz = { module = "com.google.protobuf:protobuf-gradle-plugin", version = "0.9.3" }
+protobufGradlePlugin = { module = "com.google.protobuf:protobuf-gradle-plugin", version = "0.9.4" }
 protobufLite = { module = "com.google.protobuf:protobuf-javalite", version.ref = "protobuf" }
 protobufKotlin = { module = "com.google.protobuf:protobuf-kotlin", version.ref = "protobuf" }
 reactiveStreams = { module = "org.reactivestreams:reactive-streams", version = "1.0.0" }
@@ -264,7 +264,7 @@
 spdxGradlePluginz = { module = "org.spdx:spdx-gradle-plugin", version.ref = "spdxGradlePlugin" }
 sqldelightAndroid = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqldelight" }
 sqldelightCoroutinesExt = { module = "com.squareup.sqldelight:coroutines-extensions", version.ref = "sqldelight" }
-sqliteJdbc = { module = "org.xerial:sqlite-jdbc", version = "3.36.0" }
+sqliteJdbc = { module = "org.xerial:sqlite-jdbc", version = "3.41.2.2" }
 statelyConcurrency = { module = "co.touchlab:stately-concurrency", version.ref = "stately" }
 statelyConcurrentCollections = { module = "co.touchlab:stately-concurrent-collections", version.ref = "stately" }
 testCore = { module = "androidx.test:core", version.ref = "androidxTestCore" }
diff --git a/gradle/verification-keyring.keys b/gradle/verification-keyring.keys
index c1636dc..fa09465 100644
--- a/gradle/verification-keyring.keys
+++ b/gradle/verification-keyring.keys
@@ -2192,6 +2192,26 @@
 =84CK
 -----END PGP PUBLIC KEY BLOCK-----
 
+pub    59E05CE618187ED4
+uid    Taro L. Saito (For GitHub Actions) <[email protected]>
+
+sub    8857595B73BFD468
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: BCPG v1.68
+
+mDMEYuRVGhYJKwYBBAHaRw8BAQdA2Dp4m1Yhtb1g94pQzzL24FuP6b9KXF8lP9Dh
+hZnynhe0M1Rhcm8gTC4gU2FpdG8gKEZvciBHaXRIdWIgQWN0aW9ucykgPGxlb0B4
+ZXJpYWwub3JnPoiUBBMWCgA8FiEEwcunXsm9C6+AYZNUWeBc5hgYftQFAmLkVRoC
+GwMFCwkIBwIDIgIBBhUKCQgLAgQWAgMBAh4HAheAAAoJEFngXOYYGH7UfPwBAK7x
+TVRebZeWcAwmGaMUsbg7SgJou8xnkhByObPLUC/4AQDPsZeYmi4KXyXPzmqhCicd
+Y+ZSJWIDQqitK2ujPDFXA7g4BGLkVRoSCisGAQQBl1UBBQEBB0Atu9kejBi+6wfO
+T0a9z/LYEEdNXM/VX6xt1onKToPPdQMBCAeIeAQYFgoAIBYhBMHLp17JvQuvgGGT
+VFngXOYYGH7UBQJi5FUaAhsMAAoJEFngXOYYGH7UlMABAKyRCazhVyUFg5FOpAnm
+ckBY38CaMGPPLXVyY8Kr6dYFAP9wYLu7nsDZCOXkAgS+et4Pk1WZCggoYUkxsX1o
+0KZXBQ==
+=416A
+-----END PGP PUBLIC KEY BLOCK-----
+
 pub    6525FD70CC303655
 uid    Stephane Nicoll <[email protected]>
 
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index 5591e63..e094844 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -6,6 +6,7 @@
       <verify-signatures>true</verify-signatures>
       <key-servers enabled="false">
          <key-server uri="https://keyserver.ubuntu.com"/>
+         <key-server uri="https://keys.openpgp.org"/>
       </key-servers>
       <trusted-artifacts>
          <trust group="com.google.android.datatransport" reason="b/215442095"/>
@@ -356,6 +357,7 @@
          <trusted-key id="bcc135fc7ed8214f823d73e97fe9900f412d622e" group="com.google.flatbuffers"/>
          <trusted-key id="bdb5fa4fe719d787fb3d3197f6d4a1d411e9d1ae" group="com.google.guava"/>
          <trusted-key id="be685132afd2740d9095f9040cc0b712fee75827" group="org.assertj"/>
+         <trusted-key id="c1cba75ec9bd0baf8061935459e05ce618187ed4" group="org.xerial"/>
          <trusted-key id="c5aa57f4a38eba7b7f9156ddab2da4527f6ffc0b" group="com.squareup"/>
          <trusted-key id="c6f7d1c804c821f49af3bfc13ad93c3c677a106e" group="io.perfmark"/>
          <trusted-key id="c70b844f002f21f6d2b9c87522e44ac0622b91c3" group="com.beust"/>
@@ -403,6 +405,7 @@
          </trusted-key>
          <trusted-key id="db0597e3144342256bc81e3ec727d053c4481cf5" group="org.tensorflow"/>
          <trusted-key id="dbd744ace7ade6aa50dd591f66b50994442d2d40">
+            <trusting group="com.squareup"/>
             <trusting group="com.squareup.okhttp3"/>
             <trusting group="com.squareup.okio"/>
             <trusting group="com.squareup.wire"/>
@@ -459,19 +462,19 @@
       </trusted-keys>
    </configuration>
    <components>
-      <component group="" name="kotlin-native-prebuilt-linux-x86_64" version="1.8.22">
-         <artifact name="kotlin-native-prebuilt-linux-x86_64-1.8.22.tar.gz">
-            <sha256 value="43106db0ad8fb292facae924c97591529de15b69a0426d0474ac3811820a05b5" origin="Hand-built using sha256sum kotlin-native-prebuilt-linux-x86_64-1.8.22.tar.gz" reason="Artifact is not signed"/>
+      <component group="" name="kotlin-native-prebuilt-linux-x86_64" version="1.9.0">
+         <artifact name="kotlin-native-prebuilt-linux-x86_64-1.9.0.tar.gz">
+            <sha256 value="31737a9739fc37208e1f532b7472c3fbbf0d753f3621c9dfc1d72a69d5bc35c0" origin="Hand-built using sha256sum kotlin-native-prebuilt-linux-x86_64-1.9.0.tar.gz" reason="Artifact is not signed"/>
          </artifact>
       </component>
-      <component group="" name="kotlin-native-prebuilt-macos-aarch64" version="1.8.22">
-         <artifact name="kotlin-native-prebuilt-macos-aarch64-1.8.22.tar.gz">
-            <sha256 value="d693f7c70e491ee9fa1e9d7392192934fb1d504fab2a9c7e8dbe877af3228c7c" origin="Hand-built using sha256sum kotlin-native-prebuilt-macos-aarch64-1.8.22.tar.gz"/>
+      <component group="" name="kotlin-native-prebuilt-macos-aarch64" version="1.9.0">
+         <artifact name="kotlin-native-prebuilt-macos-aarch64-1.9.0.tar.gz">
+            <sha256 value="cbb700baef01980b9b9a6d499da7adff5c611dc61ed247efdf649a073c4dbb3c" origin="Hand-built using sha256sum kotlin-native-prebuilt-macos-aarch64-1.9.0.tar.gz"/>
          </artifact>
       </component>
-      <component group="" name="kotlin-native-prebuilt-macos-x86_64" version="1.8.22">
-         <artifact name="kotlin-native-prebuilt-macos-x86_64-1.8.22.tar.gz">
-            <sha256 value="5d924f4278028352cb2bdbaa4b3fab967ab81c3fbea845bdb91cd583af5c9bcc" origin="Hand-built using sha256sum kotlin-native-prebuilt-macos-x86_64-1.8.22.tar.gz"/>
+      <component group="" name="kotlin-native-prebuilt-macos-x86_64" version="1.9.0">
+         <artifact name="kotlin-native-prebuilt-macos-x86_64-1.9.0.tar.gz">
+            <sha256 value="ab02e67bc82d986875941036e147179e2812502bd2d6a8d8b3c511a93a8dbd1d" origin="Hand-built using sha256sum kotlin-native-prebuilt-macos-x86_64-1.9.0.tar.gz"/>
          </artifact>
       </component>
       <component group="aopalliance" name="aopalliance" version="1.0">
diff --git a/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/RoundedPolygon.kt b/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/RoundedPolygon.kt
index e492545..d0ba1e5 100644
--- a/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/RoundedPolygon.kt
+++ b/graphics/graphics-shapes/src/main/java/androidx/graphics/shapes/RoundedPolygon.kt
@@ -314,7 +314,7 @@
             val prevVertex = PointF(vertices[prevVtxIndex * 2], vertices[prevVtxIndex * 2 + 1])
             val nextVertex = PointF(vertices[nextVtxIndex * 2], vertices[nextVtxIndex * 2 + 1])
             val convex = (currVertex - prevVertex).clockwise(nextVertex - currVertex)
-            tempFeatures.add(Corner(cornerIndices, roundedCorners[i].center, currVertex,
+            tempFeatures.add(Corner(cornerIndices, currVertex, roundedCorners[i].center,
                 convex))
             tempFeatures.add(Edge(listOf(cubics.size)))
             cubics.add(Cubic.straightLine(corners[i].last().anchorX1, corners[i].last().anchorY1,
diff --git a/health/connect/connect-client/api/current.txt b/health/connect/connect-client/api/current.txt
index 7403050..e82f8fb 100644
--- a/health/connect/connect-client/api/current.txt
+++ b/health/connect/connect-client/api/current.txt
@@ -109,10 +109,10 @@
 
 package androidx.health.connect.client.contracts {
 
-  public final class ExerciseRouteRequestContract extends androidx.activity.result.contract.ActivityResultContract<java.lang.String,androidx.health.connect.client.records.ExerciseRoute.Data> {
+  public final class ExerciseRouteRequestContract extends androidx.activity.result.contract.ActivityResultContract<java.lang.String,androidx.health.connect.client.records.ExerciseRoute> {
     ctor public ExerciseRouteRequestContract();
     method public android.content.Intent createIntent(android.content.Context context, String input);
-    method public androidx.health.connect.client.records.ExerciseRoute.Data? parseResult(int resultCode, android.content.Intent? intent);
+    method public androidx.health.connect.client.records.ExerciseRoute? parseResult(int resultCode, android.content.Intent? intent);
   }
 
   public final class HealthPermissionsRequestContract extends androidx.activity.result.contract.ActivityResultContract<java.util.Set<? extends java.lang.String>,java.util.Set<? extends java.lang.String>> {
@@ -129,6 +129,7 @@
     method public static String getReadPermission(kotlin.reflect.KClass<? extends androidx.health.connect.client.records.Record> recordType);
     method public static String getWritePermission(kotlin.reflect.KClass<? extends androidx.health.connect.client.records.Record> recordType);
     field public static final androidx.health.connect.client.permission.HealthPermission.Companion Companion;
+    field public static final String PERMISSION_WRITE_EXERCISE_ROUTE = "android.permission.health.WRITE_EXERCISE_ROUTE";
   }
 
   public static final class HealthPermission.Companion {
@@ -441,15 +442,8 @@
     property public final java.time.Instant startTime;
   }
 
-  public abstract class ExerciseRoute {
-  }
-
-  public static final class ExerciseRoute.ConsentRequired extends androidx.health.connect.client.records.ExerciseRoute {
-    ctor public ExerciseRoute.ConsentRequired();
-  }
-
-  public static final class ExerciseRoute.Data extends androidx.health.connect.client.records.ExerciseRoute {
-    ctor public ExerciseRoute.Data(java.util.List<androidx.health.connect.client.records.ExerciseRoute.Location> route);
+  public final class ExerciseRoute {
+    ctor public ExerciseRoute(java.util.List<androidx.health.connect.client.records.ExerciseRoute.Location> route);
     method public java.util.List<androidx.health.connect.client.records.ExerciseRoute.Location> getRoute();
     property public final java.util.List<androidx.health.connect.client.records.ExerciseRoute.Location> route;
   }
@@ -474,8 +468,21 @@
   public static final class ExerciseRoute.Location.Companion {
   }
 
-  public static final class ExerciseRoute.NoData extends androidx.health.connect.client.records.ExerciseRoute {
-    ctor public ExerciseRoute.NoData();
+  public abstract class ExerciseRouteResult {
+  }
+
+  public static final class ExerciseRouteResult.ConsentRequired extends androidx.health.connect.client.records.ExerciseRouteResult {
+    ctor public ExerciseRouteResult.ConsentRequired();
+  }
+
+  public static final class ExerciseRouteResult.Data extends androidx.health.connect.client.records.ExerciseRouteResult {
+    ctor public ExerciseRouteResult.Data(androidx.health.connect.client.records.ExerciseRoute exerciseRoute);
+    method public androidx.health.connect.client.records.ExerciseRoute getExerciseRoute();
+    property public final androidx.health.connect.client.records.ExerciseRoute exerciseRoute;
+  }
+
+  public static final class ExerciseRouteResult.NoData extends androidx.health.connect.client.records.ExerciseRouteResult {
+    ctor public ExerciseRouteResult.NoData();
   }
 
   public final class ExerciseSegment {
@@ -569,10 +576,10 @@
     ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata, optional java.util.List<androidx.health.connect.client.records.ExerciseSegment> segments);
     ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata, optional java.util.List<androidx.health.connect.client.records.ExerciseSegment> segments, optional java.util.List<androidx.health.connect.client.records.ExerciseLap> laps);
-    ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata, optional java.util.List<androidx.health.connect.client.records.ExerciseSegment> segments, optional java.util.List<androidx.health.connect.client.records.ExerciseLap> laps, optional androidx.health.connect.client.records.ExerciseRoute.Data? exerciseRouteData);
+    ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata, optional java.util.List<androidx.health.connect.client.records.ExerciseSegment> segments, optional java.util.List<androidx.health.connect.client.records.ExerciseLap> laps, optional androidx.health.connect.client.records.ExerciseRoute? exerciseRoute);
     method public java.time.Instant getEndTime();
     method public java.time.ZoneOffset? getEndZoneOffset();
-    method public androidx.health.connect.client.records.ExerciseRoute getExerciseRoute();
+    method public androidx.health.connect.client.records.ExerciseRouteResult getExerciseRouteResult();
     method public int getExerciseType();
     method public java.util.List<androidx.health.connect.client.records.ExerciseLap> getLaps();
     method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
@@ -583,7 +590,7 @@
     method public String? getTitle();
     property public java.time.Instant endTime;
     property public java.time.ZoneOffset? endZoneOffset;
-    property public final androidx.health.connect.client.records.ExerciseRoute exerciseRoute;
+    property public final androidx.health.connect.client.records.ExerciseRouteResult exerciseRouteResult;
     property public final int exerciseType;
     property public final java.util.List<androidx.health.connect.client.records.ExerciseLap> laps;
     property public androidx.health.connect.client.records.metadata.Metadata metadata;
diff --git a/health/connect/connect-client/api/restricted_current.txt b/health/connect/connect-client/api/restricted_current.txt
index 7300554..582c44b 100644
--- a/health/connect/connect-client/api/restricted_current.txt
+++ b/health/connect/connect-client/api/restricted_current.txt
@@ -109,10 +109,10 @@
 
 package androidx.health.connect.client.contracts {
 
-  public final class ExerciseRouteRequestContract extends androidx.activity.result.contract.ActivityResultContract<java.lang.String,androidx.health.connect.client.records.ExerciseRoute.Data> {
+  public final class ExerciseRouteRequestContract extends androidx.activity.result.contract.ActivityResultContract<java.lang.String,androidx.health.connect.client.records.ExerciseRoute> {
     ctor public ExerciseRouteRequestContract();
     method public android.content.Intent createIntent(android.content.Context context, String input);
-    method public androidx.health.connect.client.records.ExerciseRoute.Data? parseResult(int resultCode, android.content.Intent? intent);
+    method public androidx.health.connect.client.records.ExerciseRoute? parseResult(int resultCode, android.content.Intent? intent);
   }
 
   public final class HealthPermissionsRequestContract extends androidx.activity.result.contract.ActivityResultContract<java.util.Set<? extends java.lang.String>,java.util.Set<? extends java.lang.String>> {
@@ -129,6 +129,7 @@
     method public static String getReadPermission(kotlin.reflect.KClass<? extends androidx.health.connect.client.records.Record> recordType);
     method public static String getWritePermission(kotlin.reflect.KClass<? extends androidx.health.connect.client.records.Record> recordType);
     field public static final androidx.health.connect.client.permission.HealthPermission.Companion Companion;
+    field public static final String PERMISSION_WRITE_EXERCISE_ROUTE = "android.permission.health.WRITE_EXERCISE_ROUTE";
   }
 
   public static final class HealthPermission.Companion {
@@ -441,15 +442,8 @@
     property public final java.time.Instant startTime;
   }
 
-  public abstract class ExerciseRoute {
-  }
-
-  public static final class ExerciseRoute.ConsentRequired extends androidx.health.connect.client.records.ExerciseRoute {
-    ctor public ExerciseRoute.ConsentRequired();
-  }
-
-  public static final class ExerciseRoute.Data extends androidx.health.connect.client.records.ExerciseRoute {
-    ctor public ExerciseRoute.Data(java.util.List<androidx.health.connect.client.records.ExerciseRoute.Location> route);
+  public final class ExerciseRoute {
+    ctor public ExerciseRoute(java.util.List<androidx.health.connect.client.records.ExerciseRoute.Location> route);
     method public java.util.List<androidx.health.connect.client.records.ExerciseRoute.Location> getRoute();
     property public final java.util.List<androidx.health.connect.client.records.ExerciseRoute.Location> route;
   }
@@ -474,8 +468,21 @@
   public static final class ExerciseRoute.Location.Companion {
   }
 
-  public static final class ExerciseRoute.NoData extends androidx.health.connect.client.records.ExerciseRoute {
-    ctor public ExerciseRoute.NoData();
+  public abstract class ExerciseRouteResult {
+  }
+
+  public static final class ExerciseRouteResult.ConsentRequired extends androidx.health.connect.client.records.ExerciseRouteResult {
+    ctor public ExerciseRouteResult.ConsentRequired();
+  }
+
+  public static final class ExerciseRouteResult.Data extends androidx.health.connect.client.records.ExerciseRouteResult {
+    ctor public ExerciseRouteResult.Data(androidx.health.connect.client.records.ExerciseRoute exerciseRoute);
+    method public androidx.health.connect.client.records.ExerciseRoute getExerciseRoute();
+    property public final androidx.health.connect.client.records.ExerciseRoute exerciseRoute;
+  }
+
+  public static final class ExerciseRouteResult.NoData extends androidx.health.connect.client.records.ExerciseRouteResult {
+    ctor public ExerciseRouteResult.NoData();
   }
 
   public final class ExerciseSegment {
@@ -569,10 +576,10 @@
     ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata);
     ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata, optional java.util.List<androidx.health.connect.client.records.ExerciseSegment> segments);
     ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata, optional java.util.List<androidx.health.connect.client.records.ExerciseSegment> segments, optional java.util.List<androidx.health.connect.client.records.ExerciseLap> laps);
-    ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata, optional java.util.List<androidx.health.connect.client.records.ExerciseSegment> segments, optional java.util.List<androidx.health.connect.client.records.ExerciseLap> laps, optional androidx.health.connect.client.records.ExerciseRoute.Data? exerciseRouteData);
+    ctor public ExerciseSessionRecord(java.time.Instant startTime, java.time.ZoneOffset? startZoneOffset, java.time.Instant endTime, java.time.ZoneOffset? endZoneOffset, int exerciseType, optional String? title, optional String? notes, optional androidx.health.connect.client.records.metadata.Metadata metadata, optional java.util.List<androidx.health.connect.client.records.ExerciseSegment> segments, optional java.util.List<androidx.health.connect.client.records.ExerciseLap> laps, optional androidx.health.connect.client.records.ExerciseRoute? exerciseRoute);
     method public java.time.Instant getEndTime();
     method public java.time.ZoneOffset? getEndZoneOffset();
-    method public androidx.health.connect.client.records.ExerciseRoute getExerciseRoute();
+    method public androidx.health.connect.client.records.ExerciseRouteResult getExerciseRouteResult();
     method public int getExerciseType();
     method public java.util.List<androidx.health.connect.client.records.ExerciseLap> getLaps();
     method public androidx.health.connect.client.records.metadata.Metadata getMetadata();
@@ -583,7 +590,7 @@
     method public String? getTitle();
     property public java.time.Instant endTime;
     property public java.time.ZoneOffset? endZoneOffset;
-    property public final androidx.health.connect.client.records.ExerciseRoute exerciseRoute;
+    property public final androidx.health.connect.client.records.ExerciseRouteResult exerciseRouteResult;
     property public final int exerciseType;
     property public final java.util.List<androidx.health.connect.client.records.ExerciseLap> laps;
     property public androidx.health.connect.client.records.metadata.Metadata metadata;
diff --git a/health/connect/connect-client/build.gradle b/health/connect/connect-client/build.gradle
index 16260ad..fc33af3 100644
--- a/health/connect/connect-client/build.gradle
+++ b/health/connect/connect-client/build.gradle
@@ -40,7 +40,7 @@
     implementation(libs.guavaAndroid)
     implementation(libs.kotlinCoroutinesAndroid)
     implementation(libs.kotlinCoroutinesGuava)
-    implementation("androidx.core:core-ktx:1.8.0")
+    implementation("androidx.core:core-ktx:1.12.0-alpha05")
 
     testImplementation(libs.testCore)
     testImplementation(libs.testRunner)
diff --git a/health/connect/connect-client/samples/src/main/java/androidx/health/connect/client/samples/InsertRecordsSamples.kt b/health/connect/connect-client/samples/src/main/java/androidx/health/connect/client/samples/InsertRecordsSamples.kt
index 7565e81..f22f57d 100644
--- a/health/connect/connect-client/samples/src/main/java/androidx/health/connect/client/samples/InsertRecordsSamples.kt
+++ b/health/connect/connect-client/samples/src/main/java/androidx/health/connect/client/samples/InsertRecordsSamples.kt
@@ -20,9 +20,14 @@
 
 import androidx.annotation.Sampled
 import androidx.health.connect.client.HealthConnectClient
+import androidx.health.connect.client.permission.HealthPermission.Companion.PERMISSION_WRITE_EXERCISE_ROUTE
+import androidx.health.connect.client.permission.HealthPermission.Companion.getWritePermission
+import androidx.health.connect.client.records.ExerciseRoute
+import androidx.health.connect.client.records.ExerciseSessionRecord
 import androidx.health.connect.client.records.HeartRateRecord
 import androidx.health.connect.client.records.NutritionRecord
 import androidx.health.connect.client.records.StepsRecord
+import androidx.health.connect.client.units.Length
 import androidx.health.connect.client.units.grams
 import androidx.health.connect.client.units.kilocalories
 import java.time.Duration
@@ -89,3 +94,54 @@
         )
     healthConnectClient.insertRecords(listOf(heartRateRecord))
 }
+
+@Sampled
+suspend fun InsertExerciseRoute(healthConnectClient: HealthConnectClient) {
+    val grantedPermissions = healthConnectClient.permissionController.getGrantedPermissions()
+
+    if (!grantedPermissions.contains(getWritePermission(ExerciseSessionRecord::class))) {
+        return
+    }
+
+    val sessionStartTime = Instant.parse("2023-07-11T10:00:00.00Z")
+    val sessionDuration = Duration.ofMinutes(10)
+
+    val startLatitude = 51.511831
+    val endLatitude = 51.506007
+    val startLongitude = -0.165785
+    val endLongitude = -0.164888
+    val latitudeDeltaPerSecond = (endLatitude - startLatitude) / sessionDuration.seconds
+    val longitudeDeltaPerSecond = (endLongitude - startLongitude) / sessionDuration.seconds
+
+    val exerciseRoute =
+        if (grantedPermissions.contains(PERMISSION_WRITE_EXERCISE_ROUTE)) {
+            ExerciseRoute(
+                List(sessionDuration.seconds.toInt()) { timeSeconds ->
+                    ExerciseRoute.Location(
+                        time = sessionStartTime.plusSeconds(timeSeconds.toLong()),
+                        latitude = startLatitude + latitudeDeltaPerSecond * timeSeconds,
+                        longitude = startLongitude + longitudeDeltaPerSecond * timeSeconds,
+                        horizontalAccuracy = Length.meters(2.0),
+                        verticalAccuracy = Length.meters(2.0),
+                        altitude = Length.meters(19.0)
+                    )
+                }
+            )
+        } else {
+            null
+        }
+
+    val exerciseSessionRecord =
+        ExerciseSessionRecord(
+            startTime = sessionStartTime,
+            startZoneOffset = ZoneOffset.UTC,
+            endTime = sessionStartTime.plus(sessionDuration),
+            endZoneOffset = ZoneOffset.UTC,
+            exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_RUNNING,
+            title = "Morning Run",
+            notes = "A nice run in a park",
+            exerciseRoute = exerciseRoute
+        )
+
+    healthConnectClient.insertRecords(listOf(exerciseSessionRecord))
+}
diff --git a/health/connect/connect-client/samples/src/main/java/androidx/health/connect/client/samples/ReadRecordsSamples.kt b/health/connect/connect-client/samples/src/main/java/androidx/health/connect/client/samples/ReadRecordsSamples.kt
index 8f5cca7..578fd0e 100644
--- a/health/connect/connect-client/samples/src/main/java/androidx/health/connect/client/samples/ReadRecordsSamples.kt
+++ b/health/connect/connect-client/samples/src/main/java/androidx/health/connect/client/samples/ReadRecordsSamples.kt
@@ -23,6 +23,7 @@
 import androidx.health.connect.client.HealthConnectClient
 import androidx.health.connect.client.contracts.ExerciseRouteRequestContract
 import androidx.health.connect.client.records.ExerciseRoute
+import androidx.health.connect.client.records.ExerciseRouteResult
 import androidx.health.connect.client.records.ExerciseSessionRecord
 import androidx.health.connect.client.records.HeartRateRecord
 import androidx.health.connect.client.records.SleepSessionRecord
@@ -86,14 +87,14 @@
 suspend fun ReadExerciseRoute(
     activityResultCaller: ActivityResultCaller,
     healthConnectClient: HealthConnectClient,
-    displayExerciseRoute: (ExerciseRoute.Data) -> Unit,
+    displayExerciseRoute: (ExerciseRoute) -> Unit,
     recordId: String
 ) {
     // See https://developer.android.com/training/basics/intents/result#launch for appropriately
     // handling ActivityResultContract.
     val requestExerciseRoute =
         activityResultCaller.registerForActivityResult(ExerciseRouteRequestContract()) {
-            exerciseRoute: ExerciseRoute.Data? ->
+            exerciseRoute: ExerciseRoute? ->
             if (exerciseRoute != null) {
                 displayExerciseRoute(exerciseRoute)
             } else {
@@ -105,10 +106,10 @@
     val exerciseSessionRecord =
         healthConnectClient.readRecord(ExerciseSessionRecord::class, recordId).record
 
-    when (val exerciseRoute = exerciseSessionRecord.exerciseRoute) {
-        is ExerciseRoute.Data -> displayExerciseRoute(exerciseRoute)
-        is ExerciseRoute.ConsentRequired -> requestExerciseRoute.launch(recordId)
-        is ExerciseRoute.NoData -> Unit // No exercise route to show
+    when (val exerciseRouteResult = exerciseSessionRecord.exerciseRouteResult) {
+        is ExerciseRouteResult.Data -> displayExerciseRoute(exerciseRouteResult.exerciseRoute)
+        is ExerciseRouteResult.ConsentRequired -> requestExerciseRoute.launch(recordId)
+        is ExerciseRouteResult.NoData -> Unit // No exercise route to show
         else -> Unit
     }
 }
diff --git a/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/RequestExerciseRouteUpsideDownCakeTest.kt b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/RequestExerciseRouteUpsideDownCakeTest.kt
index 23160ad..ea4548d 100644
--- a/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/RequestExerciseRouteUpsideDownCakeTest.kt
+++ b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/RequestExerciseRouteUpsideDownCakeTest.kt
@@ -80,7 +80,7 @@
         val intent = Intent()
         intent.putExtra(HealthConnectManager.EXTRA_EXERCISE_ROUTE, PlatformExerciseRoute(listOf()))
         val result = requestRouteContract.parseResult(0, intent)
-        assertThat(result).isEqualTo(ExerciseRoute.Data(listOf()))
+        assertThat(result).isEqualTo(ExerciseRoute(listOf()))
     }
 
     @Test
@@ -103,7 +103,7 @@
         val result = requestRouteContract.parseResult(0, intent)
         assertThat(result)
             .isEqualTo(
-                ExerciseRoute.Data(
+                ExerciseRoute(
                     listOf(
                         ExerciseRoute.Location(
                             time = Instant.ofEpochMilli(1234L),
diff --git a/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/records/RecordConvertersTest.kt b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/records/RecordConvertersTest.kt
index d0cf773..953b19b 100644
--- a/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/records/RecordConvertersTest.kt
+++ b/health/connect/connect-client/src/androidTest/java/androidx/health/connect/client/impl/platform/records/RecordConvertersTest.kt
@@ -35,6 +35,7 @@
 import androidx.health.connect.client.records.ElevationGainedRecord
 import androidx.health.connect.client.records.ExerciseLap
 import androidx.health.connect.client.records.ExerciseRoute
+import androidx.health.connect.client.records.ExerciseRouteResult
 import androidx.health.connect.client.records.ExerciseSegment
 import androidx.health.connect.client.records.ExerciseSessionRecord
 import androidx.health.connect.client.records.FloorsClimbedRecord
@@ -408,7 +409,7 @@
                             )
                         ),
                     exerciseRoute =
-                        ExerciseRoute.Data(
+                        ExerciseRoute(
                             listOf(
                                 ExerciseRoute.Location(
                                     START_TIME,
@@ -1261,17 +1262,19 @@
                         10
                     )
                 )
-            assertThat(exerciseRoute as ExerciseRoute.Data)
+            assertThat(exerciseRouteResult as ExerciseRouteResult.Data)
                 .isEqualTo(
-                    ExerciseRoute.Data(
-                        listOf(
-                            ExerciseRoute.Location(
-                                time = START_TIME,
-                                latitude = 23.4,
-                                longitude = -23.4,
-                                altitude = Length.meters(10.0),
-                                horizontalAccuracy = Length.meters(2.0),
-                                verticalAccuracy = Length.meters(3.0)
+                    ExerciseRouteResult.Data(
+                        ExerciseRoute(
+                            listOf(
+                                ExerciseRoute.Location(
+                                    time = START_TIME,
+                                    latitude = 23.4,
+                                    longitude = -23.4,
+                                    altitude = Length.meters(10.0),
+                                    horizontalAccuracy = Length.meters(2.0),
+                                    verticalAccuracy = Length.meters(3.0)
+                                )
                             )
                         )
                     )
@@ -1283,7 +1286,7 @@
                 as ExerciseSessionRecord
 
         assertSdkRecord(sdkExerciseSession) {
-            assertThat(exerciseRoute).isEqualTo(ExerciseRoute.NoData())
+            assertThat(exerciseRouteResult).isEqualTo(ExerciseRouteResult.NoData())
         }
     }
 
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/contracts/ExerciseRouteRequestContract.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/contracts/ExerciseRouteRequestContract.kt
index 6e8e74b..0e1e78f 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/contracts/ExerciseRouteRequestContract.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/contracts/ExerciseRouteRequestContract.kt
@@ -32,9 +32,9 @@
  *
  * @sample androidx.health.connect.client.samples.ReadExerciseRoute
  */
-class ExerciseRouteRequestContract : ActivityResultContract<String, ExerciseRoute.Data?>() {
+class ExerciseRouteRequestContract : ActivityResultContract<String, ExerciseRoute?>() {
 
-    private val delegate: ActivityResultContract<String, ExerciseRoute.Data?> =
+    private val delegate: ActivityResultContract<String, ExerciseRoute?> =
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
             RequestExerciseRouteUpsideDownCake()
         } else {
@@ -42,7 +42,7 @@
         }
 
     /**
-     * Creates an intent to request an [ExerciseRoute.Data]. It receives the exercise session id as
+     * Creates an intent to request an [ExerciseRoute]. It receives the exercise session id as
      * [input].
      *
      * @param context the context
@@ -56,13 +56,13 @@
     }
 
     /**
-     * Converts the activity result into [ExerciseRoute.Data], to return as output.
+     * Converts the activity result into [ExerciseRoute], to return as output.
      *
      * @return null if the user didn't grant access to the exercise route or if there's no exercise
      *   route for the session id passed on [createIntent].
      * @see ActivityResultContract.parseResult
      */
-    override fun parseResult(resultCode: Int, intent: Intent?): ExerciseRoute.Data? {
+    override fun parseResult(resultCode: Int, intent: Intent?): ExerciseRoute? {
         return delegate.parseResult(resultCode, intent)
     }
 }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/HealthConnectClientImpl.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/HealthConnectClientImpl.kt
index 2fcac9a..8d6bfec 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/HealthConnectClientImpl.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/HealthConnectClientImpl.kt
@@ -64,11 +64,16 @@
 internal constructor(
     private val delegate: HealthDataAsyncClient,
     private val allPermissions: List<String> =
-        HealthPermission.RECORD_TYPE_TO_PERMISSION.flatMap {
-            listOf<String>(
-                HealthPermission.WRITE_PERMISSION_PREFIX + it.value,
-                HealthPermission.READ_PERMISSION_PREFIX + it.value
+        buildList() {
+            addAll(
+                HealthPermission.RECORD_TYPE_TO_PERMISSION.flatMap {
+                    listOf<String>(
+                        HealthPermission.WRITE_PERMISSION_PREFIX + it.value,
+                        HealthPermission.READ_PERMISSION_PREFIX + it.value
+                    )
+                }
             )
+            add(HealthPermission.PERMISSION_WRITE_EXERCISE_ROUTE)
         },
 ) : HealthConnectClient, PermissionController {
 
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordConverters.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordConverters.kt
index dc60de8..a7f79299 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordConverters.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordConverters.kt
@@ -35,6 +35,7 @@
 import androidx.health.connect.client.records.DistanceRecord
 import androidx.health.connect.client.records.ElevationGainedRecord
 import androidx.health.connect.client.records.ExerciseRoute
+import androidx.health.connect.client.records.ExerciseRouteResult
 import androidx.health.connect.client.records.ExerciseSessionRecord
 import androidx.health.connect.client.records.FloorsClimbedRecord
 import androidx.health.connect.client.records.HeartRateRecord
@@ -421,13 +422,13 @@
                     metadata = metadata,
                     segments = subTypeDataListsMap["segments"]?.toSegmentList() ?: emptyList(),
                     laps = subTypeDataListsMap["laps"]?.toLapList() ?: emptyList(),
-                    exerciseRoute =
+                    exerciseRouteResult =
                         subTypeDataListsMap["route"]?.let {
-                            ExerciseRoute.Data(route = it.toLocationList())
+                            ExerciseRouteResult.Data(ExerciseRoute(route = it.toLocationList()))
                         }
                             ?: if (valuesMap["hasRoute"]?.booleanVal == true)
-                                ExerciseRoute.ConsentRequired()
-                            else ExerciseRoute.NoData(),
+                                ExerciseRouteResult.ConsentRequired()
+                            else ExerciseRouteResult.NoData(),
                 )
             }
             "Distance" ->
@@ -587,8 +588,8 @@
 
 fun toExerciseRouteData(
     protoWrapper: androidx.health.platform.client.exerciseroute.ExerciseRoute
-): ExerciseRoute.Data {
-    return ExerciseRoute.Data(
+): ExerciseRoute {
+    return ExerciseRoute(
         protoWrapper.proto.valuesList.map { value ->
             ExerciseRoute.Location(
                 time = Instant.ofEpochMilli(value.startTimeMillis),
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordUtils.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordUtils.kt
index f2fa0f4..168a416 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordUtils.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/ProtoToRecordUtils.kt
@@ -119,17 +119,17 @@
             startTime = Instant.ofEpochMilli(it.startTimeMillis),
             endTime = Instant.ofEpochMilli(it.endTimeMillis),
             stage = STAGE_TYPE_STRING_TO_INT_MAP[it.valuesMap["stage"]?.enumVal]
-                ?: SleepSessionRecord.STAGE_TYPE_UNKNOWN
+                    ?: SleepSessionRecord.STAGE_TYPE_UNKNOWN
         )
     }
 }
+
 internal fun DataProto.DataPoint.SubTypeDataList.toSegmentList(): List<ExerciseSegment> {
     return valuesList.map {
         ExerciseSegment(
             startTime = Instant.ofEpochMilli(it.startTimeMillis),
             endTime = Instant.ofEpochMilli(it.endTimeMillis),
-            segmentType = (it.valuesMap["type"]?.longVal
-                ?: EXERCISE_SEGMENT_TYPE_UNKNOWN).toInt(),
+            segmentType = (it.valuesMap["type"]?.longVal ?: EXERCISE_SEGMENT_TYPE_UNKNOWN).toInt(),
             repetitions = it.valuesMap["reps"]?.longVal?.toInt() ?: 0
         )
     }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/RecordToProtoConverters.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/RecordToProtoConverters.kt
index e9e29d8..34127fc 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/RecordToProtoConverters.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/RecordToProtoConverters.kt
@@ -34,7 +34,7 @@
 import androidx.health.connect.client.records.CyclingPedalingCadenceRecord
 import androidx.health.connect.client.records.DistanceRecord
 import androidx.health.connect.client.records.ElevationGainedRecord
-import androidx.health.connect.client.records.ExerciseRoute
+import androidx.health.connect.client.records.ExerciseRouteResult
 import androidx.health.connect.client.records.ExerciseSessionRecord
 import androidx.health.connect.client.records.FloorsClimbedRecord
 import androidx.health.connect.client.records.HeartRateRecord
@@ -289,7 +289,7 @@
         is ExerciseSessionRecord ->
             intervalProto()
                 .setDataType(protoDataType("ActivitySession"))
-                .putValues("hasRoute", boolVal(exerciseRoute !is ExerciseRoute.NoData))
+                .putValues("hasRoute", boolVal(exerciseRouteResult !is ExerciseRouteResult.NoData))
                 .apply {
                     val exerciseType =
                         enumValFromInt(
@@ -316,11 +316,13 @@
                                 .build()
                         )
                     }
-                    if (exerciseRoute is ExerciseRoute.Data) {
+                    if (exerciseRouteResult is ExerciseRouteResult.Data) {
                         putSubTypeDataLists(
                             "route",
                             DataProto.DataPoint.SubTypeDataList.newBuilder()
-                                .addAllValues(exerciseRoute.route.map { it.toProto() })
+                                .addAllValues(
+                                    exerciseRouteResult.exerciseRoute.route.map { it.toProto() }
+                                )
                                 .build()
                         )
                     }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/RecordToProtoUtils.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/RecordToProtoUtils.kt
index 8c1cbe2..3e5fa01 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/RecordToProtoUtils.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/converters/records/RecordToProtoUtils.kt
@@ -100,8 +100,10 @@
             enumValFromInt(stage, SleepSessionRecord.STAGE_TYPE_INT_TO_STRING_MAP)?.let {
                 putValues("stage", it)
             }
-        }.build()
+        }
+        .build()
 }
+
 internal fun ExerciseSegment.toProto(): DataProto.SubTypeDataValue {
     return DataProto.SubTypeDataValue.newBuilder()
         .setStartTimeMillis(startTime.toEpochMilli())
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/RecordConverters.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/RecordConverters.kt
index 4da86be..ad43fd6 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/RecordConverters.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/impl/platform/records/RecordConverters.kt
@@ -36,6 +36,7 @@
 import androidx.health.connect.client.records.ElevationGainedRecord
 import androidx.health.connect.client.records.ExerciseLap
 import androidx.health.connect.client.records.ExerciseRoute
+import androidx.health.connect.client.records.ExerciseRouteResult
 import androidx.health.connect.client.records.ExerciseSegment
 import androidx.health.connect.client.records.ExerciseSessionRecord
 import androidx.health.connect.client.records.FloorsClimbedRecord
@@ -293,8 +294,9 @@
         laps = laps.map { it.toSdkExerciseLap() }.sortedBy { it.startTime },
         segments = segments.map { it.toSdkExerciseSegment() }.sortedBy { it.startTime },
         metadata = metadata.toSdkMetadata(),
-        exerciseRoute = route?.toSdkExerciseRouteData()
-                ?: if (hasRoute()) ExerciseRoute.ConsentRequired() else ExerciseRoute.NoData(),
+        exerciseRouteResult = route?.let { ExerciseRouteResult.Data(it.toSdkExerciseRoute()) }
+                ?: if (hasRoute()) ExerciseRouteResult.ConsentRequired()
+                else ExerciseRouteResult.NoData(),
     )
 
 private fun PlatformFloorsClimbedRecord.toSdkFloorsClimbedRecord() =
@@ -708,8 +710,8 @@
             title?.let { setTitle(it) }
             setLaps(laps.map { it.toPlatformExerciseLap() })
             setSegments(segments.map { it.toPlatformExerciseSegment() })
-            if (exerciseRoute is ExerciseRoute.Data) {
-                setRoute(exerciseRoute.toPlatformExerciseRoute())
+            if (exerciseRouteResult is ExerciseRouteResult.Data) {
+                setRoute(exerciseRouteResult.exerciseRoute.toPlatformExerciseRoute())
             }
         }
         .build()
@@ -719,7 +721,7 @@
         .apply { length?.let { setLength(it.toPlatformLength()) } }
         .build()
 
-private fun ExerciseRoute.Data.toPlatformExerciseRoute() =
+private fun ExerciseRoute.toPlatformExerciseRoute() =
     PlatformExerciseRoute(
         route.map { location ->
             PlatformExerciseRouteLocationBuilder(
@@ -1033,8 +1035,8 @@
 private fun PlatformSleepSessionStage.toSdkSleepSessionStage() =
     SleepSessionRecord.Stage(startTime, endTime, type.toSdkSleepStageType())
 
-internal fun PlatformExerciseRoute.toSdkExerciseRouteData() =
-    ExerciseRoute.Data(
+internal fun PlatformExerciseRoute.toSdkExerciseRoute() =
+    ExerciseRoute(
         routeLocations.map { value ->
             ExerciseRoute.Location(
                 time = value.time,
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/HealthPermission.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/HealthPermission.kt
index d09bd4e..6c665b0 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/HealthPermission.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/HealthPermission.kt
@@ -131,6 +131,21 @@
 
         internal const val PERMISSION_PREFIX = "android.permission.health."
 
+        /**
+         * A permission to write exercise routes.
+         *
+         * This permission must be granted to successfully insert a route as a field of the
+         * corresponding [androidx.health.connect.client.records.ExerciseSessionRecord]. An attempt
+         * to insert/update a session with a set route without the permission granted will result in
+         * a failed call and the session insertion/update will be rejected.
+         *
+         * If the permission is not granted the previously written route will not be deleted if the
+         * session gets updated with no route set.
+         *
+         * @sample androidx.health.connect.client.samples.InsertExerciseRoute
+         */
+        const val PERMISSION_WRITE_EXERCISE_ROUTE = PERMISSION_PREFIX + "WRITE_EXERCISE_ROUTE"
+
         // Read permissions for ACTIVITY.
         internal const val READ_ACTIVE_CALORIES_BURNED =
             PERMISSION_PREFIX + "READ_ACTIVE_CALORIES_BURNED"
@@ -153,15 +168,18 @@
         internal const val READ_BODY_WATER_MASS = PERMISSION_PREFIX + "READ_BODY_WATER_MASS"
         internal const val READ_BONE_MASS = PERMISSION_PREFIX + "READ_BONE_MASS"
         internal const val READ_HEIGHT = PERMISSION_PREFIX + "READ_HEIGHT"
+
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         internal const val READ_HIP_CIRCUMFERENCE = PERMISSION_PREFIX + "READ_HIP_CIRCUMFERENCE"
         internal const val READ_LEAN_BODY_MASS = PERMISSION_PREFIX + "READ_LEAN_BODY_MASS"
+
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         internal const val READ_WAIST_CIRCUMFERENCE = PERMISSION_PREFIX + "READ_WAIST_CIRCUMFERENCE"
         internal const val READ_WEIGHT = PERMISSION_PREFIX + "READ_WEIGHT"
 
         // Read permissions for CYCLE_TRACKING.
         internal const val READ_CERVICAL_MUCUS = PERMISSION_PREFIX + "READ_CERVICAL_MUCUS"
+
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         internal const val READ_INTERMENSTRUAL_BLEEDING =
             PERMISSION_PREFIX + "READ_INTERMENSTRUAL_BLEEDING"
@@ -195,7 +213,6 @@
         internal const val WRITE_DISTANCE = PERMISSION_PREFIX + "WRITE_DISTANCE"
         internal const val WRITE_ELEVATION_GAINED = PERMISSION_PREFIX + "WRITE_ELEVATION_GAINED"
         internal const val WRITE_EXERCISE = PERMISSION_PREFIX + "WRITE_EXERCISE"
-        internal const val WRITE_EXERCISE_ROUTE = PERMISSION_PREFIX + "WRITE_EXERCISE_ROUTE"
         internal const val WRITE_FLOORS_CLIMBED = PERMISSION_PREFIX + "WRITE_FLOORS_CLIMBED"
         internal const val WRITE_STEPS = PERMISSION_PREFIX + "WRITE_STEPS"
         internal const val WRITE_TOTAL_CALORIES_BURNED =
@@ -212,9 +229,11 @@
         internal const val WRITE_BODY_WATER_MASS = PERMISSION_PREFIX + "WRITE_BODY_WATER_MASS"
         internal const val WRITE_BONE_MASS = PERMISSION_PREFIX + "WRITE_BONE_MASS"
         internal const val WRITE_HEIGHT = PERMISSION_PREFIX + "WRITE_HEIGHT"
+
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         internal const val WRITE_HIP_CIRCUMFERENCE = PERMISSION_PREFIX + "WRITE_HIP_CIRCUMFERENCE"
         internal const val WRITE_LEAN_BODY_MASS = PERMISSION_PREFIX + "WRITE_LEAN_BODY_MASS"
+
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         internal const val WRITE_WAIST_CIRCUMFERENCE =
             PERMISSION_PREFIX + "WRITE_WAIST_CIRCUMFERENCE"
@@ -222,6 +241,7 @@
 
         // Write permissions for CYCLE_TRACKING.
         internal const val WRITE_CERVICAL_MUCUS = PERMISSION_PREFIX + "WRITE_CERVICAL_MUCUS"
+
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         internal const val WRITE_INTERMENSTRUAL_BLEEDING =
             PERMISSION_PREFIX + "WRITE_INTERMENSTRUAL_BLEEDING"
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/RequestExerciseRouteInternal.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/RequestExerciseRouteInternal.kt
index 1fdba9c..b96640c 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/RequestExerciseRouteInternal.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/RequestExerciseRouteInternal.kt
@@ -32,8 +32,7 @@
  * @see androidx.activity.ComponentActivity.registerForActivityResult
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY)
-internal class RequestExerciseRouteInternal :
-    ActivityResultContract<String, ExerciseRoute.Data?>() {
+internal class RequestExerciseRouteInternal : ActivityResultContract<String, ExerciseRoute?>() {
     override fun createIntent(context: Context, input: String): Intent {
         require(input.isNotEmpty()) { "Session identifier can't be empty" }
         return Intent(HealthDataServiceConstants.ACTION_REQUEST_ROUTE).apply {
@@ -42,7 +41,7 @@
     }
 
     @Suppress("DEPRECATION") // getParcelableExtra
-    override fun parseResult(resultCode: Int, intent: Intent?): ExerciseRoute.Data? {
+    override fun parseResult(resultCode: Int, intent: Intent?): ExerciseRoute? {
         val route =
             intent?.getParcelableExtra<androidx.health.platform.client.exerciseroute.ExerciseRoute>(
                 HealthDataServiceConstants.EXTRA_EXERCISE_ROUTE
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/platform/RequestExerciseRouteUpsideDownCake.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/platform/RequestExerciseRouteUpsideDownCake.kt
index f1a435a..c5ec226 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/platform/RequestExerciseRouteUpsideDownCake.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/permission/platform/RequestExerciseRouteUpsideDownCake.kt
@@ -24,7 +24,7 @@
 import androidx.annotation.RestrictTo
 import androidx.health.connect.client.HealthConnectClient
 import androidx.health.connect.client.impl.platform.records.PlatformExerciseRoute
-import androidx.health.connect.client.impl.platform.records.toSdkExerciseRouteData
+import androidx.health.connect.client.impl.platform.records.toSdkExerciseRoute
 import androidx.health.connect.client.records.ExerciseRoute
 import androidx.health.platform.client.impl.logger.Logger
 
@@ -36,7 +36,7 @@
 @RequiresApi(34)
 @RestrictTo(RestrictTo.Scope.LIBRARY)
 internal class RequestExerciseRouteUpsideDownCake :
-    ActivityResultContract<String, ExerciseRoute.Data?>() {
+    ActivityResultContract<String, ExerciseRoute?>() {
     override fun createIntent(context: Context, input: String): Intent {
         require(input.isNotEmpty()) { "Session identifier can't be empty" }
         return Intent(HealthConnectManager.ACTION_REQUEST_EXERCISE_ROUTE).apply {
@@ -44,7 +44,7 @@
         }
     }
 
-    override fun parseResult(resultCode: Int, intent: Intent?): ExerciseRoute.Data? {
+    override fun parseResult(resultCode: Int, intent: Intent?): ExerciseRoute? {
         val route =
             intent?.getParcelableExtra(
                 HealthConnectManager.EXTRA_EXERCISE_ROUTE,
@@ -55,6 +55,6 @@
             return null
         }
         Logger.debug(HealthConnectClient.HEALTH_CONNECT_CLIENT_TAG, "Returned a route.")
-        return route.toSdkExerciseRouteData()
+        return route.toSdkExerciseRoute()
     }
 }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRoute.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRoute.kt
index 6f36983..679eddb 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRoute.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRoute.kt
@@ -19,63 +19,37 @@
 import androidx.health.connect.client.units.Length
 import java.time.Instant
 
-/** Captures a route associated with an exercise session a user does. */
-abstract class ExerciseRoute internal constructor() {
-
-    /**
-     * Class containing data of an exercise route.
-     *
-     * Contains a sequence of location points, with timestamps, which do not have to be in order.
-     *
-     * Location points contain a timestamp, longitude, latitude, and optionally altitude, horizontal
-     * and vertical accuracy.
-     */
-    class Data constructor(val route: List<Location>) : ExerciseRoute() {
-        init {
-            val sortedRoute: List<Location> = route.sortedBy { it.time }
-            for (i in 0 until sortedRoute.lastIndex) {
-                require(sortedRoute[i].time.isBefore(sortedRoute[i + 1].time))
-            }
-        }
-
-        internal fun isWithin(startTime: Instant, endTime: Instant): Boolean {
-            val minTime = route.minBy { it.time }.time
-            val maxTime = route.maxBy { it.time }.time
-            return !minTime.isBefore(startTime) && maxTime.isBefore(endTime)
-        }
-
-        override fun equals(other: Any?): Boolean {
-            if (this === other) return true
-            if (other !is Data) return false
-
-            return route == other.route
-        }
-
-        override fun hashCode(): Int {
-            return route.hashCode()
+/**
+ * Captures a route associated with an exercise session a user does.
+ *
+ * Contains a sequence of location points, with timestamps, which do not have to be in order.
+ *
+ * Location points contain a timestamp, longitude, latitude, and optionally altitude, horizontal and
+ * vertical accuracy.
+ */
+class ExerciseRoute constructor(val route: List<Location>) {
+    init {
+        val sortedRoute: List<Location> = route.sortedBy { it.time }
+        for (i in 0 until sortedRoute.lastIndex) {
+            require(sortedRoute[i].time.isBefore(sortedRoute[i + 1].time))
         }
     }
 
-    /** Class indicating that a permission hasn't been granted and a value couldn't be returned. */
-    class ConsentRequired : ExerciseRoute() {
-        override fun equals(other: Any?): Boolean {
-            return other is ConsentRequired
-        }
-
-        override fun hashCode(): Int {
-            return 0
-        }
+    internal fun isWithin(startTime: Instant, endTime: Instant): Boolean {
+        val minTime = route.minBy { it.time }.time
+        val maxTime = route.maxBy { it.time }.time
+        return !minTime.isBefore(startTime) && maxTime.isBefore(endTime)
     }
 
-    /** Class indicating that there's no data to request permissions for. */
-    class NoData : ExerciseRoute() {
-        override fun equals(other: Any?): Boolean {
-            return other is NoData
-        }
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is ExerciseRoute) return false
 
-        override fun hashCode(): Int {
-            return 0
-        }
+        return route == other.route
+    }
+
+    override fun hashCode(): Int {
+        return route.hashCode()
     }
 
     /**
@@ -88,7 +62,7 @@
      * @param horizontalAccuracy in [Length] unit. Optional field. Valid range: non-negative
      *   numbers.
      * @param verticalAccuracy in [Length] unit. Optional field. Valid range: non-negative numbers.
-     * @see ExerciseRoute
+     * @see ExerciseRouteResult
      */
     class Location(
         val time: Instant,
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRouteResult.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRouteResult.kt
new file mode 100644
index 0000000..3978f5c
--- /dev/null
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseRouteResult.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.connect.client.records
+
+/** Result of the route associated with an exercise session a user does. */
+abstract class ExerciseRouteResult internal constructor() {
+
+    /** Class containing data for an [ExerciseRoute]. */
+    class Data(val exerciseRoute: ExerciseRoute) : ExerciseRouteResult() {
+
+        override fun equals(other: Any?): Boolean {
+            if (other !is Data) {
+                return false
+            }
+            return exerciseRoute == other.exerciseRoute
+        }
+
+        override fun hashCode(): Int {
+            return 0
+        }
+    }
+
+    /** Class indicating that a permission hasn't been granted and a value couldn't be returned. */
+    class ConsentRequired : ExerciseRouteResult() {
+        override fun equals(other: Any?): Boolean {
+            return other is ConsentRequired
+        }
+
+        override fun hashCode(): Int {
+            return 0
+        }
+    }
+
+    /** Class indicating that there's no data to request permissions for. */
+    class NoData : ExerciseRouteResult() {
+        override fun equals(other: Any?): Boolean {
+            return other is NoData
+        }
+
+        override fun hashCode(): Int {
+            return 0
+        }
+    }
+}
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseSessionRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseSessionRecord.kt
index 1a3747c..8a33653 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseSessionRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/ExerciseSessionRecord.kt
@@ -59,8 +59,8 @@
      */
     val laps: List<ExerciseLap> = emptyList(),
 
-    /** [ExerciseRoute] [ExerciseRoute] of the session. */
-    val exerciseRoute: ExerciseRoute = ExerciseRoute.NoData(),
+    /** [ExerciseRouteResult] [ExerciseRouteResult] of the session. */
+    val exerciseRouteResult: ExerciseRouteResult = ExerciseRouteResult.NoData(),
 ) : IntervalRecord {
 
     @JvmOverloads
@@ -78,7 +78,7 @@
         metadata: Metadata = Metadata.EMPTY,
         segments: List<ExerciseSegment> = emptyList(),
         laps: List<ExerciseLap> = emptyList(),
-        exerciseRouteData: ExerciseRoute.Data? = null,
+        exerciseRoute: ExerciseRoute? = null,
     ) : this(
         startTime,
         startZoneOffset,
@@ -90,7 +90,7 @@
         metadata,
         segments,
         laps,
-        exerciseRouteData ?: ExerciseRoute.NoData()
+        exerciseRoute?.let { ExerciseRouteResult.Data(it) } ?: ExerciseRouteResult.NoData()
     )
 
     init {
@@ -130,8 +130,8 @@
                 "laps can not be out of parent time range."
             }
         }
-        if (exerciseRoute is ExerciseRoute.Data) {
-            require(exerciseRoute.isWithin(startTime, endTime)) {
+        if (exerciseRouteResult is ExerciseRouteResult.Data) {
+            require(exerciseRouteResult.exerciseRoute.isWithin(startTime, endTime)) {
                 "route can not be out of parent time range."
             }
         }
@@ -151,7 +151,7 @@
         if (metadata != other.metadata) return false
         if (segments != other.segments) return false
         if (laps != other.laps) return false
-        if (exerciseRoute != other.exerciseRoute) return false
+        if (exerciseRouteResult != other.exerciseRouteResult) return false
 
         return true
     }
@@ -164,7 +164,7 @@
         result = 31 * result + endTime.hashCode()
         result = 31 * result + (endZoneOffset?.hashCode() ?: 0)
         result = 31 * result + metadata.hashCode()
-        result = 31 * result + exerciseRoute.hashCode()
+        result = 31 * result + exerciseRouteResult.hashCode()
         return result
     }
 
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/MealType.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/MealType.kt
index 3a6a40f..6849832 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/MealType.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/MealType.kt
@@ -49,6 +49,7 @@
     @JvmField
     val MEAL_TYPE_STRING_TO_INT_MAP: Map<String, Int> =
         mapOf(
+            UNKNOWN to MEAL_TYPE_UNKNOWN,
             BREAKFAST to MEAL_TYPE_BREAKFAST,
             LUNCH to MEAL_TYPE_LUNCH,
             DINNER to MEAL_TYPE_DINNER,
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/Vo2MaxRecord.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/Vo2MaxRecord.kt
index 0a6f4df..87a60cf 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/Vo2MaxRecord.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/records/Vo2MaxRecord.kt
@@ -81,6 +81,7 @@
         @JvmField
         val MEASUREMENT_METHOD_STRING_TO_INT_MAP: Map<String, Int> =
             mapOf(
+                MeasurementMethod.OTHER to MEASUREMENT_METHOD_OTHER,
                 MeasurementMethod.METABOLIC_CART to MEASUREMENT_METHOD_METABOLIC_CART,
                 MeasurementMethod.HEART_RATE_RATIO to MEASUREMENT_METHOD_HEART_RATE_RATIO,
                 MeasurementMethod.COOPER_TEST to MEASUREMENT_METHOD_COOPER_TEST,
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/impl/HealthConnectClientImplTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/impl/HealthConnectClientImplTest.kt
index 48a531a..bc640cd 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/impl/HealthConnectClientImplTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/impl/HealthConnectClientImplTest.kt
@@ -24,10 +24,12 @@
 import android.os.Looper
 import androidx.health.connect.client.changes.DeletionChange
 import androidx.health.connect.client.changes.UpsertionChange
+import androidx.health.connect.client.permission.HealthPermission
 import androidx.health.connect.client.permission.HealthPermission.Companion.getReadPermission
 import androidx.health.connect.client.permission.HealthPermission.Companion.getWritePermission
 import androidx.health.connect.client.records.ActiveCaloriesBurnedRecord
 import androidx.health.connect.client.records.HeartRateRecord
+import androidx.health.connect.client.records.MealType
 import androidx.health.connect.client.records.NutritionRecord
 import androidx.health.connect.client.records.StepsRecord
 import androidx.health.connect.client.records.StepsRecord.Companion.COUNT_TOTAL
@@ -211,6 +213,20 @@
     }
 
     @Test
+    fun getGrantedPermissions_exerciseRoute() = runTest {
+        fakeAhpServiceStub.addGrantedPermission(
+            androidx.health.platform.client.permission.Permission(
+                PermissionProto.Permission.newBuilder()
+                    .setPermission(HealthPermission.PERMISSION_WRITE_EXERCISE_ROUTE)
+                    .build()
+            )
+        )
+        val response = testBlocking { healthConnectClient.getGrantedPermissions() }
+
+        assertThat(response).containsExactly(HealthPermission.PERMISSION_WRITE_EXERCISE_ROUTE)
+    }
+
+    @Test
     fun insertRecords_steps() = runTest {
         fakeAhpServiceStub.insertDataResponse = InsertDataResponse(listOf("0"))
         val response = testBlocking {
@@ -296,6 +312,10 @@
                     .setEndTimeMillis(5678L)
                     .putValues("vitaminC", DataProto.Value.newBuilder().setDoubleVal(20.0).build())
                     .putValues("vitaminE", DataProto.Value.newBuilder().setDoubleVal(10.0).build())
+                    .putValues(
+                        "mealType",
+                        DataProto.Value.newBuilder().setEnumVal(MealType.UNKNOWN).build()
+                    )
                     .setDataType(DataProto.DataType.newBuilder().setName("Nutrition"))
                     .build()
             )
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/impl/converters/records/AllRecordsConverterTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/impl/converters/records/AllRecordsConverterTest.kt
index ac86a98..2bd6f9b 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/impl/converters/records/AllRecordsConverterTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/impl/converters/records/AllRecordsConverterTest.kt
@@ -643,7 +643,7 @@
                         )
                     ),
                 exerciseRoute =
-                    ExerciseRoute.Data(
+                    ExerciseRoute(
                         route =
                             listOf(
                                 ExerciseRoute.Location(
@@ -676,7 +676,7 @@
                 startZoneOffset = null,
                 endTime = END_TIME,
                 endZoneOffset = null,
-                exerciseRouteData = null,
+                exerciseRoute = null,
             )
 
         checkProtoAndRecordTypeNameMatch(data)
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/permission/RequestExerciseRouteInternalTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/permission/RequestExerciseRouteInternalTest.kt
index ffa47f9..b650485 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/permission/RequestExerciseRouteInternalTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/permission/RequestExerciseRouteInternalTest.kt
@@ -73,7 +73,7 @@
             )
         )
         val result = requestRouteContract.parseResult(0, intent)
-        assertThat(result).isEqualTo(ExerciseRoute.Data(listOf()))
+        assertThat(result).isEqualTo(ExerciseRoute(listOf()))
     }
 
     @Test
@@ -114,7 +114,7 @@
         val result = requestRouteContract.parseResult(0, intent)
         assertThat(result)
             .isEqualTo(
-                ExerciseRoute.Data(
+                ExerciseRoute(
                     listOf(
                         ExerciseRoute.Location(
                             time = Instant.ofEpochMilli(1234L),
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/BloodGlucoseRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/BloodGlucoseRecordTest.kt
index 590b879..3e9cb7e 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/BloodGlucoseRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/BloodGlucoseRecordTest.kt
@@ -27,7 +27,7 @@
     @Test
     fun relationToMealEnums_existInMapping() {
         val allEnums =
-            BloodGlucoseRecord.Companion::class.allIntDefEnumsWithPrefix("RELATION_TO_MEAL")
+            getAllIntDefEnums<BloodGlucoseRecord>("""RELATION_TO_MEAL.*(?<!UNKNOWN)$""")
 
         assertThat(BloodGlucoseRecord.RELATION_TO_MEAL_STRING_TO_INT_MAP.values)
             .containsExactlyElementsIn(allEnums)
@@ -37,8 +37,7 @@
 
     @Test
     fun specimenSourceEnums_existInMapping() {
-        val allEnums =
-            BloodGlucoseRecord.Companion::class.allIntDefEnumsWithPrefix("SPECIMEN_SOURCE")
+        val allEnums = getAllIntDefEnums<BloodGlucoseRecord>("""SPECIMEN_SOURCE.*(?<!UNKNOWN)$""")
 
         assertThat(BloodGlucoseRecord.SPECIMEN_SOURCE_STRING_TO_INT_MAP.values)
             .containsExactlyElementsIn(allEnums)
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/BloodPressureRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/BloodPressureRecordTest.kt
index 63539bd1..c4a846f 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/BloodPressureRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/BloodPressureRecordTest.kt
@@ -25,8 +25,7 @@
 class BloodPressureRecordTest {
     @Test
     fun bodyPositionEnums_existInMapping() {
-        val allEnums =
-            BloodPressureRecord.Companion::class.allIntDefEnumsWithPrefix("BODY_POSITION")
+        val allEnums = getAllIntDefEnums<BloodPressureRecord>("""BODY_POSITION.*(?<!UNKNOWN)$""")
 
         assertThat(BloodPressureRecord.BODY_POSITION_STRING_TO_INT_MAP.values)
             .containsExactlyElementsIn(allEnums)
@@ -37,7 +36,9 @@
     @Test
     fun measurementLocationEnums_existInMapping() {
         val allEnums =
-            BloodPressureRecord.Companion::class.allIntDefEnumsWithPrefix("MEASUREMENT_LOCATION")
+            getAllIntDefEnums<BloodPressureRecord>(
+                """MEASUREMENT_LOCATION.*(?<!UNKNOWN)$"""
+            )
 
         assertThat(BloodPressureRecord.MEASUREMENT_LOCATION_STRING_TO_INT_MAP.values)
             .containsExactlyElementsIn(allEnums)
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/BodyTemperatureMeasurementLocationTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/BodyTemperatureMeasurementLocationTest.kt
index 0a805f4..b70bd0e 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/BodyTemperatureMeasurementLocationTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/BodyTemperatureMeasurementLocationTest.kt
@@ -27,8 +27,8 @@
     @Test
     fun enums_existInMapping() {
         val allEnums =
-            BodyTemperatureMeasurementLocation::class.allObjectIntDefEnumsWithPrefix(
-                "MEASUREMENT_LOCATION"
+            getAllIntDefEnums<BodyTemperatureMeasurementLocation>(
+                """MEASUREMENT_LOCATION.*(?<!UNKNOWN)$"""
             )
 
         assertThat(BodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_STRING_TO_INT_MAP.values)
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/CervicalMucusRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/CervicalMucusRecordTest.kt
index ad92237..a9620fbd 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/CervicalMucusRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/CervicalMucusRecordTest.kt
@@ -26,7 +26,7 @@
 
     @Test
     fun appearanceEnums_existInMapping() {
-        val allEnums = CervicalMucusRecord.Companion::class.allIntDefEnumsWithPrefix("APPEARANCE")
+        val allEnums = getAllIntDefEnums<CervicalMucusRecord>("""APPEARANCE.*(?<!UNKNOWN)$""")
 
         assertThat(CervicalMucusRecord.APPEARANCE_STRING_TO_INT_MAP.values)
             .containsExactlyElementsIn(allEnums)
@@ -36,7 +36,7 @@
 
     @Test
     fun sensationEnums_existInMapping() {
-        val allEnums = CervicalMucusRecord.Companion::class.allIntDefEnumsWithPrefix("SENSATION")
+        val allEnums = getAllIntDefEnums<CervicalMucusRecord>("""SENSATION.*(?<!UNKNOWN)$""")
 
         assertThat(CervicalMucusRecord.SENSATION_STRING_TO_INT_MAP.values)
             .containsExactlyElementsIn(allEnums)
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/EnumTestUtils.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/EnumTestUtils.kt
index ddaf765..ab5218e 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/EnumTestUtils.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/EnumTestUtils.kt
@@ -16,25 +16,14 @@
 
 package androidx.health.connect.client.records
 
-import kotlin.reflect.KClass
-import kotlin.reflect.typeOf
+internal inline fun <reified T> getAllIntDefEnums(pattern: String): Collection<Int> {
+    val regex = pattern.toRegex()
 
-internal fun KClass<*>.allIntDefEnumsWithPrefix(prefix: String): Collection<Int> {
-    return members
+    return T::class.java.fields
         .asSequence()
-        .filter { it.name.startsWith(prefix) && !it.name.endsWith("UNKNOWN") }
-        .filter { it.returnType == typeOf<Int>() }
-        .map { it.call(null) }
-        .filterIsInstance<Int>()
-        .toHashSet()
-}
-
-internal fun KClass<*>.allObjectIntDefEnumsWithPrefix(prefix: String): Collection<Int> {
-    return members
-        .asSequence()
-        .filter { it.name.startsWith(prefix) && !it.name.endsWith("UNKNOWN") }
-        .filter { it.returnType == typeOf<Int>() }
-        .map { it.call() }
+        .filter { it.name.matches(regex) }
+        .filter { it.type == Int::class.javaPrimitiveType }
+        .map { it.get(null) }
         .filterIsInstance<Int>()
         .toHashSet()
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseRouteTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseRouteTest.kt
index 46afdf5..35275bf 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseRouteTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseRouteTest.kt
@@ -74,7 +74,7 @@
 
     @Test
     fun emptyRoute() {
-        assertThat(ExerciseRoute.Data(listOf())).isEqualTo(ExerciseRoute.Data(listOf()))
+        assertThat(ExerciseRoute(listOf())).isEqualTo(ExerciseRoute(listOf()))
     }
 
     @Test
@@ -94,8 +94,8 @@
                 latitude = 34.8,
                 longitude = -34.8,
             )
-        assertThat(ExerciseRoute.Data(listOf(location1, location2)))
-            .isEqualTo(ExerciseRoute.Data(listOf(location1, location2)))
+        assertThat(ExerciseRoute(listOf(location1, location2)))
+            .isEqualTo(ExerciseRoute(listOf(location1, location2)))
     }
 
     @Test
@@ -112,8 +112,6 @@
                 latitude = 34.8,
                 longitude = -34.8,
             )
-        assertFailsWith<IllegalArgumentException> {
-            ExerciseRoute.Data(listOf(location1, location2))
-        }
+        assertFailsWith<IllegalArgumentException> { ExerciseRoute(listOf(location1, location2)) }
     }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseSessionRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseSessionRecordTest.kt
index 6adb8bb..73af184 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseSessionRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/ExerciseSessionRecordTest.kt
@@ -62,7 +62,7 @@
                             )
                         ),
                     exerciseRoute =
-                        ExerciseRoute.Data(
+                        ExerciseRoute(
                             route =
                                 listOf(
                                     ExerciseRoute.Location(
@@ -103,7 +103,7 @@
                             )
                         ),
                     exerciseRoute =
-                        ExerciseRoute.Data(
+                        ExerciseRoute(
                             route =
                                 listOf(
                                     ExerciseRoute.Location(
@@ -131,7 +131,7 @@
                 exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_EXERCISE_CLASS,
                 title = "title",
                 notes = "notes",
-                exerciseRouteData = null,
+                exerciseRoute = null,
             )
         }
     }
@@ -181,7 +181,7 @@
                             segmentType = ExerciseSegment.EXERCISE_SEGMENT_TYPE_BIKING
                         )
                     ),
-                exerciseRouteData = null,
+                exerciseRoute = null,
             )
         }
 
@@ -200,7 +200,7 @@
                             segmentType = ExerciseSegment.EXERCISE_SEGMENT_TYPE_BIKING
                         )
                     ),
-                exerciseRouteData = null,
+                exerciseRoute = null,
             )
         }
     }
@@ -221,7 +221,7 @@
                             endTime = Instant.ofEpochMilli(1235L),
                         )
                     ),
-                exerciseRouteData = null,
+                exerciseRoute = null,
             )
         }
 
@@ -239,7 +239,7 @@
                             endTime = Instant.ofEpochMilli(1236L),
                         )
                     ),
-                exerciseRouteData = null,
+                exerciseRoute = null,
             )
         }
     }
@@ -254,7 +254,7 @@
                 endZoneOffset = null,
                 exerciseType = EXERCISE_TYPE_BIKING,
                 exerciseRoute =
-                    ExerciseRoute.Data(
+                    ExerciseRoute(
                         route =
                             listOf(
                                 ExerciseRoute.Location(
@@ -275,7 +275,7 @@
                 endZoneOffset = null,
                 exerciseType = EXERCISE_TYPE_BIKING,
                 exerciseRoute =
-                    ExerciseRoute.Data(
+                    ExerciseRoute(
                         route =
                             listOf(
                                 ExerciseRoute.Location(
@@ -311,7 +311,7 @@
                             segmentType = ExerciseSegment.EXERCISE_SEGMENT_TYPE_BIKING
                         ),
                     ),
-                exerciseRouteData = null,
+                exerciseRoute = null,
             )
         }
     }
@@ -336,7 +336,7 @@
                             endTime = Instant.ofEpochMilli(1236L),
                         ),
                     ),
-                exerciseRouteData = null,
+                exerciseRoute = null,
             )
         }
     }
@@ -358,7 +358,7 @@
                             segmentType = ExerciseSegment.EXERCISE_SEGMENT_TYPE_PLANK
                         ),
                     ),
-                exerciseRouteData = null,
+                exerciseRoute = null,
             )
         }
     }
@@ -373,7 +373,7 @@
                         endZoneOffset = null,
                         exerciseType = EXERCISE_TYPE_BIKING,
                         exerciseRoute =
-                            ExerciseRoute.Data(
+                            ExerciseRoute(
                                 route =
                                     listOf(
                                         ExerciseRoute.Location(
@@ -384,18 +384,20 @@
                                     )
                             ),
                     )
-                    .exerciseRoute
+                    .exerciseRouteResult
             )
             .isEqualTo(
-                ExerciseRoute.Data(
-                    route =
-                        listOf(
-                            ExerciseRoute.Location(
-                                time = Instant.ofEpochMilli(1235L),
-                                latitude = 34.5,
-                                longitude = -34.5
+                ExerciseRouteResult.Data(
+                    ExerciseRoute(
+                        route =
+                            listOf(
+                                ExerciseRoute.Location(
+                                    time = Instant.ofEpochMilli(1235L),
+                                    latitude = 34.5,
+                                    longitude = -34.5
+                                )
                             )
-                        )
+                    )
                 )
             )
         assertThat(
@@ -405,10 +407,10 @@
                         endTime = Instant.ofEpochMilli(1236L),
                         endZoneOffset = null,
                         exerciseType = EXERCISE_TYPE_BIKING,
-                        exerciseRouteData = null
+                        exerciseRoute = null
                     )
-                    .exerciseRoute
+                    .exerciseRouteResult
             )
-            .isEqualTo(ExerciseRoute.NoData())
+            .isEqualTo(ExerciseRouteResult.NoData())
     }
 }
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/MealTypeTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/MealTypeTest.kt
index de588ab..3a9554c 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/MealTypeTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/MealTypeTest.kt
@@ -26,7 +26,7 @@
 
     @Test
     fun enums_existInMapping() {
-        val allEnums = MealType::class.allObjectIntDefEnumsWithPrefix("MEAL_TYPE")
+        val allEnums = getAllIntDefEnums<MealType>("""MEAL_TYPE.*""")
 
         assertThat(MealType.MEAL_TYPE_STRING_TO_INT_MAP.values).containsExactlyElementsIn(allEnums)
         assertThat(MealType.MEAL_TYPE_INT_TO_STRING_MAP.keys).containsExactlyElementsIn(allEnums)
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/OvulationTestRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/OvulationTestRecordTest.kt
index d3f74d9..7e64ee6 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/OvulationTestRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/OvulationTestRecordTest.kt
@@ -26,7 +26,7 @@
 
     @Test
     fun resultEnums_existInMapping() {
-        val allEnums = OvulationTestRecord.Companion::class.allIntDefEnumsWithPrefix("RESULT")
+        val allEnums = getAllIntDefEnums<OvulationTestRecord>("""RESULT.*(?<!UNKNOWN)$""")
 
         assertThat(OvulationTestRecord.RESULT_STRING_TO_INT_MAP.values)
             .containsExactlyElementsIn(allEnums)
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/SexualActivityRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/SexualActivityRecordTest.kt
index e829d17..78f9bd6 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/SexualActivityRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/SexualActivityRecordTest.kt
@@ -26,8 +26,7 @@
 
     @Test
     fun protectionEnums_existInMapping() {
-        val allEnums =
-            SexualActivityRecord.Companion::class.allIntDefEnumsWithPrefix("PROTECTION_USED")
+        val allEnums = getAllIntDefEnums<SexualActivityRecord>("""PROTECTION_USED.*(?<!UNKNOWN)$""")
 
         Truth.assertThat(SexualActivityRecord.PROTECTION_USED_STRING_TO_INT_MAP.values)
             .containsExactlyElementsIn(allEnums)
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/Vo2MaxRecordTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/Vo2MaxRecordTest.kt
index a414d8c..3f531bd 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/Vo2MaxRecordTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/records/Vo2MaxRecordTest.kt
@@ -25,10 +25,7 @@
 class Vo2MaxRecordTest {
     @Test
     fun measurementMethodEnums_existMapping() {
-        val allEnums =
-            Vo2MaxRecord.Companion::class.allIntDefEnumsWithPrefix("MEASUREMENT_METHOD").filter {
-                it != Vo2MaxRecord.MEASUREMENT_METHOD_OTHER
-            }
+        val allEnums = getAllIntDefEnums<Vo2MaxRecord>("""MEASUREMENT_METHOD.*""")
 
         Truth.assertThat(Vo2MaxRecord.MEASUREMENT_METHOD_STRING_TO_INT_MAP.values)
             .containsExactlyElementsIn(allEnums)
diff --git a/health/health-services-client/api/current.txt b/health/health-services-client/api/current.txt
index 92e1fab..aa45592 100644
--- a/health/health-services-client/api/current.txt
+++ b/health/health-services-client/api/current.txt
@@ -41,6 +41,7 @@
 
   public interface ExerciseUpdateCallback {
     method public void onAvailabilityChanged(androidx.health.services.client.data.DataType<?,?> dataType, androidx.health.services.client.data.Availability availability);
+    method public default void onExerciseEventReceived(androidx.health.services.client.data.ExerciseEvent event);
     method public void onExerciseUpdateReceived(androidx.health.services.client.data.ExerciseUpdate update);
     method public void onLapSummaryReceived(androidx.health.services.client.data.ExerciseLapSummary lapSummary);
     method public void onRegistered();
@@ -331,12 +332,17 @@
   }
 
   public final class ExerciseConfig {
+    ctor public ExerciseConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes, boolean isAutoPauseAndResumeEnabled, boolean isGpsEnabled);
+    ctor public ExerciseConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes, boolean isAutoPauseAndResumeEnabled, boolean isGpsEnabled, optional java.util.List<? extends androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals);
+    ctor public ExerciseConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes, boolean isAutoPauseAndResumeEnabled, boolean isGpsEnabled, optional java.util.List<? extends androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals, optional android.os.Bundle exerciseParams);
     ctor public ExerciseConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes, boolean isAutoPauseAndResumeEnabled, boolean isGpsEnabled, optional java.util.List<? extends androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals, optional android.os.Bundle exerciseParams, optional @FloatRange(from=0.0) float swimmingPoolLengthMeters);
     ctor public ExerciseConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes, boolean isAutoPauseAndResumeEnabled, boolean isGpsEnabled, optional java.util.List<? extends androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals, optional android.os.Bundle exerciseParams, optional @FloatRange(from=0.0) float swimmingPoolLengthMeters, optional androidx.health.services.client.data.ExerciseTypeConfig? exerciseTypeConfig);
     ctor public ExerciseConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes, boolean isAutoPauseAndResumeEnabled, boolean isGpsEnabled, optional java.util.List<? extends androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals, optional android.os.Bundle exerciseParams, optional @FloatRange(from=0.0) float swimmingPoolLengthMeters, optional androidx.health.services.client.data.ExerciseTypeConfig? exerciseTypeConfig, optional java.util.Set<androidx.health.services.client.data.BatchingMode> batchingModeOverrides);
+    ctor public ExerciseConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes, boolean isAutoPauseAndResumeEnabled, boolean isGpsEnabled, optional java.util.List<? extends androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals, optional android.os.Bundle exerciseParams, optional @FloatRange(from=0.0) float swimmingPoolLengthMeters, optional androidx.health.services.client.data.ExerciseTypeConfig? exerciseTypeConfig, optional java.util.Set<androidx.health.services.client.data.BatchingMode> batchingModeOverrides, optional java.util.Set<? extends androidx.health.services.client.data.ExerciseEventType<?>> exerciseEventTypes);
     method public static androidx.health.services.client.data.ExerciseConfig.Builder builder(androidx.health.services.client.data.ExerciseType exerciseType);
     method public java.util.Set<androidx.health.services.client.data.BatchingMode> getBatchingModeOverrides();
     method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getDataTypes();
+    method public java.util.Set<androidx.health.services.client.data.ExerciseEventType<?>> getExerciseEventTypes();
     method public java.util.List<androidx.health.services.client.data.ExerciseGoal<?>> getExerciseGoals();
     method public android.os.Bundle getExerciseParams();
     method public androidx.health.services.client.data.ExerciseType getExerciseType();
@@ -346,6 +352,7 @@
     method public boolean isGpsEnabled();
     property public final java.util.Set<androidx.health.services.client.data.BatchingMode> batchingModeOverrides;
     property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> dataTypes;
+    property public final java.util.Set<androidx.health.services.client.data.ExerciseEventType<?>> exerciseEventTypes;
     property public final java.util.List<androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals;
     property public final android.os.Bundle exerciseParams;
     property public final androidx.health.services.client.data.ExerciseType exerciseType;
@@ -362,6 +369,7 @@
     method public androidx.health.services.client.data.ExerciseConfig build();
     method public androidx.health.services.client.data.ExerciseConfig.Builder setBatchingModeOverrides(java.util.Set<androidx.health.services.client.data.BatchingMode> batchingModeOverrides);
     method public androidx.health.services.client.data.ExerciseConfig.Builder setDataTypes(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes);
+    method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseEventTypes(java.util.Set<? extends androidx.health.services.client.data.ExerciseEventType<?>> exerciseEventTypes);
     method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseGoals(java.util.List<? extends androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals);
     method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseParams(android.os.Bundle exerciseParams);
     method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseTypeConfig(androidx.health.services.client.data.ExerciseTypeConfig? exerciseTypeConfig);
@@ -374,6 +382,31 @@
     method public androidx.health.services.client.data.ExerciseConfig.Builder builder(androidx.health.services.client.data.ExerciseType exerciseType);
   }
 
+  public abstract class ExerciseEvent {
+    field public static final androidx.health.services.client.data.ExerciseEvent.Companion Companion;
+  }
+
+  public static final class ExerciseEvent.Companion {
+  }
+
+  public abstract class ExerciseEventCapabilities {
+    method public abstract boolean isSupported();
+    property public abstract boolean isSupported;
+    field public static final androidx.health.services.client.data.ExerciseEventCapabilities.Companion Companion;
+  }
+
+  public static final class ExerciseEventCapabilities.Companion {
+  }
+
+  public final class ExerciseEventType<C extends androidx.health.services.client.data.ExerciseEventCapabilities> {
+    field public static final androidx.health.services.client.data.ExerciseEventType.Companion Companion;
+    field public static final androidx.health.services.client.data.ExerciseEventType<androidx.health.services.client.data.GolfShotEventCapabilities> GOLF_SHOT_EVENT;
+    field public static final androidx.health.services.client.data.ExerciseEventType<androidx.health.services.client.data.ExerciseEventCapabilities> UNKNOWN;
+  }
+
+  public static final class ExerciseEventType.Companion {
+  }
+
   public final class ExerciseGoal<T extends java.lang.Number> implements android.os.Parcelable {
     method public static <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createMilestone(androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> condition, T period);
     method public static <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createMilestoneGoalWithUpdatedThreshold(androidx.health.services.client.data.ExerciseGoal<T> goal, T newThreshold);
@@ -577,11 +610,16 @@
 
   public final class ExerciseTypeCapabilities {
     ctor public ExerciseTypeCapabilities(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> supportedDataTypes, java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals, java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones, boolean supportsAutoPauseAndResume);
+    ctor public ExerciseTypeCapabilities(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> supportedDataTypes, java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals, java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones, boolean supportsAutoPauseAndResume, optional java.util.Set<? extends androidx.health.services.client.data.ExerciseEventType<?>> supportedExerciseEvents);
+    ctor public ExerciseTypeCapabilities(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> supportedDataTypes, java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals, java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones, boolean supportsAutoPauseAndResume, optional java.util.Set<? extends androidx.health.services.client.data.ExerciseEventType<?>> supportedExerciseEvents, optional java.util.Map<androidx.health.services.client.data.ExerciseEventType<?>,? extends androidx.health.services.client.data.ExerciseEventCapabilities> exerciseEventCapabilities);
+    method public <C extends androidx.health.services.client.data.ExerciseEventCapabilities> C? getExerciseEventCapabilityDetails(androidx.health.services.client.data.ExerciseEventType<C> exerciseEventType);
     method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getSupportedDataTypes();
+    method public java.util.Set<androidx.health.services.client.data.ExerciseEventType<?>> getSupportedExerciseEvents();
     method public java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> getSupportedGoals();
     method public java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> getSupportedMilestones();
     method public boolean getSupportsAutoPauseAndResume();
     property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> supportedDataTypes;
+    property public final java.util.Set<androidx.health.services.client.data.ExerciseEventType<?>> supportedExerciseEvents;
     property public final java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals;
     property public final java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones;
     property public final boolean supportsAutoPauseAndResume;
@@ -645,6 +683,33 @@
   public static final class GolfExerciseTypeConfig.GolfShotTrackingPlaceInfo.Companion {
   }
 
+  public final class GolfShotEvent extends androidx.health.services.client.data.ExerciseEvent {
+    ctor public GolfShotEvent(java.time.Duration durationSinceBoot, androidx.health.services.client.data.GolfShotEvent.GolfShotSwingType swingType);
+    method public java.time.Duration getDurationSinceBoot();
+    method public androidx.health.services.client.data.GolfShotEvent.GolfShotSwingType getSwingType();
+    property public final java.time.Duration durationSinceBoot;
+    property public final androidx.health.services.client.data.GolfShotEvent.GolfShotSwingType swingType;
+  }
+
+  public static final class GolfShotEvent.GolfShotSwingType {
+    field public static final androidx.health.services.client.data.GolfShotEvent.GolfShotSwingType.Companion Companion;
+    field public static final androidx.health.services.client.data.GolfShotEvent.GolfShotSwingType FULL;
+    field public static final androidx.health.services.client.data.GolfShotEvent.GolfShotSwingType PARTIAL;
+    field public static final androidx.health.services.client.data.GolfShotEvent.GolfShotSwingType PUTT;
+    field public static final androidx.health.services.client.data.GolfShotEvent.GolfShotSwingType UNKNOWN;
+  }
+
+  public static final class GolfShotEvent.GolfShotSwingType.Companion {
+  }
+
+  public final class GolfShotEventCapabilities extends androidx.health.services.client.data.ExerciseEventCapabilities {
+    ctor public GolfShotEventCapabilities(boolean isSupported, boolean isSwingTypeClassificationSupported);
+    method public boolean isSupported();
+    method public boolean isSwingTypeClassificationSupported();
+    property public boolean isSupported;
+    property public final boolean isSwingTypeClassificationSupported;
+  }
+
   public final class HealthEvent {
     ctor public HealthEvent(androidx.health.services.client.data.HealthEvent.Type type, java.time.Instant eventTime, androidx.health.services.client.data.DataPointContainer metrics);
     method public java.time.Instant getEventTime();
diff --git a/health/health-services-client/api/restricted_current.txt b/health/health-services-client/api/restricted_current.txt
index 92e1fab..a74becf 100644
--- a/health/health-services-client/api/restricted_current.txt
+++ b/health/health-services-client/api/restricted_current.txt
@@ -41,6 +41,7 @@
 
   public interface ExerciseUpdateCallback {
     method public void onAvailabilityChanged(androidx.health.services.client.data.DataType<?,?> dataType, androidx.health.services.client.data.Availability availability);
+    method public default void onExerciseEventReceived(androidx.health.services.client.data.ExerciseEvent event);
     method public void onExerciseUpdateReceived(androidx.health.services.client.data.ExerciseUpdate update);
     method public void onLapSummaryReceived(androidx.health.services.client.data.ExerciseLapSummary lapSummary);
     method public void onRegistered();
@@ -331,12 +332,17 @@
   }
 
   public final class ExerciseConfig {
+    ctor public ExerciseConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes, boolean isAutoPauseAndResumeEnabled, boolean isGpsEnabled);
+    ctor public ExerciseConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes, boolean isAutoPauseAndResumeEnabled, boolean isGpsEnabled, optional java.util.List<? extends androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals);
+    ctor public ExerciseConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes, boolean isAutoPauseAndResumeEnabled, boolean isGpsEnabled, optional java.util.List<? extends androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals, optional android.os.Bundle exerciseParams);
     ctor public ExerciseConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes, boolean isAutoPauseAndResumeEnabled, boolean isGpsEnabled, optional java.util.List<? extends androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals, optional android.os.Bundle exerciseParams, optional @FloatRange(from=0.0) float swimmingPoolLengthMeters);
     ctor public ExerciseConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes, boolean isAutoPauseAndResumeEnabled, boolean isGpsEnabled, optional java.util.List<? extends androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals, optional android.os.Bundle exerciseParams, optional @FloatRange(from=0.0) float swimmingPoolLengthMeters, optional androidx.health.services.client.data.ExerciseTypeConfig? exerciseTypeConfig);
     ctor public ExerciseConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes, boolean isAutoPauseAndResumeEnabled, boolean isGpsEnabled, optional java.util.List<? extends androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals, optional android.os.Bundle exerciseParams, optional @FloatRange(from=0.0) float swimmingPoolLengthMeters, optional androidx.health.services.client.data.ExerciseTypeConfig? exerciseTypeConfig, optional java.util.Set<androidx.health.services.client.data.BatchingMode> batchingModeOverrides);
+    ctor public ExerciseConfig(androidx.health.services.client.data.ExerciseType exerciseType, java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes, boolean isAutoPauseAndResumeEnabled, boolean isGpsEnabled, optional java.util.List<? extends androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals, optional android.os.Bundle exerciseParams, optional @FloatRange(from=0.0) float swimmingPoolLengthMeters, optional androidx.health.services.client.data.ExerciseTypeConfig? exerciseTypeConfig, optional java.util.Set<androidx.health.services.client.data.BatchingMode> batchingModeOverrides, optional java.util.Set<? extends androidx.health.services.client.data.ExerciseEventType<?>> exerciseEventTypes);
     method public static androidx.health.services.client.data.ExerciseConfig.Builder builder(androidx.health.services.client.data.ExerciseType exerciseType);
     method public java.util.Set<androidx.health.services.client.data.BatchingMode> getBatchingModeOverrides();
     method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getDataTypes();
+    method public java.util.Set<androidx.health.services.client.data.ExerciseEventType<?>> getExerciseEventTypes();
     method public java.util.List<androidx.health.services.client.data.ExerciseGoal<?>> getExerciseGoals();
     method public android.os.Bundle getExerciseParams();
     method public androidx.health.services.client.data.ExerciseType getExerciseType();
@@ -346,6 +352,7 @@
     method public boolean isGpsEnabled();
     property public final java.util.Set<androidx.health.services.client.data.BatchingMode> batchingModeOverrides;
     property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> dataTypes;
+    property public final java.util.Set<androidx.health.services.client.data.ExerciseEventType<?>> exerciseEventTypes;
     property public final java.util.List<androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals;
     property public final android.os.Bundle exerciseParams;
     property public final androidx.health.services.client.data.ExerciseType exerciseType;
@@ -362,6 +369,7 @@
     method public androidx.health.services.client.data.ExerciseConfig build();
     method public androidx.health.services.client.data.ExerciseConfig.Builder setBatchingModeOverrides(java.util.Set<androidx.health.services.client.data.BatchingMode> batchingModeOverrides);
     method public androidx.health.services.client.data.ExerciseConfig.Builder setDataTypes(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> dataTypes);
+    method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseEventTypes(java.util.Set<? extends androidx.health.services.client.data.ExerciseEventType<?>> exerciseEventTypes);
     method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseGoals(java.util.List<? extends androidx.health.services.client.data.ExerciseGoal<?>> exerciseGoals);
     method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseParams(android.os.Bundle exerciseParams);
     method public androidx.health.services.client.data.ExerciseConfig.Builder setExerciseTypeConfig(androidx.health.services.client.data.ExerciseTypeConfig? exerciseTypeConfig);
@@ -374,6 +382,33 @@
     method public androidx.health.services.client.data.ExerciseConfig.Builder builder(androidx.health.services.client.data.ExerciseType exerciseType);
   }
 
+  public abstract class ExerciseEvent {
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public ExerciseEvent();
+    field public static final androidx.health.services.client.data.ExerciseEvent.Companion Companion;
+  }
+
+  public static final class ExerciseEvent.Companion {
+  }
+
+  public abstract class ExerciseEventCapabilities {
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public ExerciseEventCapabilities();
+    method public abstract boolean isSupported();
+    property public abstract boolean isSupported;
+    field public static final androidx.health.services.client.data.ExerciseEventCapabilities.Companion Companion;
+  }
+
+  public static final class ExerciseEventCapabilities.Companion {
+  }
+
+  public final class ExerciseEventType<C extends androidx.health.services.client.data.ExerciseEventCapabilities> {
+    field public static final androidx.health.services.client.data.ExerciseEventType.Companion Companion;
+    field public static final androidx.health.services.client.data.ExerciseEventType<androidx.health.services.client.data.GolfShotEventCapabilities> GOLF_SHOT_EVENT;
+    field public static final androidx.health.services.client.data.ExerciseEventType<androidx.health.services.client.data.ExerciseEventCapabilities> UNKNOWN;
+  }
+
+  public static final class ExerciseEventType.Companion {
+  }
+
   public final class ExerciseGoal<T extends java.lang.Number> implements android.os.Parcelable {
     method public static <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createMilestone(androidx.health.services.client.data.DataTypeCondition<T,androidx.health.services.client.data.AggregateDataType<T,?>> condition, T period);
     method public static <T extends java.lang.Number> androidx.health.services.client.data.ExerciseGoal<T> createMilestoneGoalWithUpdatedThreshold(androidx.health.services.client.data.ExerciseGoal<T> goal, T newThreshold);
@@ -577,11 +612,16 @@
 
   public final class ExerciseTypeCapabilities {
     ctor public ExerciseTypeCapabilities(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> supportedDataTypes, java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals, java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones, boolean supportsAutoPauseAndResume);
+    ctor public ExerciseTypeCapabilities(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> supportedDataTypes, java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals, java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones, boolean supportsAutoPauseAndResume, optional java.util.Set<? extends androidx.health.services.client.data.ExerciseEventType<?>> supportedExerciseEvents);
+    ctor public ExerciseTypeCapabilities(java.util.Set<? extends androidx.health.services.client.data.DataType<?,?>> supportedDataTypes, java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals, java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,? extends java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones, boolean supportsAutoPauseAndResume, optional java.util.Set<? extends androidx.health.services.client.data.ExerciseEventType<?>> supportedExerciseEvents, optional java.util.Map<androidx.health.services.client.data.ExerciseEventType<?>,? extends androidx.health.services.client.data.ExerciseEventCapabilities> exerciseEventCapabilities);
+    method public <C extends androidx.health.services.client.data.ExerciseEventCapabilities> C? getExerciseEventCapabilityDetails(androidx.health.services.client.data.ExerciseEventType<C> exerciseEventType);
     method public java.util.Set<androidx.health.services.client.data.DataType<?,?>> getSupportedDataTypes();
+    method public java.util.Set<androidx.health.services.client.data.ExerciseEventType<?>> getSupportedExerciseEvents();
     method public java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> getSupportedGoals();
     method public java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> getSupportedMilestones();
     method public boolean getSupportsAutoPauseAndResume();
     property public final java.util.Set<androidx.health.services.client.data.DataType<?,?>> supportedDataTypes;
+    property public final java.util.Set<androidx.health.services.client.data.ExerciseEventType<?>> supportedExerciseEvents;
     property public final java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedGoals;
     property public final java.util.Map<androidx.health.services.client.data.AggregateDataType<?,?>,java.util.Set<androidx.health.services.client.data.ComparisonType>> supportedMilestones;
     property public final boolean supportsAutoPauseAndResume;
@@ -645,6 +685,33 @@
   public static final class GolfExerciseTypeConfig.GolfShotTrackingPlaceInfo.Companion {
   }
 
+  public final class GolfShotEvent extends androidx.health.services.client.data.ExerciseEvent {
+    ctor public GolfShotEvent(java.time.Duration durationSinceBoot, androidx.health.services.client.data.GolfShotEvent.GolfShotSwingType swingType);
+    method public java.time.Duration getDurationSinceBoot();
+    method public androidx.health.services.client.data.GolfShotEvent.GolfShotSwingType getSwingType();
+    property public final java.time.Duration durationSinceBoot;
+    property public final androidx.health.services.client.data.GolfShotEvent.GolfShotSwingType swingType;
+  }
+
+  public static final class GolfShotEvent.GolfShotSwingType {
+    field public static final androidx.health.services.client.data.GolfShotEvent.GolfShotSwingType.Companion Companion;
+    field public static final androidx.health.services.client.data.GolfShotEvent.GolfShotSwingType FULL;
+    field public static final androidx.health.services.client.data.GolfShotEvent.GolfShotSwingType PARTIAL;
+    field public static final androidx.health.services.client.data.GolfShotEvent.GolfShotSwingType PUTT;
+    field public static final androidx.health.services.client.data.GolfShotEvent.GolfShotSwingType UNKNOWN;
+  }
+
+  public static final class GolfShotEvent.GolfShotSwingType.Companion {
+  }
+
+  public final class GolfShotEventCapabilities extends androidx.health.services.client.data.ExerciseEventCapabilities {
+    ctor public GolfShotEventCapabilities(boolean isSupported, boolean isSwingTypeClassificationSupported);
+    method public boolean isSupported();
+    method public boolean isSwingTypeClassificationSupported();
+    property public boolean isSupported;
+    property public final boolean isSwingTypeClassificationSupported;
+  }
+
   public final class HealthEvent {
     ctor public HealthEvent(androidx.health.services.client.data.HealthEvent.Type type, java.time.Instant eventTime, androidx.health.services.client.data.DataPointContainer metrics);
     method public java.time.Instant getEventTime();
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/ExerciseUpdateCallback.kt b/health/health-services-client/src/main/java/androidx/health/services/client/ExerciseUpdateCallback.kt
index f2bc4c34..11aaf5d 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/ExerciseUpdateCallback.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/ExerciseUpdateCallback.kt
@@ -18,6 +18,7 @@
 
 import androidx.health.services.client.data.Availability
 import androidx.health.services.client.data.DataType
+import androidx.health.services.client.data.ExerciseEvent
 import androidx.health.services.client.data.ExerciseLapSummary
 import androidx.health.services.client.data.ExerciseState
 import androidx.health.services.client.data.ExerciseUpdate
@@ -59,4 +60,10 @@
      * @param availability the new [Availability] state
      */
     public fun onAvailabilityChanged(dataType: DataType<*, *>, availability: Availability)
+
+    /**
+     * Called when an [ExerciseEvent] is emitted. May be called during any exercise state
+     * except for PREPARING or ENDED.
+     */
+    public fun onExerciseEventReceived(event: ExerciseEvent) {}
 }
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/BatchingMode.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/BatchingMode.kt
index fa9ea16..b61a875 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/BatchingMode.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/BatchingMode.kt
@@ -37,13 +37,15 @@
 
     public companion object {
         /**
-         * Batching mode for receiving [DataType.HEART_RATE_BPM] updates with fast frequency.
+         * Deliver smaller and more frequent batches of [DataType.HEART_RATE_BPM] when the device is
+         * not interactive (e.g. screen is off).
          *
-         * Note: This mode will cause significantly increased power consumption compared to the
-         * default batching mode, while still being more power efficient than streaming when in
-         * non-interactive state. The exact power/performance tradeoff of this mode is device
-         * implementation dependent and batched updates may be aligned with other wake ups but
-         * target five second updates.
+         * This setting significantly increases power consumption, and is intended to be used by
+         * apps which need to send data to a separate device (e.g. a connected phone or TV) for
+         * real-time visualisation. It has no effect if the device is interactive.
+         *
+         * The exact power/performance tradeoff of this mode is device implementation dependent and
+         * batched updates may be aligned with other wake ups but target five second updates.
          */
         @JvmField public val HEART_RATE_5_SECONDS: BatchingMode = BatchingMode(1)
 
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseConfig.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseConfig.kt
index 9110a8d..f417756 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseConfig.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseConfig.kt
@@ -42,9 +42,12 @@
  * @property exerciseTypeConfig [ExerciseTypeConfig] containing attributes which may be
  * modified after the exercise has started
  * @property batchingModeOverrides [BatchingMode] overrides for this exercise
+ * @property exerciseEventTypes [ExerciseEventType]s which should be tracked for this exercise
  */
 @Suppress("ParcelCreator")
-class ExerciseConfig(
+class ExerciseConfig
+@JvmOverloads
+constructor(
     val exerciseType: ExerciseType,
     val dataTypes: Set<DataType<*, *>>,
     val isAutoPauseAndResumeEnabled: Boolean,
@@ -54,46 +57,8 @@
     @FloatRange(from = 0.0) val swimmingPoolLengthMeters: Float = SWIMMING_POOL_LENGTH_UNSPECIFIED,
     val exerciseTypeConfig: ExerciseTypeConfig? = null,
     val batchingModeOverrides: Set<BatchingMode> = emptySet(),
+    val exerciseEventTypes: Set<ExerciseEventType<*>> = emptySet(),
 ) {
-    constructor(
-        exerciseType: ExerciseType,
-        dataTypes: Set<DataType<*, *>>,
-        isAutoPauseAndResumeEnabled: Boolean,
-        isGpsEnabled: Boolean,
-        exerciseGoals: List<ExerciseGoal<*>> = listOf(),
-        exerciseParams: Bundle = Bundle(),
-        @FloatRange(from = 0.0) swimmingPoolLengthMeters: Float = SWIMMING_POOL_LENGTH_UNSPECIFIED,
-        exerciseTypeConfig: ExerciseTypeConfig? = null,
-    ) : this(
-        exerciseType,
-        dataTypes,
-        isAutoPauseAndResumeEnabled,
-        isGpsEnabled,
-        exerciseGoals,
-        exerciseParams,
-        swimmingPoolLengthMeters,
-        exerciseTypeConfig,
-        emptySet()
-    )
-
-    constructor(
-        exerciseType: ExerciseType,
-        dataTypes: Set<DataType<*, *>>,
-        isAutoPauseAndResumeEnabled: Boolean,
-        isGpsEnabled: Boolean,
-        exerciseGoals: List<ExerciseGoal<*>> = listOf(),
-        exerciseParams: Bundle = Bundle(),
-        @FloatRange(from = 0.0) swimmingPoolLengthMeters: Float = SWIMMING_POOL_LENGTH_UNSPECIFIED,
-    ) : this(
-            exerciseType,
-            dataTypes,
-            isAutoPauseAndResumeEnabled,
-            isGpsEnabled,
-            exerciseGoals,
-            exerciseParams,
-            swimmingPoolLengthMeters,
-            null
-    )
 
     internal constructor(
         proto: DataProto.ExerciseConfig
@@ -114,6 +79,7 @@
             ExerciseTypeConfig.fromProto(proto.exerciseTypeConfig)
         } else null,
         proto.batchingModeOverridesList.map { BatchingMode(it) }.toSet(),
+        proto.exerciseEventTypesList.map { ExerciseEventType.fromProto(it) }.toSet(),
     )
 
     init {
@@ -149,6 +115,7 @@
         private var swimmingPoolLength: Float = SWIMMING_POOL_LENGTH_UNSPECIFIED
         private var exerciseTypeConfig: ExerciseTypeConfig? = null
         private var batchingModeOverrides: Set<BatchingMode> = emptySet()
+        private var exerciseEventTypes: Set<ExerciseEventType<*>> = emptySet()
 
         /**
          * Sets the requested [DataType]s that should be tracked during this exercise. If not
@@ -248,6 +215,16 @@
             return this
         }
 
+        /**
+         * Sets the [ExerciseEventType]s that should be tracked for this exercise.
+         *
+         * @param exerciseEventTypes the set of [ExerciseEventType]s to begin the exercise with
+         */
+        fun setExerciseEventTypes(exerciseEventTypes: Set<ExerciseEventType<*>>): Builder {
+            this.exerciseEventTypes = exerciseEventTypes
+            return this
+        }
+
         /** Returns the built [ExerciseConfig]. */
         fun build(): ExerciseConfig {
             return ExerciseConfig(
@@ -260,6 +237,7 @@
                 swimmingPoolLength,
                 exerciseTypeConfig,
                 batchingModeOverrides,
+                exerciseEventTypes,
             )
         }
     }
@@ -285,6 +263,7 @@
             .setExerciseParams(BundlesUtil.toProto(exerciseParams))
             .setSwimmingPoolLength(swimmingPoolLengthMeters)
             .addAllBatchingModeOverrides(batchingModeOverrides.map { it.toProto() })
+            .addAllExerciseEventTypes(exerciseEventTypes.map { it.toProto() })
         if (exerciseTypeConfig != null) {
             builder.exerciseTypeConfig = exerciseTypeConfig.toProto()
         }
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseEvent.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseEvent.kt
new file mode 100644
index 0000000..6e6e28a
--- /dev/null
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseEvent.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.services.client.data
+
+import androidx.annotation.RestrictTo
+import androidx.annotation.RestrictTo.Scope
+import androidx.health.services.client.proto.DataProto
+import androidx.health.services.client.proto.DataProto.ExerciseEvent.ExerciseEventData.ExerciseEventDataCase
+import java.time.Duration
+
+/** Contains the latest exercise event for the current exercise. */
+public abstract class ExerciseEvent @RestrictTo(Scope.LIBRARY_GROUP) constructor() {
+  internal open fun toProto():
+    DataProto.ExerciseEvent = DataProto.ExerciseEvent.getDefaultInstance()
+
+  public companion object {
+    @JvmStatic
+    internal fun fromProto(proto: DataProto.ExerciseEvent): ExerciseEvent =
+      when (proto.exerciseEventData.exerciseEventDataCase) {
+        ExerciseEventDataCase.GOLF_SHOT_DATA ->
+          GolfShotEvent(
+            Duration.ofMillis(proto.exerciseEventData.golfShotData.durationFromBootMs),
+            GolfShotEvent.GolfShotSwingType.fromProto(
+              proto.exerciseEventData.golfShotData.golfShotSwingType)
+          )
+        else -> throw IllegalStateException("Exercise event not set on $proto")
+      }
+  }
+}
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseEventCapabilities.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseEventCapabilities.kt
new file mode 100644
index 0000000..87a525c
--- /dev/null
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseEventCapabilities.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.services.client.data
+
+import androidx.annotation.RestrictTo
+import androidx.annotation.RestrictTo.Scope
+import androidx.health.services.client.proto.DataProto
+import androidx.health.services.client.proto.DataProto.ExerciseEventCapabilities.ExerciseEventCapabilitiesCase
+
+/** Contains the capabilities specific to the associated [ExerciseEvent]. */
+public abstract class ExerciseEventCapabilities @RestrictTo(Scope.LIBRARY_GROUP) constructor() {
+  /** Returns true if this [ExerciseEvent] is supported by the device and false otherwise. */
+  public abstract val isSupported: Boolean
+
+  internal open fun toProto(): DataProto.ExerciseEventCapabilities =
+    DataProto.ExerciseEventCapabilities.getDefaultInstance()
+
+  public companion object {
+    @JvmStatic
+    internal fun fromProto(proto: DataProto.ExerciseEventCapabilities): ExerciseEventCapabilities =
+      when (proto.exerciseEventCapabilitiesCase) {
+        ExerciseEventCapabilitiesCase.GOLF_SHOT_CAPABILITIES ->
+          GolfShotEventCapabilities(proto.golfShotCapabilities)
+        else -> throw IllegalStateException("Exercise event capabilities not set on $proto")
+      }
+  }
+}
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseEventType.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseEventType.kt
new file mode 100644
index 0000000..35996a2
--- /dev/null
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseEventType.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.services.client.data
+
+import androidx.annotation.RestrictTo
+import androidx.annotation.RestrictTo.Scope
+import androidx.health.services.client.proto.DataProto
+import androidx.health.services.client.proto.DataProto.ExerciseEventType.EXERCISE_EVENT_TYPE_GOLF_SHOT
+import androidx.health.services.client.proto.DataProto.ExerciseEventType.EXERCISE_EVENT_TYPE_UNKNOWN
+
+/**
+ * Type of exercise event which specifies the representations of [ExerciseEventCapabilities] for the
+ * event.
+ *
+ * Note: the exercise event type defines only the representation and data format of event types. It
+ * does not act as a form of delivery for the event data.
+ */
+public class ExerciseEventType<C : ExerciseEventCapabilities> @RestrictTo(RestrictTo.Scope.LIBRARY)
+public constructor(
+  private val id: Int
+) {
+  internal fun toProto(): DataProto.ExerciseEventType =
+    when (this) {
+      GOLF_SHOT_EVENT -> EXERCISE_EVENT_TYPE_GOLF_SHOT
+      else -> EXERCISE_EVENT_TYPE_UNKNOWN
+    }
+
+  override fun equals(other: Any?): Boolean {
+    if (this === other) return true
+    if (other !is ExerciseEventType<*>) return false
+
+    if (id != other.id) return false
+
+    return true
+  }
+
+  override fun hashCode(): Int = id
+
+  override fun toString(): String {
+    val name = when (id) {
+      2 -> "GolfShotEvent"
+      else -> "UNKNOWN"
+    }
+    return "ExerciseEventType{ $name }"
+  }
+
+  public companion object {
+    /**
+     * An exercise event that can be used to notify the user a golf shot is detected by the device.
+     */
+    @JvmField
+    public val GOLF_SHOT_EVENT: ExerciseEventType<GolfShotEventCapabilities> =
+      ExerciseEventType<GolfShotEventCapabilities>(2)
+
+    /** An unknown event type. This should not be received. */
+    @JvmField
+    public val UNKNOWN: ExerciseEventType<ExerciseEventCapabilities> =
+      ExerciseEventType<ExerciseEventCapabilities>(0)
+
+    internal fun fromProto(proto: DataProto.ExerciseEventType): ExerciseEventType<*> =
+      when (proto) {
+        EXERCISE_EVENT_TYPE_GOLF_SHOT -> GOLF_SHOT_EVENT
+        EXERCISE_EVENT_TYPE_UNKNOWN -> UNKNOWN
+      }
+  }
+}
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseTypeCapabilities.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseTypeCapabilities.kt
index 9c3dbc9..8f48a02 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseTypeCapabilities.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/ExerciseTypeCapabilities.kt
@@ -22,7 +22,9 @@
 
 /** Provides exercise specific capabilities data. */
 @Suppress("ParcelCreator")
-public class ExerciseTypeCapabilities(
+public class ExerciseTypeCapabilities
+@JvmOverloads
+constructor(
     /** Supported [DataType]s for a given exercise. */
     public val supportedDataTypes: Set<DataType<*, *>>,
     /** Map from supported goals [DataType]s to a set of compatible [ComparisonType]s. */
@@ -31,6 +33,11 @@
     public val supportedMilestones: Map<AggregateDataType<*, *>, Set<ComparisonType>>,
     /** Returns `true` if the given exercise supports auto pause and resume. */
     public val supportsAutoPauseAndResume: Boolean,
+    /** Supported [ExerciseEvent]s for a given exercise. */
+    public val supportedExerciseEvents: Set<ExerciseEventType<*>> = emptySet(),
+    /** Map from [ExerciseEventType]s to their [ExerciseEventCapabilities]. */
+    internal val exerciseEventCapabilities: Map<ExerciseEventType<*>, ExerciseEventCapabilities> =
+    emptyMap(),
 ) {
 
     internal constructor(
@@ -62,6 +69,16 @@
             }
             .toMap(),
         supportsAutoPauseAndResume = proto.isAutoPauseAndResumeSupported,
+        supportedExerciseEvents =
+            proto.supportedExerciseEventsList
+                .map { ExerciseEventType.fromProto(it.exerciseEventType) }
+                .toSet(),
+            proto.supportedExerciseEventsList
+                .map { entry ->
+                    ExerciseEventType.fromProto(entry.exerciseEventType) to
+                      ExerciseEventCapabilities.fromProto(entry)
+                }
+                .toMap(),
     )
 
     internal val proto: DataProto.ExerciseTypeCapabilities =
@@ -88,8 +105,17 @@
                     .sortedBy { it.dataType.name } // Sorting to ensure equals() works
             )
             .setIsAutoPauseAndResumeSupported(supportsAutoPauseAndResume)
+            .addAllSupportedExerciseEvents(exerciseEventCapabilities.map { it.value.toProto() })
             .build()
 
+    /** Returns the [ExerciseEventCapabilities] for a requested [ExerciseEventType]. */
+    public fun <C : ExerciseEventCapabilities> getExerciseEventCapabilityDetails(
+        exerciseEventType: ExerciseEventType<C>
+    ): C? {
+        @Suppress("UNCHECKED_CAST") // Map's keys' and values' types will match
+        return exerciseEventCapabilities[exerciseEventType] as C?
+    }
+
     override fun toString(): String =
         "ExerciseTypeCapabilities(" +
             "supportedDataTypes=$supportedDataTypes, " +
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/GolfShotEvent.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/GolfShotEvent.kt
new file mode 100644
index 0000000..e2e05b6
--- /dev/null
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/GolfShotEvent.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.services.client.data
+
+import androidx.health.services.client.proto.DataProto
+import java.time.Duration
+import java.util.Objects
+
+/** An [ExerciseEvent] that contains information about Golf Shot events for the current exercise. */
+public class GolfShotEvent(
+  val durationSinceBoot: Duration,
+  val swingType: GolfShotSwingType
+) : ExerciseEvent() {
+
+  internal constructor(
+    proto: DataProto.GolfShotData
+  ) : this(
+    Duration.ofMillis(proto.durationFromBootMs),
+    GolfShotSwingType.fromProto(proto.golfShotSwingType),
+  )
+
+  /** Golf Shot Swing Types. */
+  public class GolfShotSwingType private constructor(private val id: Int) {
+    override fun equals(other: Any?): Boolean {
+      if (this === other) return true
+      if (other !is GolfShotSwingType) return false
+      if (id != other.id) return false
+
+      return true
+    }
+    override fun hashCode(): Int = id
+
+    override fun toString(): String {
+      val name = when (id) {
+        1 -> "PUTT"
+        2 -> "PARTIAL"
+        3 -> "FULL"
+        else -> "UNKNOWN"
+      }
+      return "GolfShotEvent{ $name }"
+    }
+
+    internal fun toProto(): DataProto.GolfShotSwingType =
+      DataProto.GolfShotSwingType.forNumber(id)
+        ?: DataProto.GolfShotSwingType.GOLF_SHOT_SWING_TYPE_UNKNOWN
+
+    companion object {
+      /** The swing type of the received golf shot is unknown. */
+      @JvmField val UNKNOWN: GolfShotSwingType = GolfShotSwingType(0)
+
+      /** The swing type of the received golf shot is putt. */
+      @JvmField val PUTT: GolfShotSwingType = GolfShotSwingType(1)
+
+      /** The swing type of the received golf shot is partial. */
+      @JvmField val PARTIAL: GolfShotSwingType = GolfShotSwingType(2)
+
+      /** The swing type of the received golf shot is full. */
+      @JvmField val FULL: GolfShotSwingType = GolfShotSwingType(3)
+
+      internal fun fromProto(proto: DataProto.GolfShotSwingType): GolfShotSwingType =
+        when (proto) {
+          DataProto.GolfShotSwingType.GOLF_SHOT_SWING_TYPE_PUTT -> PUTT
+          DataProto.GolfShotSwingType.GOLF_SHOT_SWING_TYPE_PARTIAL -> PARTIAL
+          DataProto.GolfShotSwingType.GOLF_SHOT_SWING_TYPE_FULL -> FULL
+          else -> UNKNOWN
+        }
+    }
+  }
+
+  internal override fun toProto(): DataProto.ExerciseEvent =
+    DataProto.ExerciseEvent.newBuilder()
+      .setExerciseEventData(
+        DataProto.ExerciseEvent.ExerciseEventData.newBuilder()
+          .setGolfShotData(
+            DataProto.GolfShotData.newBuilder()
+              .setDurationFromBootMs(durationSinceBoot.toMillis())
+              .setGolfShotSwingType(swingType.toProto()))
+      )
+      .build()
+
+  override fun equals(other: Any?): Boolean {
+    if (this === other) return true
+    if (other !is GolfShotEvent) return false
+
+    if (durationSinceBoot != other.durationSinceBoot) return false
+    if (swingType != other.swingType) return false
+
+    return true
+  }
+
+  override fun hashCode(): Int {
+    return Objects.hash(durationSinceBoot, swingType)
+  }
+
+  override fun toString(): String {
+    return "${this::class.simpleName}" +
+      "(durationSinceBoot=$durationSinceBoot, swingType=$swingType)"
+  }
+}
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/GolfShotEventCapabilities.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/GolfShotEventCapabilities.kt
new file mode 100644
index 0000000..51d5bca
--- /dev/null
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/GolfShotEventCapabilities.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.services.client.data
+
+import androidx.annotation.RestrictTo
+import androidx.health.services.client.proto.DataProto
+import androidx.health.services.client.proto.DataProto.ExerciseEventCapabilities.GolfShotCapabilities
+import androidx.health.services.client.proto.DataProto.ExerciseEventType.EXERCISE_EVENT_TYPE_GOLF_SHOT
+import java.util.Objects
+
+/** Contains the Golf Shot capabilities specific to the associated [GolfShotEvent]. */
+public class GolfShotEventCapabilities(
+  override val isSupported: Boolean,
+  public val isSwingTypeClassificationSupported: Boolean,
+) : ExerciseEventCapabilities() {
+  internal constructor(
+    proto: DataProto.ExerciseEventCapabilities.GolfShotCapabilities
+  ) : this(proto.isSupported, proto.isSwingTypeClassificationSupported)
+
+  @RestrictTo(RestrictTo.Scope.LIBRARY)
+  override fun toProto(): DataProto.ExerciseEventCapabilities =
+    DataProto.ExerciseEventCapabilities.newBuilder()
+      .setExerciseEventType(EXERCISE_EVENT_TYPE_GOLF_SHOT)
+      .setGolfShotCapabilities(
+        GolfShotCapabilities.newBuilder()
+          .setIsSupported(this.isSupported)
+          .setIsSwingTypeClassificationSupported(this.isSwingTypeClassificationSupported)
+          .build()
+      )
+      .build()
+
+  override fun equals(other: Any?): Boolean {
+    if (this === other) return true
+    if (other !is GolfShotEventCapabilities) return false
+    if (isSupported != other.isSupported) return false
+    if (isSwingTypeClassificationSupported != other.isSwingTypeClassificationSupported) return false
+
+    return true
+  }
+
+  override fun hashCode(): Int {
+    return Objects.hash(isSupported, isSwingTypeClassificationSupported)
+  }
+}
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/impl/ExerciseUpdateListenerStub.kt b/health/health-services-client/src/main/java/androidx/health/services/client/impl/ExerciseUpdateListenerStub.kt
index 74d6913..86832b4 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/impl/ExerciseUpdateListenerStub.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/impl/ExerciseUpdateListenerStub.kt
@@ -21,6 +21,7 @@
 import androidx.health.services.client.ExerciseUpdateCallback
 import androidx.health.services.client.data.Availability
 import androidx.health.services.client.data.DataType
+import androidx.health.services.client.data.ExerciseEvent
 import androidx.health.services.client.data.ExerciseLapSummary
 import androidx.health.services.client.data.ExerciseUpdate
 import androidx.health.services.client.impl.event.ExerciseUpdateListenerEvent
@@ -69,7 +70,13 @@
                 val availability = Availability.fromProto(proto.availabilityResponse.availability)
                 matchingDataTypes.forEach { listener.onAvailabilityChanged(it, availability) }
             }
-            null, EventCase.EVENT_NOT_SET -> Log.w(TAG, "Received unknown event ${proto.eventCase}")
+            EventCase.EXERCISE_EVENT_RESPONSE ->
+                listener
+                    .onExerciseEventReceived(
+                        ExerciseEvent.fromProto(
+                            proto.exerciseEventResponse.exerciseEvent))
+            null,
+            EventCase.EVENT_NOT_SET -> Log.w(TAG, "Received unknown event ${proto.eventCase}")
         }
     }
 
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/impl/event/ExerciseUpdateListenerEvent.kt b/health/health-services-client/src/main/java/androidx/health/services/client/impl/event/ExerciseUpdateListenerEvent.kt
index 7b4a169..919d2d5 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/impl/event/ExerciseUpdateListenerEvent.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/impl/event/ExerciseUpdateListenerEvent.kt
@@ -3,6 +3,7 @@
 import android.os.Parcelable
 import androidx.health.services.client.data.ProtoParcelable
 import androidx.health.services.client.impl.response.AvailabilityResponse
+import androidx.health.services.client.impl.response.ExerciseEventResponse
 import androidx.health.services.client.impl.response.ExerciseLapSummaryResponse
 import androidx.health.services.client.impl.response.ExerciseUpdateResponse
 import androidx.health.services.client.proto.EventsProto.ExerciseUpdateListenerEvent as ListenerProto
@@ -40,5 +41,13 @@
             ExerciseUpdateListenerEvent(
                 ListenerProto.newBuilder().setAvailabilityResponse(availability.proto).build()
             )
+
+        @JvmStatic
+        public fun createExerciseEventUpdateEvent(
+            exerciseEvent: ExerciseEventResponse
+        ): ExerciseUpdateListenerEvent =
+            ExerciseUpdateListenerEvent(
+                ListenerProto.newBuilder().setExerciseEventResponse(exerciseEvent.proto).build()
+            )
     }
 }
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/impl/response/ExerciseEventResponse.kt b/health/health-services-client/src/main/java/androidx/health/services/client/impl/response/ExerciseEventResponse.kt
new file mode 100644
index 0000000..23fd82b
--- /dev/null
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/impl/response/ExerciseEventResponse.kt
@@ -0,0 +1,24 @@
+package androidx.health.services.client.impl.response
+
+import android.os.Parcelable
+import androidx.health.services.client.data.ExerciseEvent
+import androidx.health.services.client.data.ProtoParcelable
+import androidx.health.services.client.proto.ResponsesProto
+
+/** Response containing [ExerciseEvent] when it's sent. */
+internal class ExerciseEventResponse(public val exerciseEvent: ExerciseEvent) :
+  ProtoParcelable<ResponsesProto.ExerciseEventResponse>() {
+  override val proto: ResponsesProto.ExerciseEventResponse by lazy {
+    ResponsesProto.ExerciseEventResponse.newBuilder()
+      .setExerciseEvent(exerciseEvent.toProto())
+      .build()
+  }
+
+  public companion object {
+    @JvmField
+    public val CREATOR: Parcelable.Creator<ExerciseEventResponse> = newCreator { bytes ->
+      val proto = ResponsesProto.ExerciseEventResponse.parseFrom(bytes)
+      ExerciseEventResponse(ExerciseEvent.fromProto(proto.exerciseEvent))
+    }
+  }
+}
diff --git a/health/health-services-client/src/main/proto/data.proto b/health/health-services-client/src/main/proto/data.proto
index f754276..2a2e5ee 100644
--- a/health/health-services-client/src/main/proto/data.proto
+++ b/health/health-services-client/src/main/proto/data.proto
@@ -204,8 +204,9 @@
   optional float swimming_pool_length = 8;
   optional ExerciseTypeConfig exercise_type_config = 10;
   repeated BatchingMode batching_mode_overrides = 11 [packed = true];
+  repeated ExerciseEventType exercise_event_types = 12 [packed = true];
   reserved 9;
-  reserved 12 to max;  // Next ID
+  reserved 13 to max;  // Next ID
 }
 
 message ExerciseInfo {
@@ -372,6 +373,56 @@
   reserved 93 to max;  // Next ID
 }
 
+enum GolfShotSwingType {
+  GOLF_SHOT_SWING_TYPE_UNKNOWN = 0;
+  GOLF_SHOT_SWING_TYPE_PUTT = 1;
+  GOLF_SHOT_SWING_TYPE_PARTIAL = 2;
+  GOLF_SHOT_SWING_TYPE_FULL = 3;
+  reserved 4 to max;  // Next ID
+}
+
+message GolfShotData {
+  optional int64 duration_from_boot_ms = 1;
+  optional GolfShotSwingType golf_shot_swing_type = 2;
+  reserved 3 to max;  // Next ID
+}
+
+enum ExerciseEventType {
+  EXERCISE_EVENT_TYPE_UNKNOWN = 0;
+  EXERCISE_EVENT_TYPE_GOLF_SHOT = 2;
+  reserved 1;
+  reserved 3 to max;  // Next ID
+}
+
+message ExerciseEvent {
+  message ExerciseEventData {
+    oneof exerciseEventData {
+      GolfShotData golf_shot_data = 2;
+    }
+    reserved 1;
+    reserved 3 to max;  // Next ID
+  }
+
+  optional ExerciseEventData exercise_event_data = 1;
+  reserved 2 to max;  // Next ID
+}
+
+message ExerciseEventCapabilities {
+  message GolfShotCapabilities {
+    optional bool is_supported = 1;
+    optional bool is_swing_type_classification_supported = 2;
+
+    reserved 3 to max;  // Next ID
+  }
+
+  optional ExerciseEventType exercise_event_type = 1;
+  oneof exerciseEventCapabilities {
+    GolfShotCapabilities golf_shot_capabilities = 3;
+  }
+  reserved 2;
+  reserved 4 to max;  // Next ID
+}
+
 message ExerciseTypeCapabilities {
   message SupportedGoalEntry {
     optional DataType data_type = 1;
@@ -389,7 +440,9 @@
   repeated SupportedMilestoneEntry supported_milestones = 3;
   optional bool is_auto_pause_and_resume_supported = 4;
   optional bool is_laps_supported = 5;
-  reserved 6 to max;  // Next ID
+  repeated ExerciseEventCapabilities supported_exercise_events = 7;
+  reserved 6;
+  reserved 8 to max;  // Next ID
 }
 
 message ExerciseUpdate {
diff --git a/health/health-services-client/src/main/proto/events.proto b/health/health-services-client/src/main/proto/events.proto
index d8385ca..f621a8c 100644
--- a/health/health-services-client/src/main/proto/events.proto
+++ b/health/health-services-client/src/main/proto/events.proto
@@ -28,8 +28,9 @@
     ExerciseUpdateResponse exercise_update_response = 1;
     ExerciseLapSummaryResponse lap_summary_response = 2;
     AvailabilityResponse availability_response = 3;
+    ExerciseEventResponse exercise_event_response = 4;
   }
-  reserved 4 to max;  // Next ID
+  reserved 5 to max;  // Next ID
 }
 
 message MeasureCallbackEvent {
diff --git a/health/health-services-client/src/main/proto/responses.proto b/health/health-services-client/src/main/proto/responses.proto
index eb72f92..6f38afa 100644
--- a/health/health-services-client/src/main/proto/responses.proto
+++ b/health/health-services-client/src/main/proto/responses.proto
@@ -39,6 +39,11 @@
   reserved 2 to max;  // Next ID
 }
 
+message ExerciseEventResponse {
+  optional ExerciseEvent exercise_event = 1;
+  reserved 2 to max;  // Next ID
+}
+
 message ExerciseInfoResponse {
   optional ExerciseInfo exercise_info = 1;
   reserved 2 to max;  // Next ID
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseCapabilitiesTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseCapabilitiesTest.kt
index 4a43798..fc24a36 100644
--- a/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseCapabilitiesTest.kt
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseCapabilitiesTest.kt
@@ -31,8 +31,8 @@
             EXERCISE_CAPABILITIES.getExerciseTypeCapabilities(
                 ExerciseType.WALKING
             ).supportedDataTypes
-        ).isEqualTo(
-            ImmutableSet.of(DataType.STEPS)
+        ).containsExactly(
+            DataType.STEPS
         )
     }
 
@@ -58,6 +58,19 @@
     }
 
     @Test
+    fun supportedExerciseEventForGolfExercise() {
+        assertThat(
+            EXERCISE_CAPABILITIES.getExerciseTypeCapabilities(
+                ExerciseType.GOLF).supportedExerciseEvents
+        ).isEqualTo(
+            EXERCISE_CAPABILITIES.typeToCapabilities.get(ExerciseType.GOLF)?.supportedExerciseEvents
+        )
+        assertThat(EXERCISE_CAPABILITIES.typeToCapabilities[ExerciseType.GOLF]
+                        ?.getExerciseEventCapabilityDetails(ExerciseEventType.GOLF_SHOT_EVENT)
+                        ?.isSwingTypeClassificationSupported).isTrue()
+    }
+
+    @Test
     fun exercisesSupportingAutoResumeAndPause_returnCorrectSet() {
         val supportsAutoPauseAndResume = ExerciseTypeCapabilities(
             supportedDataTypes = ImmutableSet.of(),
@@ -93,6 +106,9 @@
         assertThat(capabilities.autoPauseAndResumeEnabledExercises).containsExactlyElementsIn(
             EXERCISE_CAPABILITIES.autoPauseAndResumeEnabledExercises
         )
+        assertThat(capabilities.supportedBatchingModeOverrides).containsExactlyElementsIn(
+            EXERCISE_CAPABILITIES.supportedBatchingModeOverrides
+        )
     }
 
     @Test
@@ -106,6 +122,9 @@
         assertThat(emptyCapabilities.autoPauseAndResumeEnabledExercises).containsExactlyElementsIn(
             roundTripEmptyCapabilities.autoPauseAndResumeEnabledExercises
         )
+        assertThat(emptyCapabilities.supportedBatchingModeOverrides).containsExactlyElementsIn(
+            roundTripEmptyCapabilities.supportedBatchingModeOverrides
+        )
     }
 
     companion object {
@@ -145,14 +164,29 @@
             supportsAutoPauseAndResume = true,
         )
 
+        private val GOLF_SHOT_EVENT_CAPABILITIES: GolfShotEventCapabilities =
+            GolfShotEventCapabilities(isSupported = true, isSwingTypeClassificationSupported = true)
+
+        private val GOLF_CAPABILITIES = ExerciseTypeCapabilities(
+            supportedDataTypes = emptySet(),
+            supportedGoals = emptyMap(),
+            supportedMilestones = emptyMap(),
+            supportsAutoPauseAndResume = true,
+            supportedExerciseEvents = setOf(ExerciseEventType.GOLF_SHOT_EVENT),
+            exerciseEventCapabilities =
+            ImmutableMap.of(ExerciseEventType.GOLF_SHOT_EVENT, GOLF_SHOT_EVENT_CAPABILITIES),
+        )
+
         private val EXERCISE_TYPE_TO_EXERCISE_CAPABILITIES_MAPPING =
             ImmutableMap.of(
                 ExerciseType.WALKING, WALKING_CAPABILITIES,
                 ExerciseType.RUNNING, RUNNING_CAPABILITIES,
-                ExerciseType.SWIMMING_POOL, SWIMMING_CAPABILITIES
+                ExerciseType.SWIMMING_POOL, SWIMMING_CAPABILITIES,
+                ExerciseType.GOLF, GOLF_CAPABILITIES,
             )
 
         private val EXERCISE_CAPABILITIES: ExerciseCapabilities =
-            ExerciseCapabilities(EXERCISE_TYPE_TO_EXERCISE_CAPABILITIES_MAPPING)
+            ExerciseCapabilities(EXERCISE_TYPE_TO_EXERCISE_CAPABILITIES_MAPPING,
+                ImmutableSet.of(BatchingMode.HEART_RATE_5_SECONDS))
     }
 }
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseConfigTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseConfigTest.kt
index da70783e..4a0aef3 100644
--- a/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseConfigTest.kt
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseConfigTest.kt
@@ -48,6 +48,7 @@
                     .GolfShotTrackingPlaceInfo.GOLF_SHOT_TRACKING_PLACE_INFO_FAIRWAY
             ),
             batchingModeOverrides = setOf(BatchingMode.HEART_RATE_5_SECONDS),
+            exerciseEventTypes = setOf(ExerciseEventType.GOLF_SHOT_EVENT),
         ).toProto()
 
         val config = ExerciseConfig(proto)
@@ -69,6 +70,7 @@
         ).isEqualTo(GolfExerciseTypeConfig
             .GolfShotTrackingPlaceInfo.GOLF_SHOT_TRACKING_PLACE_INFO_FAIRWAY)
         assertThat(config.batchingModeOverrides).containsExactly(BatchingMode.HEART_RATE_5_SECONDS)
+        assertThat(config.exerciseEventTypes).containsExactly(ExerciseEventType.GOLF_SHOT_EVENT)
     }
 
     @Test
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseEventCapabilitiesTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseEventCapabilitiesTest.kt
new file mode 100644
index 0000000..7d4be53
--- /dev/null
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseEventCapabilitiesTest.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.services.client.data
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class ExerciseEventCapabilitiesTest {
+  @Test
+  fun golfShotEventCapabilities_roundTrip_returnsOriginal() {
+    val originalGolfShotCapabilities =
+      GolfShotEventCapabilities(
+        isSupported = true,
+        isSwingTypeClassificationSupported = true,
+      )
+
+    val proto = originalGolfShotCapabilities.toProto()
+    val capabilities = ExerciseEventCapabilities.fromProto(proto) as GolfShotEventCapabilities
+
+    assertThat(capabilities.isSupported).isTrue()
+    assertThat(capabilities.isSwingTypeClassificationSupported).isTrue()
+    assertThat(capabilities).isEqualTo(originalGolfShotCapabilities)
+  }
+}
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseEventTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseEventTest.kt
new file mode 100644
index 0000000..1d8610b
--- /dev/null
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseEventTest.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.services.client.data
+
+import com.google.common.truth.Truth.assertThat
+import java.time.Duration
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class ExerciseEventTest {
+  @Test
+  fun exerciseEventGolfShotRoundTrip() {
+    val originalGolfShotEvent =
+      GolfShotEvent(Duration.ofSeconds(1), GolfShotEvent.GolfShotSwingType.FULL)
+
+    val proto = originalGolfShotEvent.toProto()
+    val event = ExerciseEvent.fromProto(proto) as GolfShotEvent
+
+    assertThat(event.durationSinceBoot).isEqualTo(Duration.ofSeconds(1))
+    assertThat(event.swingType).isEqualTo(GolfShotEvent.GolfShotSwingType.FULL)
+    assertThat(event).isEqualTo(originalGolfShotEvent)
+  }
+}
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseEventTypeTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseEventTypeTest.kt
new file mode 100644
index 0000000..023cb9f
--- /dev/null
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/data/ExerciseEventTypeTest.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.services.client.data
+
+import androidx.health.services.client.data.ExerciseEventType.Companion.GOLF_SHOT_EVENT
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class ExerciseEventTypeTest {
+  @Test
+  fun exerciseEventTypeGolfShotEventRoundTrip() {
+    val proto = GOLF_SHOT_EVENT.toProto()
+    val exerciseEventType = ExerciseEventType.fromProto(proto)
+
+    assertThat(exerciseEventType.toString()).isEqualTo("ExerciseEventType{ GolfShotEvent }")
+    assertThat(exerciseEventType).isEqualTo(GOLF_SHOT_EVENT)
+  }
+}
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/impl/ServiceBackedExerciseClientTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/impl/ServiceBackedExerciseClientTest.kt
index c918351..d0afedb 100644
--- a/health/health-services-client/src/test/java/androidx/health/services/client/impl/ServiceBackedExerciseClientTest.kt
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/impl/ServiceBackedExerciseClientTest.kt
@@ -24,14 +24,19 @@
 import androidx.health.services.client.data.Availability
 import androidx.health.services.client.data.BatchingMode
 import androidx.health.services.client.data.DataType
+import androidx.health.services.client.data.DataType.Companion.GOLF_SHOT_COUNT
 import androidx.health.services.client.data.DataType.Companion.HEART_RATE_BPM
 import androidx.health.services.client.data.DataType.Companion.HEART_RATE_BPM_STATS
 import androidx.health.services.client.data.DataTypeAvailability.Companion.ACQUIRING
 import androidx.health.services.client.data.ExerciseConfig
+import androidx.health.services.client.data.ExerciseEvent
+import androidx.health.services.client.data.ExerciseEventType
 import androidx.health.services.client.data.ExerciseLapSummary
 import androidx.health.services.client.data.ExerciseType
 import androidx.health.services.client.data.ExerciseUpdate
 import androidx.health.services.client.data.GolfExerciseTypeConfig
+import androidx.health.services.client.data.GolfShotEvent
+import androidx.health.services.client.data.GolfShotEvent.GolfShotSwingType
 import androidx.health.services.client.data.WarmUpConfig
 import androidx.health.services.client.impl.event.ExerciseUpdateListenerEvent
 import androidx.health.services.client.impl.internal.IExerciseInfoCallback
@@ -47,8 +52,10 @@
 import androidx.health.services.client.impl.request.UpdateExerciseTypeConfigRequest
 import androidx.health.services.client.impl.response.AvailabilityResponse
 import androidx.health.services.client.impl.response.ExerciseCapabilitiesResponse
+import androidx.health.services.client.impl.response.ExerciseEventResponse
 import androidx.test.core.app.ApplicationProvider
 import com.google.common.truth.Truth.assertThat
+import java.time.Duration
 import kotlin.test.assertFailsWith
 import org.junit.After
 import org.junit.Before
@@ -205,6 +212,37 @@
     }
 
     @Test
+    fun withExerciseEventConfig_startExercise_receiveCorrectExerciseEventCallback() {
+        val exerciseConfig = ExerciseConfig(
+            ExerciseType.GOLF,
+            setOf(GOLF_SHOT_COUNT),
+            isAutoPauseAndResumeEnabled = false,
+            isGpsEnabled = false,
+            exerciseTypeConfig = GolfExerciseTypeConfig(
+                GolfExerciseTypeConfig
+                    .GolfShotTrackingPlaceInfo.GOLF_SHOT_TRACKING_PLACE_INFO_PUTTING_GREEN
+            ),
+            exerciseEventTypes = setOf(ExerciseEventType.GOLF_SHOT_EVENT),
+        )
+        val golfShotEvent = ExerciseUpdateListenerEvent.createExerciseEventUpdateEvent(
+            ExerciseEventResponse(
+                GolfShotEvent(
+                    Duration.ofMinutes(1), GolfShotSwingType.PUTT
+                )
+            )
+        )
+
+        client.setUpdateCallback(callback)
+        client.startExerciseAsync(exerciseConfig)
+        shadowOf(getMainLooper()).idle()
+        fakeService.listener!!.onExerciseUpdateListenerEvent(golfShotEvent)
+        shadowOf(getMainLooper()).idle()
+
+        assertThat(callback.exerciseEvents)
+            .contains(GolfShotEvent(Duration.ofMinutes(1), GolfShotSwingType.PUTT))
+    }
+
+    @Test
     fun dataTypeInAvailabilityCallbackShouldMatchRequested_justSampleType_prepare() {
         val warmUpConfig = WarmUpConfig(
             ExerciseType.WALKING,
@@ -260,6 +298,7 @@
         val registrationFailureThrowables = mutableListOf<Throwable>()
         var onRegisteredCalls = 0
         var onRegistrationFailedCalls = 0
+        var exerciseEvents = mutableSetOf<ExerciseEvent>()
 
         override fun onRegistered() {
             onRegisteredCalls++
@@ -277,6 +316,14 @@
         override fun onAvailabilityChanged(dataType: DataType<*, *>, availability: Availability) {
             availabilities[dataType] = availability
         }
+
+        override fun onExerciseEventReceived(event: ExerciseEvent) {
+            when (event) {
+                is GolfShotEvent -> {
+                    exerciseEvents.add(event)
+                }
+            }
+        }
     }
 
     class FakeServiceStub : IExerciseApiService.Stub() {
diff --git a/hilt/hilt-compiler/build.gradle b/hilt/hilt-compiler/build.gradle
index c25c109..aec41ba 100644
--- a/hilt/hilt-compiler/build.gradle
+++ b/hilt/hilt-compiler/build.gradle
@@ -16,6 +16,7 @@
 
 import androidx.build.LibraryType
 import androidx.build.SdkHelperKt
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
     id("AndroidXPlugin")
@@ -31,18 +32,29 @@
     kapt(libs.autoService)
     compileOnly(libs.gradleIncapHelper)
     kapt(libs.gradleIncapHelperProcessor)
-    implementation(libs.autoCommon)
+    implementation(project(":room:room-compiler-processing"))
     implementation(libs.javapoet)
+    implementation(libs.kspApi)
 
     testImplementation(project(":hilt:hilt-common"))
+    testImplementation(project(":annotation:annotation"))
     testImplementation(libs.junit)
     testImplementation(libs.truth)
-    testImplementation(libs.googleCompileTesting)
+    testImplementation(project(":room:room-compiler-processing-testing"))
     testImplementation(libs.hiltCore)
     testImplementationAarAsJar(project(":hilt:hilt-work"))
     testImplementation(SdkHelperKt.getSdkDependency(project))
 }
 
+tasks.withType(KotlinCompile).configureEach {
+    kotlinOptions {
+        freeCompilerArgs += [
+                "-opt-in=androidx.room.compiler.processing.ExperimentalProcessingApi",
+                "-opt-in=com.squareup.kotlinpoet.javapoet.KotlinPoetJavaPoetPreview"
+        ]
+    }
+}
+
 tasks.withType(Test).configureEach {
     // https://github.com/google/compile-testing/issues/222
     it.jvmArgs "--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED"
diff --git a/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/AndroidXHiltKspProcessor.kt b/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/AndroidXHiltKspProcessor.kt
new file mode 100644
index 0000000..7c6fef4
--- /dev/null
+++ b/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/AndroidXHiltKspProcessor.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import androidx.hilt.work.WorkerStep
+import androidx.room.compiler.processing.ksp.KspBasicAnnotationProcessor
+import com.google.auto.service.AutoService
+import com.google.devtools.ksp.processing.SymbolProcessor
+import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
+import com.google.devtools.ksp.processing.SymbolProcessorProvider
+
+class AndroidXHiltKspProcessor(
+    environment: SymbolProcessorEnvironment
+) : KspBasicAnnotationProcessor(environment) {
+    override fun processingSteps() = listOf(WorkerStep())
+
+    @AutoService(SymbolProcessorProvider::class)
+    class Provider : SymbolProcessorProvider {
+        override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
+            return AndroidXHiltKspProcessor(environment)
+        }
+    }
+}
diff --git a/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/AndroidXHiltProcessor.kt b/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/AndroidXHiltProcessor.kt
index 277140b..cf646af 100644
--- a/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/AndroidXHiltProcessor.kt
+++ b/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/AndroidXHiltProcessor.kt
@@ -17,13 +17,10 @@
 package androidx.hilt
 
 import androidx.hilt.work.WorkerStep
+import androidx.room.compiler.processing.javac.JavacBasicAnnotationProcessor
 import com.google.auto.service.AutoService
-import javax.annotation.processing.AbstractProcessor
 import javax.annotation.processing.Processor
-import javax.annotation.processing.RoundEnvironment
 import javax.lang.model.SourceVersion
-import javax.lang.model.element.Element
-import javax.lang.model.element.TypeElement
 import net.ltgt.gradle.incap.IncrementalAnnotationProcessor
 import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.ISOLATING
 
@@ -32,32 +29,9 @@
  */
 @AutoService(Processor::class)
 @IncrementalAnnotationProcessor(ISOLATING)
-class AndroidXHiltProcessor : AbstractProcessor() {
+class AndroidXHiltProcessor : JavacBasicAnnotationProcessor() {
 
-    override fun getSupportedAnnotationTypes() = setOf(
-        ClassNames.HILT_WORKER.canonicalName()
-    )
+    override fun processingSteps() = listOf(WorkerStep())
 
     override fun getSupportedSourceVersion() = SourceVersion.latest()
-
-    override fun process(
-        annotations: MutableSet<out TypeElement>,
-        roundEnv: RoundEnvironment
-    ): Boolean {
-        getSteps().forEach { step ->
-            annotations.firstOrNull { it.qualifiedName.contentEquals(step.annotation()) }?.let {
-                step.process(roundEnv.getElementsAnnotatedWith(it))
-            }
-        }
-        return false
-    }
-
-    private fun getSteps() = listOf(
-        WorkerStep(processingEnv)
-    )
-
-    interface Step {
-        fun annotation(): String
-        fun process(annotatedElements: Set<Element>)
-    }
 }
diff --git a/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/assisted/AssistedFactoryGenerator.kt b/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/assisted/AssistedFactoryGenerator.kt
deleted file mode 100644
index b452df0..0000000
--- a/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/assisted/AssistedFactoryGenerator.kt
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.hilt.assisted
-
-import androidx.hilt.ClassNames
-import androidx.hilt.ext.L
-import androidx.hilt.ext.T
-import androidx.hilt.ext.W
-import androidx.hilt.ext.addGeneratedAnnotation
-import com.squareup.javapoet.ClassName
-import com.squareup.javapoet.CodeBlock
-import com.squareup.javapoet.FieldSpec
-import com.squareup.javapoet.JavaFile
-import com.squareup.javapoet.MethodSpec
-import com.squareup.javapoet.ParameterSpec
-import com.squareup.javapoet.ParameterizedTypeName
-import com.squareup.javapoet.TypeName
-import com.squareup.javapoet.TypeSpec
-import javax.annotation.processing.ProcessingEnvironment
-import javax.lang.model.element.Modifier
-import javax.lang.model.element.TypeElement
-import javax.lang.model.util.ElementFilter
-
-/**
- * Source generator for assisted factories.
- */
-internal class AssistedFactoryGenerator(
-    private val processingEnv: ProcessingEnvironment,
-    private val productClassName: ClassName,
-    private val factoryClassName: ClassName,
-    private val factorySuperTypeName: ParameterizedTypeName,
-    private val originatingElement: TypeElement,
-    private val dependencyRequests: List<DependencyRequest>
-) {
-
-    fun generate() {
-        val factoryTypeSpec = TypeSpec.classBuilder(factoryClassName)
-            .addOriginatingElement(originatingElement)
-            .addSuperinterface(factorySuperTypeName)
-            .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
-            .addGeneratedAnnotation(processingEnv.elementUtils, processingEnv.sourceVersion)
-            .addFields(getFieldSpecs())
-            .addMethod(getConstructorMethodSpec())
-            .addMethod(getCreateMethodSpec())
-            .build()
-        JavaFile.builder(factoryClassName.packageName(), factoryTypeSpec)
-            .build()
-            .writeTo(processingEnv.filer)
-    }
-
-    private fun getFieldSpecs() = dependencyRequests
-        .filterNot { it.isAssisted }
-        .map { dependencyRequest ->
-            val fieldTypeName = dependencyRequest.providerTypeName.withoutAnnotations()
-            FieldSpec.builder(fieldTypeName, dependencyRequest.name)
-                .addModifiers(Modifier.PRIVATE, Modifier.FINAL)
-                .build()
-        }
-
-    private fun getConstructorMethodSpec() =
-        MethodSpec.constructorBuilder()
-            .addAnnotation(ClassNames.INJECT)
-            .apply {
-                dependencyRequests
-                    .filterNot { it.isAssisted }
-                    .forEach { dependencyRequest ->
-                        addParameter(dependencyRequest.providerTypeName, dependencyRequest.name)
-                        addStatement("this.$1N = $1N", dependencyRequest.name)
-                    }
-            }
-            .build()
-
-    private fun getCreateMethodSpec(): MethodSpec {
-        val factoryTypeElement =
-            processingEnv.elementUtils.getTypeElement(factorySuperTypeName.rawType.canonicalName())
-        val factoryMethod = ElementFilter.methodsIn(factoryTypeElement.enclosedElements).first()
-        val parameterSpecs = factoryMethod.parameters.map { ParameterSpec.get(it) }
-        val constructorArgs = dependencyRequests.map {
-            val paramLiteral = when {
-                it.isAssisted -> {
-                    factoryMethod.parameters.first { param ->
-                        TypeName.get(param.asType()) == it.type
-                    }.simpleName.toString()
-                }
-                it.isProvider -> it.name
-                else -> "${it.name}.get()"
-            }
-            CodeBlock.of(L, paramLiteral)
-        }
-        return MethodSpec.methodBuilder(factoryMethod.simpleName.toString())
-            .addAnnotation(Override::class.java)
-            .addAnnotation(ClassNames.NON_NULL)
-            .addModifiers(Modifier.PUBLIC)
-            .returns(productClassName)
-            .addParameters(parameterSpecs)
-            .addStatement(
-                "return new $T($L)",
-                productClassName, CodeBlock.join(constructorArgs, ",$W")
-            )
-            .build()
-    }
-}
diff --git a/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/assisted/DependencyRequest.kt b/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/assisted/DependencyRequest.kt
index 822c778..6130cc7c 100644
--- a/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/assisted/DependencyRequest.kt
+++ b/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/assisted/DependencyRequest.kt
@@ -17,11 +17,12 @@
 package androidx.hilt.assisted
 
 import androidx.hilt.ClassNames
-import androidx.hilt.ext.hasAnnotation
+import androidx.room.compiler.codegen.toJavaPoet
+import androidx.room.compiler.processing.XVariableElement
+import androidx.room.compiler.processing.toAnnotationSpec
 import com.squareup.javapoet.AnnotationSpec
 import com.squareup.javapoet.ParameterizedTypeName
 import com.squareup.javapoet.TypeName
-import javax.lang.model.element.VariableElement
 
 /**
  * Data class that represents a binding request for an assisted injected type.
@@ -51,17 +52,16 @@
     }
 }
 
-internal fun VariableElement.toDependencyRequest(): DependencyRequest {
-    val qualifier = annotationMirrors.find {
-        it.annotationType.asElement().hasAnnotation("javax.inject.Qualifier")
-    }?.let { AnnotationSpec.get(it) }
-    val type = TypeName.get(asType())
+internal fun XVariableElement.toDependencyRequest(): DependencyRequest {
+    val qualifier = getAllAnnotations().find {
+        it.qualifiedName == "javax.inject.Qualifier"
+    }?.toAnnotationSpec(includeDefaultValues = false)
     return DependencyRequest(
-        name = simpleName.toString(),
-        type = type,
+        name = this.name,
+        type = this.type.asTypeName().toJavaPoet(),
         isAssisted = (
-            hasAnnotation(ClassNames.ANDROIDX_ASSISTED.canonicalName()) ||
-                hasAnnotation(ClassNames.ASSISTED.canonicalName())
+            this.hasAnnotation(ClassNames.ANDROIDX_ASSISTED) ||
+                this.hasAnnotation(ClassNames.ASSISTED)
             ) && qualifier == null,
         qualifier = qualifier
     )
diff --git a/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/ext/annotationProcessing.kt b/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/ext/annotationProcessing.kt
deleted file mode 100644
index 21827d2..0000000
--- a/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/ext/annotationProcessing.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.hilt.ext
-
-import com.google.auto.common.MoreElements
-import javax.lang.model.element.Element
-import kotlin.reflect.KClass
-
-fun Element.hasAnnotation(clazz: KClass<out Annotation>) =
-    MoreElements.isAnnotationPresent(this, clazz.java)
-
-fun Element.hasAnnotation(qName: String) = annotationMirrors.any {
-    MoreElements.asType(it.annotationType.asElement()).qualifiedName.contentEquals(qName)
-}
diff --git a/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/ext/javaPoet.kt b/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/ext/javaPoet.kt
index d3fc1c2..9aae5b2 100644
--- a/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/ext/javaPoet.kt
+++ b/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/ext/javaPoet.kt
@@ -17,10 +17,10 @@
 package androidx.hilt.ext
 
 import androidx.hilt.AndroidXHiltProcessor
-import com.google.auto.common.GeneratedAnnotationSpecs
+import androidx.room.compiler.processing.XProcessingEnv
+import com.squareup.javapoet.AnnotationSpec
+import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.TypeSpec
-import javax.lang.model.SourceVersion
-import javax.lang.model.util.Elements
 
 const val L = "\$L"
 const val T = "\$T"
@@ -28,15 +28,13 @@
 const val S = "\$S"
 const val W = "\$W"
 
-internal fun TypeSpec.Builder.addGeneratedAnnotation(
-    elements: Elements,
-    sourceVersion: SourceVersion
-) = apply {
-    GeneratedAnnotationSpecs.generatedAnnotationSpec(
-        elements,
-        sourceVersion,
-        AndroidXHiltProcessor::class.java
-    ).ifPresent { generatedAnnotation ->
-        addAnnotation(generatedAnnotation)
+internal fun TypeSpec.Builder.addGeneratedAnnotation(env: XProcessingEnv) = apply {
+    env.findGeneratedAnnotation()?.let {
+        addAnnotation(
+            AnnotationSpec.builder(ClassName.bestGuess(it.asClassName().canonicalName))
+                .addMember("value", S, AndroidXHiltProcessor::class.java.canonicalName)
+                .build()
+
+        )
     }
 }
diff --git a/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/work/WorkerElements.kt b/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/work/WorkerElement.kt
similarity index 75%
rename from hilt/hilt-compiler/src/main/kotlin/androidx/hilt/work/WorkerElements.kt
rename to hilt/hilt-compiler/src/main/kotlin/androidx/hilt/work/WorkerElement.kt
index f62c1ee6..52d4ff4 100644
--- a/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/work/WorkerElements.kt
+++ b/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/work/WorkerElement.kt
@@ -18,23 +18,23 @@
 
 import androidx.hilt.ClassNames
 import androidx.hilt.assisted.toDependencyRequest
-import com.google.auto.common.MoreElements
+import androidx.room.compiler.codegen.toJavaPoet
+import androidx.room.compiler.processing.XConstructorElement
+import androidx.room.compiler.processing.XTypeElement
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.ParameterizedTypeName
-import javax.lang.model.element.ExecutableElement
-import javax.lang.model.element.TypeElement
 
 /**
  * Data class that represents a Hilt injected Worker
  */
-internal data class WorkerElements(
-    val typeElement: TypeElement,
-    val constructorElement: ExecutableElement
+internal data class WorkerElement(
+    val typeElement: XTypeElement,
+    val constructorElement: XConstructorElement
 ) {
-    val className = ClassName.get(typeElement)
+    val className = typeElement.asClassName().toJavaPoet()
 
     val factoryClassName = ClassName.get(
-        MoreElements.getPackage(typeElement).qualifiedName.toString(),
+        typeElement.packageName,
         "${className.simpleNames().joinToString("_")}_AssistedFactory"
     )
 
@@ -44,7 +44,7 @@
     )
 
     val moduleClassName = ClassName.get(
-        MoreElements.getPackage(typeElement).qualifiedName.toString(),
+        typeElement.packageName,
         "${className.simpleNames().joinToString("_")}_HiltModule"
     )
 
diff --git a/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/work/WorkerGenerator.kt b/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/work/WorkerGenerator.kt
index 4052322..155f2e4 100644
--- a/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/work/WorkerGenerator.kt
+++ b/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/work/WorkerGenerator.kt
@@ -20,13 +20,15 @@
 import androidx.hilt.ext.S
 import androidx.hilt.ext.T
 import androidx.hilt.ext.addGeneratedAnnotation
+import androidx.room.compiler.processing.XProcessingEnv
+import androidx.room.compiler.processing.addOriginatingElement
+import androidx.room.compiler.processing.writeTo
 import com.squareup.javapoet.AnnotationSpec
 import com.squareup.javapoet.JavaFile
 import com.squareup.javapoet.MethodSpec
 import com.squareup.javapoet.ParameterizedTypeName
 import com.squareup.javapoet.TypeSpec
 import com.squareup.javapoet.WildcardTypeName
-import javax.annotation.processing.ProcessingEnvironment
 import javax.lang.model.element.Modifier
 
 /**
@@ -52,13 +54,13 @@
  * ```
  */
 internal class WorkerGenerator(
-    private val processingEnv: ProcessingEnvironment,
-    private val injectedWorker: WorkerElements
+    private val processingEnv: XProcessingEnv,
+    private val injectedWorker: WorkerElement
 ) {
     fun generate() {
         val assistedFactoryTypeSpec = TypeSpec.interfaceBuilder(injectedWorker.factoryClassName)
             .addOriginatingElement(injectedWorker.typeElement)
-            .addGeneratedAnnotation(processingEnv.elementUtils, processingEnv.sourceVersion)
+            .addGeneratedAnnotation(processingEnv)
             .addAnnotation(ClassNames.ASSISTED_FACTORY)
             .addModifiers(Modifier.PUBLIC)
             .addSuperinterface(injectedWorker.factorySuperTypeName)
@@ -69,7 +71,7 @@
 
         val hiltModuleTypeSpec = TypeSpec.interfaceBuilder(injectedWorker.moduleClassName)
             .addOriginatingElement(injectedWorker.typeElement)
-            .addGeneratedAnnotation(processingEnv.elementUtils, processingEnv.sourceVersion)
+            .addGeneratedAnnotation(processingEnv)
             .addAnnotation(ClassNames.MODULE)
             .addAnnotation(
                 AnnotationSpec.builder(ClassNames.INSTALL_IN)
diff --git a/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/work/WorkerStep.kt b/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/work/WorkerStep.kt
index d06abf9..a02521a 100644
--- a/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/work/WorkerStep.kt
+++ b/hilt/hilt-compiler/src/main/kotlin/androidx/hilt/work/WorkerStep.kt
@@ -16,99 +16,83 @@
 
 package androidx.hilt.work
 
-import androidx.hilt.AndroidXHiltProcessor
 import androidx.hilt.ClassNames
-import androidx.hilt.ext.hasAnnotation
-import com.google.auto.common.MoreElements
-import com.squareup.javapoet.TypeName
-import javax.annotation.processing.ProcessingEnvironment
-import javax.lang.model.element.Element
-import javax.lang.model.element.Modifier
-import javax.lang.model.element.NestingKind
-import javax.lang.model.element.TypeElement
-import javax.lang.model.util.ElementFilter
+import androidx.room.compiler.codegen.toJavaPoet
+import androidx.room.compiler.processing.XElement
+import androidx.room.compiler.processing.XProcessingEnv
+import androidx.room.compiler.processing.XProcessingStep
+import androidx.room.compiler.processing.XTypeElement
 import javax.tools.Diagnostic
 
 /**
  * Processing step that generates code enabling assisted injection of Workers using Hilt.
  */
-class WorkerStep(
-    private val processingEnv: ProcessingEnvironment
-) : AndroidXHiltProcessor.Step {
+class WorkerStep : XProcessingStep {
 
-    private val elements = processingEnv.elementUtils
-    private val types = processingEnv.typeUtils
-    private val messager = processingEnv.messager
+    override fun annotations() = setOf(ClassNames.HILT_WORKER.canonicalName())
 
-    override fun annotation() = ClassNames.HILT_WORKER.canonicalName()
-
-    override fun process(annotatedElements: Set<Element>) {
-        val parsedElements = mutableSetOf<TypeElement>()
-        annotatedElements.forEach { element ->
-            val typeElement = MoreElements.asType(element)
-            if (parsedElements.add(typeElement)) {
-                parse(typeElement)?.let { worker ->
-                    WorkerGenerator(
-                        processingEnv,
-                        worker
-                    ).generate()
-                }
-            }
-        }
+    override fun process(
+        env: XProcessingEnv,
+        elementsByAnnotation: Map<String, Set<XElement>>,
+        isLastRound: Boolean
+    ): Set<XElement> {
+        elementsByAnnotation[ClassNames.HILT_WORKER.canonicalName()]
+            ?.filterIsInstance<XTypeElement>()
+            ?.mapNotNull { element -> parse(env, element) }
+            ?.forEach { worker -> WorkerGenerator(env, worker).generate() }
+        return emptySet()
     }
 
-    private fun parse(typeElement: TypeElement): WorkerElements? {
+    private fun parse(env: XProcessingEnv, workerTypeElement: XTypeElement): WorkerElement? {
         var valid = true
 
-        if (elements.getTypeElement(ClassNames.WORKER_ASSISTED_FACTORY.toString()) == null) {
-            error(
+        if (env.findTypeElement(ClassNames.WORKER_ASSISTED_FACTORY) == null) {
+            env.error(
                 "To use @HiltWorker you must add the 'work' artifact. " +
                     "androidx.hilt:hilt-work:<version>"
             )
             valid = false
         }
 
-        if (!types.isSubtype(
-                typeElement.asType(),
-                elements.getTypeElement(ClassNames.LISTENABLE_WORKER.toString()).asType()
-            )
-        ) {
-            error(
+        val workerType = workerTypeElement.type
+        val listenableWorkerType = env.requireType(ClassNames.LISTENABLE_WORKER)
+        if (!listenableWorkerType.isAssignableFrom(workerType)) {
+            env.error(
                 "@HiltWorker is only supported on types that subclass " +
-                    "${ClassNames.LISTENABLE_WORKER}."
+                    "${ClassNames.LISTENABLE_WORKER}.",
+                workerTypeElement
             )
             valid = false
         }
 
-        val constructors = ElementFilter.constructorsIn(typeElement.enclosedElements).filter {
-            if (it.hasAnnotation(ClassNames.INJECT.canonicalName())) {
-                error(
+        val constructors = workerTypeElement.getConstructors().filter {
+            if (it.hasAnnotation(ClassNames.INJECT)) {
+                env.error(
                     "Worker constructor should be annotated with @AssistedInject instead of " +
-                        "@Inject."
+                        "@Inject.",
+                    it
                 )
                 valid = false
             }
-            it.hasAnnotation(ClassNames.ASSISTED_INJECT.canonicalName())
+            it.hasAnnotation(ClassNames.ASSISTED_INJECT)
         }
         if (constructors.size != 1) {
-            error(
+            env.error(
                 "@HiltWorker annotated class should contain exactly one @AssistedInject " +
                     "annotated constructor.",
-                typeElement
+                workerTypeElement
             )
             valid = false
         }
-        constructors.filter { it.modifiers.contains(Modifier.PRIVATE) }.forEach {
-            error("@AssistedInject annotated constructors must not be private.", it)
+        constructors.filter { it.isPrivate() }.forEach {
+            env.error("@AssistedInject annotated constructors must not be private.", it)
             valid = false
         }
 
-        if (typeElement.nestingKind == NestingKind.MEMBER &&
-            !typeElement.modifiers.contains(Modifier.STATIC)
-        ) {
-            error(
+        if (workerTypeElement.isNested() && !workerTypeElement.isStatic()) {
+            env.error(
                 "@HiltWorker may only be used on inner classes if they are static.",
-                typeElement
+                workerTypeElement
             )
             valid = false
         }
@@ -119,35 +103,40 @@
         var contextIndex = -1
         var workerParametersIndex = -1
         injectConstructor.parameters.forEachIndexed { index, param ->
-            if (TypeName.get(param.asType()) == ClassNames.CONTEXT) {
-                if (!param.hasAnnotation(ClassNames.ASSISTED.canonicalName())) {
-                    error("Missing @Assisted annotation in param '${param.simpleName}'.", param)
+            if (param.type.asTypeName().toJavaPoet() == ClassNames.CONTEXT) {
+                if (!param.hasAnnotation(ClassNames.ASSISTED)) {
+                    env.error("Missing @Assisted annotation in param '${param.name}'.", param)
                     valid = false
                 }
                 contextIndex = index
             }
-            if (TypeName.get(param.asType()) == ClassNames.WORKER_PARAMETERS) {
-                if (!param.hasAnnotation(ClassNames.ASSISTED.canonicalName())) {
-                    error("Missing @Assisted annotation in param '${param.simpleName}'.", param)
+            if (param.type.asTypeName().toJavaPoet() == ClassNames.WORKER_PARAMETERS) {
+                if (!param.hasAnnotation(ClassNames.ASSISTED)) {
+                    env.error("Missing @Assisted annotation in param '${param.name}'.", param)
                     valid = false
                 }
                 workerParametersIndex = index
             }
         }
         if (contextIndex > workerParametersIndex) {
-            error(
+            env.error(
                 "The 'Context' parameter must be declared before the 'WorkerParameters' in the " +
                     "@AssistedInject constructor of a @HiltWorker annotated class.",
                 injectConstructor
             )
+            valid = false
         }
 
         if (!valid) return null
 
-        return WorkerElements(typeElement, injectConstructor)
+        return WorkerElement(workerTypeElement, injectConstructor)
     }
 
-    private fun error(message: String, element: Element? = null) {
-        messager.printMessage(Diagnostic.Kind.ERROR, message, element)
+    private fun XProcessingEnv.error(message: String, element: XElement? = null) {
+        if (element != null) {
+            messager.printMessage(Diagnostic.Kind.ERROR, message, element)
+        } else {
+            messager.printMessage(Diagnostic.Kind.ERROR, message)
+        }
     }
 }
diff --git a/hilt/hilt-compiler/src/test/kotlin/androidx/hilt/testUtils.kt b/hilt/hilt-compiler/src/test/kotlin/androidx/hilt/ext/testUtils.kt
similarity index 68%
rename from hilt/hilt-compiler/src/test/kotlin/androidx/hilt/testUtils.kt
rename to hilt/hilt-compiler/src/test/kotlin/androidx/hilt/ext/testUtils.kt
index b24ad5e..722c714 100644
--- a/hilt/hilt-compiler/src/test/kotlin/androidx/hilt/testUtils.kt
+++ b/hilt/hilt-compiler/src/test/kotlin/androidx/hilt/ext/testUtils.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,13 +14,11 @@
  * limitations under the License.
  */
 
-package androidx.hilt
+package androidx.hilt.ext
 
-import com.google.testing.compile.Compiler
-import com.google.testing.compile.Compiler.javac
-import com.google.testing.compile.JavaFileObjects
+import androidx.hilt.ClassNames
+import androidx.room.compiler.processing.util.Source
 import java.io.File
-import javax.tools.JavaFileObject
 
 val GENERATED_TYPE = try {
     Class.forName("javax.annotation.processing.Generated")
@@ -55,11 +53,7 @@
     }
 }
 
-fun loadJavaSource(fileName: String, qName: String): JavaFileObject {
-    val contents = File("src/test/data/sources/$fileName").readText(Charsets.UTF_8)
-    return JavaFileObjects.forSourceString(qName, contents)
-}
-
-fun compiler(): Compiler = javac().withProcessors(AndroidXHiltProcessor())
-
-fun String.toJFO(qName: String) = JavaFileObjects.forSourceString(qName, this.trimIndent())
+fun loadJavaSource(fileName: String, qName: String) = Source.loadJavaSource(
+    file = File("src/test/data/sources/$fileName"),
+    qName = qName
+)
diff --git a/hilt/hilt-compiler/src/test/kotlin/androidx/hilt/work/WorkerGeneratorTest.kt b/hilt/hilt-compiler/src/test/kotlin/androidx/hilt/work/WorkerGeneratorTest.kt
index fc892a0..b9d87f4 100644
--- a/hilt/hilt-compiler/src/test/kotlin/androidx/hilt/work/WorkerGeneratorTest.kt
+++ b/hilt/hilt-compiler/src/test/kotlin/androidx/hilt/work/WorkerGeneratorTest.kt
@@ -16,12 +16,11 @@
 
 package androidx.hilt.work
 
-import androidx.hilt.GENERATED_ANNOTATION
-import androidx.hilt.GENERATED_TYPE
-import androidx.hilt.Sources
-import androidx.hilt.compiler
-import androidx.hilt.toJFO
-import com.google.testing.compile.CompilationSubject.assertThat
+import androidx.hilt.ext.GENERATED_ANNOTATION
+import androidx.hilt.ext.GENERATED_TYPE
+import androidx.hilt.ext.Sources
+import androidx.room.compiler.processing.util.Source
+import androidx.room.compiler.processing.util.runProcessorTest
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
@@ -31,111 +30,128 @@
 
     @Test
     fun verifyAssistedFactory_mixedArgs() {
-        val foo = """
-        package androidx.hilt.work.test;
+        val foo = Source.java(
+            "androidx.hilt.work.test.Foo",
+            """
+            package androidx.hilt.work.test;
 
-        public class Foo { }
-        """.toJFO("androidx.hilt.work.test.Foo")
+            public class Foo { }
+            """
+        )
 
-        val myWorker = """
-        package androidx.hilt.work.test;
+        val myWorker = Source.java(
+            "androidx.hilt.work.test.MyWorker",
+            """
+            package androidx.hilt.work.test;
 
-        import android.content.Context;
-        import androidx.hilt.work.HiltWorker;
-        import androidx.work.Worker;
-        import androidx.work.WorkerParameters;
-        import dagger.assisted.Assisted;
-        import dagger.assisted.AssistedInject;
-        import java.lang.String;
+            import android.content.Context;
+            import androidx.hilt.work.HiltWorker;
+            import androidx.work.Worker;
+            import androidx.work.WorkerParameters;
+            import dagger.assisted.Assisted;
+            import dagger.assisted.AssistedInject;
+            import java.lang.String;
 
-        @HiltWorker
-        class MyWorker extends Worker {
-            @AssistedInject
-            MyWorker(@Assisted Context context, @Assisted WorkerParameters params, String s,
-                    Foo f, long l) {
-                super(context, params);
+            @HiltWorker
+            class MyWorker extends Worker {
+                @AssistedInject
+                MyWorker(@Assisted Context context, @Assisted WorkerParameters params, String s,
+                        Foo f, long l) {
+                    super(context, params);
+                }
             }
-        }
-        """.toJFO("androidx.hilt.work.test.MyWorker")
+            """
+        )
 
-        val expected = """
-        package androidx.hilt.work.test;
+        val expected = Source.java(
+            "androidx.hilt.work.test.MyWorker_AssistedFactory",
+            """
+            package androidx.hilt.work.test;
 
-        import androidx.hilt.work.WorkerAssistedFactory;
-        import dagger.assisted.AssistedFactory;
-        import $GENERATED_TYPE;
+            import androidx.hilt.work.WorkerAssistedFactory;
+            import dagger.assisted.AssistedFactory;
+            import $GENERATED_TYPE;
 
-        $GENERATED_ANNOTATION
-        @AssistedFactory
-        public interface MyWorker_AssistedFactory extends WorkerAssistedFactory<MyWorker> {
-        }
-        """.toJFO("androidx.hilt.work.test.MyWorker_AssistedFactory")
+            $GENERATED_ANNOTATION
+            @AssistedFactory
+            public interface MyWorker_AssistedFactory extends WorkerAssistedFactory<MyWorker> {
+            }
+            """
+        )
 
-        val compilation = compiler()
-            .compile(
+        runProcessorTest(
+            sources = listOf(
                 foo, myWorker, Sources.LISTENABLE_WORKER, Sources.WORKER,
                 Sources.WORKER_PARAMETERS
-            )
-        assertThat(compilation).apply {
-            succeeded()
-            generatedSourceFile("androidx.hilt.work.test.MyWorker_AssistedFactory")
-                .hasSourceEquivalentTo(expected)
+            ),
+            createProcessingSteps = { listOf(WorkerStep()) }
+        ) {
+            it.generatedSource(expected)
         }
     }
 
     @Test
     fun verifyMultibindModule() {
-        val myWorker = """
-        package androidx.hilt.work.test;
+        val myWorker = Source.java(
+            "androidx.hilt.work.test.MyWorker",
+            """
+            package androidx.hilt.work.test;
 
-        import android.content.Context;
-        import androidx.hilt.work.HiltWorker;
-        import androidx.work.Worker;
-        import androidx.work.WorkerParameters;
-        import dagger.assisted.Assisted;
-        import dagger.assisted.AssistedInject;
+            import android.content.Context;
+            import androidx.hilt.work.HiltWorker;
+            import androidx.work.Worker;
+            import androidx.work.WorkerParameters;
+            import dagger.assisted.Assisted;
+            import dagger.assisted.AssistedInject;
 
-        @HiltWorker
-        class MyWorker extends Worker {
-            @AssistedInject
-            MyWorker(@Assisted Context context, @Assisted WorkerParameters params) {
-                super(context, params);
+            @HiltWorker
+            class MyWorker extends Worker {
+                @AssistedInject
+                MyWorker(@Assisted Context context, @Assisted WorkerParameters params) {
+                    super(context, params);
+                }
             }
-        }
-        """.toJFO("androidx.hilt.work.test.MyWorker")
+            """
+        )
 
-        val expected = """
-        package androidx.hilt.work.test;
+        val expected = Source.java(
+            "androidx.hilt.work.test.MyWorker_HiltModule",
+            """
+            package androidx.hilt.work.test;
 
-        import androidx.hilt.work.WorkerAssistedFactory;
-        import androidx.work.ListenableWorker;
-        import dagger.Binds;
-        import dagger.Module;
-        import dagger.hilt.InstallIn;
-        import dagger.hilt.codegen.OriginatingElement;
-        import dagger.hilt.components.SingletonComponent;
-        import dagger.multibindings.IntoMap;
-        import dagger.multibindings.StringKey;
-        import $GENERATED_TYPE;
+            import androidx.hilt.work.WorkerAssistedFactory;
+            import androidx.work.ListenableWorker;
+            import dagger.Binds;
+            import dagger.Module;
+            import dagger.hilt.InstallIn;
+            import dagger.hilt.codegen.OriginatingElement;
+            import dagger.hilt.components.SingletonComponent;
+            import dagger.multibindings.IntoMap;
+            import dagger.multibindings.StringKey;
+            import $GENERATED_TYPE;
 
-        $GENERATED_ANNOTATION
-        @Module
-        @InstallIn(SingletonComponent.class)
-        @OriginatingElement(topLevelClass = MyWorker.class)
-        public interface MyWorker_HiltModule {
-            @Binds
-            @IntoMap
-            @StringKey("androidx.hilt.work.test.MyWorker")
-            WorkerAssistedFactory<? extends ListenableWorker> bind(MyWorker_AssistedFactory factory)
-        }
-        """.toJFO("androidx.hilt.work.test.MyWorker_HiltModule")
+            $GENERATED_ANNOTATION
+            @Module
+            @InstallIn(SingletonComponent.class)
+            @OriginatingElement(
+                topLevelClass = MyWorker.class
+            )
+            public interface MyWorker_HiltModule {
+                @Binds
+                @IntoMap
+                @StringKey("androidx.hilt.work.test.MyWorker")
+                WorkerAssistedFactory<? extends ListenableWorker> bind(MyWorker_AssistedFactory factory);
+            }
+            """
+        )
 
-        val compilation = compiler()
-            .compile(myWorker, Sources.LISTENABLE_WORKER, Sources.WORKER, Sources.WORKER_PARAMETERS)
-        assertThat(compilation).apply {
-            succeeded()
-            generatedSourceFile("androidx.hilt.work.test.MyWorker_HiltModule")
-                .hasSourceEquivalentTo(expected)
+        runProcessorTest(
+            sources = listOf(
+                myWorker, Sources.LISTENABLE_WORKER, Sources.WORKER, Sources.WORKER_PARAMETERS
+            ),
+            createProcessingSteps = { listOf(WorkerStep()) }
+        ) {
+            it.generatedSource(expected)
         }
     }
 }
diff --git a/hilt/hilt-compiler/src/test/kotlin/androidx/hilt/work/WorkerStepTest.kt b/hilt/hilt-compiler/src/test/kotlin/androidx/hilt/work/WorkerStepTest.kt
index 5d7c63a..2c6022e 100644
--- a/hilt/hilt-compiler/src/test/kotlin/androidx/hilt/work/WorkerStepTest.kt
+++ b/hilt/hilt-compiler/src/test/kotlin/androidx/hilt/work/WorkerStepTest.kt
@@ -14,12 +14,14 @@
  * limitations under the License.
  */
 
+@file:OptIn(ExperimentalProcessingApi::class)
+
 package androidx.hilt.work
 
-import androidx.hilt.Sources
-import androidx.hilt.compiler
-import androidx.hilt.toJFO
-import com.google.testing.compile.CompilationSubject.assertThat
+import androidx.hilt.ext.Sources
+import androidx.room.compiler.processing.ExperimentalProcessingApi
+import androidx.room.compiler.processing.util.Source
+import androidx.room.compiler.processing.util.runProcessorTest
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
@@ -29,28 +31,33 @@
 
     @Test
     fun verifyEnclosingElementExtendsWorker() {
-        val myWorker = """
-        package androidx.hilt.work.test;
+        val myWorker = Source.java(
+            "androidx.hilt.work.test.MyWorker",
+            """
+            package androidx.hilt.work.test;
 
-        import android.content.Context;
-        import androidx.hilt.work.HiltWorker;
-        import androidx.work.WorkerParameters;
-        import dagger.assisted.Assisted;
-        import dagger.assisted.AssistedInject;
+            import android.content.Context;
+            import androidx.hilt.work.HiltWorker;
+            import androidx.work.WorkerParameters;
+            import dagger.assisted.Assisted;
+            import dagger.assisted.AssistedInject;
 
-        @HiltWorker
-        class MyWorker {
-            @AssistedInject
-            MyWorker(@Assisted Context context, @Assisted WorkerParameters params) { }
-        }
-        """.toJFO("androidx.hilt.work.work.MyWorker")
+            @HiltWorker
+            class MyWorker {
+                @AssistedInject
+                MyWorker(@Assisted Context context, @Assisted WorkerParameters params) { }
+            }
+            """
+        )
 
-        val compilation = compiler()
-            .compile(myWorker, Sources.LISTENABLE_WORKER, Sources.WORKER, Sources.WORKER_PARAMETERS)
-        assertThat(compilation).apply {
-            failed()
-            hadErrorCount(1)
-            hadErrorContainingMatch(
+        runProcessorTest(
+            sources = listOf(
+                myWorker, Sources.LISTENABLE_WORKER, Sources.WORKER, Sources.WORKER_PARAMETERS
+            ),
+            createProcessingSteps = { listOf(WorkerStep()) }
+        ) {
+            it.hasErrorCount(1)
+            it.hasErrorContaining(
                 "@HiltWorker is only supported on types that subclass " +
                     "androidx.work.ListenableWorker."
             )
@@ -59,37 +66,42 @@
 
     @Test
     fun verifySingleAnnotatedConstructor() {
-        val myWorker = """
-        package androidx.hilt.work.test;
+        val myWorker = Source.java(
+            "androidx.hilt.work.test.MyWorker",
+            """
+            package androidx.hilt.work.test;
 
-        import android.content.Context;
-        import androidx.hilt.work.HiltWorker;
-        import androidx.work.Worker;
-        import androidx.work.WorkerParameters;
-        import dagger.assisted.Assisted;
-        import dagger.assisted.AssistedInject;
-        import java.lang.String;
+            import android.content.Context;
+            import androidx.hilt.work.HiltWorker;
+            import androidx.work.Worker;
+            import androidx.work.WorkerParameters;
+            import dagger.assisted.Assisted;
+            import dagger.assisted.AssistedInject;
+            import java.lang.String;
 
-        @HiltWorker
-        class MyWorker extends Worker {
-            @AssistedInject
-            MyWorker(@Assisted Context context, @Assisted WorkerParameters params) {
-                super(context, params);
+            @HiltWorker
+            class MyWorker extends Worker {
+                @AssistedInject
+                MyWorker(@Assisted Context context, @Assisted WorkerParameters params) {
+                    super(context, params);
+                }
+
+                @AssistedInject
+                MyWorker(Context context, WorkerParameters params, String s) {
+                    super(context, params);
+                }
             }
+            """
+        )
 
-            @AssistedInject
-            MyWorker(Context context, WorkerParameters params, String s) {
-                super(context, params);
-            }
-        }
-        """.toJFO("androidx.hilt.work.test.MyWorker")
-
-        val compilation = compiler()
-            .compile(myWorker, Sources.LISTENABLE_WORKER, Sources.WORKER, Sources.WORKER_PARAMETERS)
-        assertThat(compilation).apply {
-            failed()
-            hadErrorCount(1)
-            hadErrorContainingMatch(
+        runProcessorTest(
+            sources = listOf(
+                myWorker, Sources.LISTENABLE_WORKER, Sources.WORKER, Sources.WORKER_PARAMETERS
+            ),
+            createProcessingSteps = { listOf(WorkerStep()) }
+        ) {
+            it.hasErrorCount(1)
+            it.hasErrorContaining(
                 "@HiltWorker annotated class should contain exactly one @AssistedInject " +
                     "annotated constructor."
             )
@@ -98,31 +110,36 @@
 
     @Test
     fun verifyNonPrivateConstructor() {
-        val myWorker = """
-        package androidx.hilt.work.test;
+        val myWorker = Source.java(
+            "androidx.hilt.work.test.MyWorker",
+            """
+            package androidx.hilt.work.test;
 
-        import android.content.Context;
-        import androidx.hilt.work.HiltWorker;
-        import androidx.work.Worker;
-        import androidx.work.WorkerParameters;
-        import dagger.assisted.Assisted;
-        import dagger.assisted.AssistedInject;
+            import android.content.Context;
+            import androidx.hilt.work.HiltWorker;
+            import androidx.work.Worker;
+            import androidx.work.WorkerParameters;
+            import dagger.assisted.Assisted;
+            import dagger.assisted.AssistedInject;
 
-        @HiltWorker
-        class MyWorker extends Worker {
-            @AssistedInject
-            private MyWorker(@Assisted Context context, @Assisted WorkerParameters params) {
-                super(context, params);
+            @HiltWorker
+            class MyWorker extends Worker {
+                @AssistedInject
+                private MyWorker(@Assisted Context context, @Assisted WorkerParameters params) {
+                    super(context, params);
+                }
             }
-        }
-        """.toJFO("androidx.hilt.work.test.MyWorker")
+            """
+        )
 
-        val compilation = compiler()
-            .compile(myWorker, Sources.LISTENABLE_WORKER, Sources.WORKER, Sources.WORKER_PARAMETERS)
-        assertThat(compilation).apply {
-            failed()
-            hadErrorCount(1)
-            hadErrorContainingMatch(
+        runProcessorTest(
+            sources = listOf(
+                myWorker, Sources.LISTENABLE_WORKER, Sources.WORKER, Sources.WORKER_PARAMETERS
+            ),
+            createProcessingSteps = { listOf(WorkerStep()) }
+        ) {
+            it.hasErrorCount(1)
+            it.hasErrorContaining(
                 "@AssistedInject annotated constructors must not be private."
             )
         }
@@ -130,33 +147,38 @@
 
     @Test
     fun verifyInnerClassIsStatic() {
-        val myWorker = """
-        package androidx.hilt.work.test;
+        val myWorker = Source.java(
+            "androidx.hilt.work.test.Outer",
+            """
+            package androidx.hilt.work.test;
 
-        import android.content.Context;
-        import androidx.hilt.work.HiltWorker;
-        import androidx.work.Worker;
-        import androidx.work.WorkerParameters;
-        import dagger.assisted.Assisted;
-        import dagger.assisted.AssistedInject;
+            import android.content.Context;
+            import androidx.hilt.work.HiltWorker;
+            import androidx.work.Worker;
+            import androidx.work.WorkerParameters;
+            import dagger.assisted.Assisted;
+            import dagger.assisted.AssistedInject;
 
-        class Outer {
-            @HiltWorker
-            class MyWorker extends Worker {
-                @AssistedInject
-                MyWorker(@Assisted Context context, @Assisted WorkerParameters params) {
-                    super(context, params);
+            class Outer {
+                @HiltWorker
+                class MyWorker extends Worker {
+                    @AssistedInject
+                    MyWorker(@Assisted Context context, @Assisted WorkerParameters params) {
+                        super(context, params);
+                    }
                 }
             }
-        }
-        """.toJFO("androidx.hilt.work.test.Outer")
+            """
+        )
 
-        val compilation = compiler()
-            .compile(myWorker, Sources.LISTENABLE_WORKER, Sources.WORKER, Sources.WORKER_PARAMETERS)
-        assertThat(compilation).apply {
-            failed()
-            hadErrorCount(1)
-            hadErrorContainingMatch(
+        runProcessorTest(
+            sources = listOf(
+                myWorker, Sources.LISTENABLE_WORKER, Sources.WORKER, Sources.WORKER_PARAMETERS
+            ),
+            createProcessingSteps = { listOf(WorkerStep()) }
+        ) {
+            it.hasErrorCount(1)
+            it.hasErrorContaining(
                 "@HiltWorker may only be used on inner classes " +
                     "if they are static."
             )
@@ -165,32 +187,37 @@
 
     @Test
     fun verifyConstructorAnnotation() {
-        val myWorker = """
-        package androidx.hilt.work.test;
+        val myWorker = Source.java(
+            "androidx.hilt.work.test.MyWorker",
+            """
+            package androidx.hilt.work.test;
 
-        import android.content.Context;
-        import androidx.hilt.work.HiltWorker;
-        import androidx.work.Worker;
-        import androidx.work.WorkerParameters;
-        import dagger.assisted.Assisted;
-        import dagger.assisted.AssistedInject;
-        import java.lang.String;
-        import javax.inject.Inject;
+            import android.content.Context;
+            import androidx.hilt.work.HiltWorker;
+            import androidx.work.Worker;
+            import androidx.work.WorkerParameters;
+            import dagger.assisted.Assisted;
+            import dagger.assisted.AssistedInject;
+            import java.lang.String;
+            import javax.inject.Inject;
 
-        @HiltWorker
-        class MyWorker extends Worker {
-            @Inject
-            MyWorker(@Assisted Context context, @Assisted WorkerParameters params) {
-                super(context, params);
+            @HiltWorker
+            class MyWorker extends Worker {
+                @Inject
+                MyWorker(@Assisted Context context, @Assisted WorkerParameters params) {
+                    super(context, params);
+                }
             }
-        }
-        """.toJFO("androidx.hilt.work.test.MyWorker")
+            """
+        )
 
-        val compilation = compiler()
-            .compile(myWorker, Sources.LISTENABLE_WORKER, Sources.WORKER, Sources.WORKER_PARAMETERS)
-        assertThat(compilation).apply {
-            failed()
-            hadErrorContainingMatch(
+        runProcessorTest(
+            sources = listOf(
+                myWorker, Sources.LISTENABLE_WORKER, Sources.WORKER, Sources.WORKER_PARAMETERS
+            ),
+            createProcessingSteps = { listOf(WorkerStep()) }
+        ) {
+            it.hasErrorContaining(
                 "Worker constructor should be annotated with @AssistedInject instead of @Inject."
             )
         }
@@ -198,31 +225,36 @@
 
     @Test
     fun verifyAssistedParamOrder() {
-        val myWorker = """
-        package androidx.hilt.work.test;
+        val myWorker = Source.java(
+            "androidx.hilt.work.test.MyWorker",
+            """
+            package androidx.hilt.work.test;
 
-        import android.content.Context;
-        import androidx.hilt.work.HiltWorker;
-        import androidx.work.Worker;
-        import androidx.work.WorkerParameters;
-        import dagger.assisted.Assisted;
-        import dagger.assisted.AssistedInject;
-        import java.lang.String;
+            import android.content.Context;
+            import androidx.hilt.work.HiltWorker;
+            import androidx.work.Worker;
+            import androidx.work.WorkerParameters;
+            import dagger.assisted.Assisted;
+            import dagger.assisted.AssistedInject;
+            import java.lang.String;
 
-        @HiltWorker
-        class MyWorker extends Worker {
-            @AssistedInject
-            MyWorker(@Assisted WorkerParameters params, @Assisted Context context) {
-                super(context, params);
+            @HiltWorker
+            class MyWorker extends Worker {
+                @AssistedInject
+                MyWorker(@Assisted WorkerParameters params, @Assisted Context context) {
+                    super(context, params);
+                }
             }
-        }
-        """.toJFO("androidx.hilt.work.test.MyWorker")
+            """
+        )
 
-        val compilation = compiler()
-            .compile(myWorker, Sources.LISTENABLE_WORKER, Sources.WORKER, Sources.WORKER_PARAMETERS)
-        assertThat(compilation).apply {
-            failed()
-            hadErrorContainingMatch(
+        runProcessorTest(
+            sources = listOf(
+                myWorker, Sources.LISTENABLE_WORKER, Sources.WORKER, Sources.WORKER_PARAMETERS
+            ),
+            createProcessingSteps = { listOf(WorkerStep()) }
+        ) {
+            it.hasErrorContaining(
                 "The 'Context' parameter must be declared before the 'WorkerParameters' in the " +
                     "@AssistedInject constructor of a @HiltWorker annotated class.",
             )
diff --git a/inspection/inspection-gradle-plugin/build.gradle b/inspection/inspection-gradle-plugin/build.gradle
index bddd9ff..2eab7a7 100644
--- a/inspection/inspection-gradle-plugin/build.gradle
+++ b/inspection/inspection-gradle-plugin/build.gradle
@@ -33,7 +33,7 @@
     implementation(gradleApi())
     implementation(libs.androidGradlePluginz)
     implementation(libs.kotlinStdlib)
-    implementation(libs.protobufGradlePluginz)
+    implementation(libs.protobufGradlePlugin)
     implementation(libs.shadow)
 
     testImplementation(project(":internal-testutils-gradle-plugin"))
diff --git a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/AGPExtensions.kt b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/AGPExtensions.kt
index 6d6a8e6..520e21d7 100644
--- a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/AGPExtensions.kt
+++ b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/AGPExtensions.kt
@@ -20,6 +20,8 @@
 import java.io.File
 import java.util.Locale
 import org.gradle.api.Project
+import org.gradle.api.file.Directory
+import org.gradle.api.provider.Provider
 
 internal fun Variant.taskName(baseName: String) =
     "$baseName${name.replaceFirstChar(Char::titlecase)}"
@@ -27,9 +29,8 @@
 internal fun Project.taskWorkingDir(
     variant: Variant,
     baseName: String
-): File {
-    val inspectionDir = File(project.buildDir, "androidx_inspection")
-    return File(File(inspectionDir, baseName), variant.name)
+): Provider<Directory> {
+    return layout.buildDirectory.dir("androidx_inspection/$baseName/${variant.name}")
 }
 
 // Functions below will be removed once registerGenerateProguardDetectionFileTask is migrated
diff --git a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/DexInspectorTask.kt b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/DexInspectorTask.kt
index 7d95e6b..e8edc46e 100644
--- a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/DexInspectorTask.kt
+++ b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/DexInspectorTask.kt
@@ -128,7 +128,8 @@
 ): TaskProvider<Copy> {
     return tasks.register(variant.taskName("unpackInspectorAAR"), Copy::class.java) {
         it.from(zipTree(variant.artifacts.get(SingleArtifact.AAR)))
-        it.destinationDir = taskWorkingDir(variant, "unpackedInspectorAAR")
+        // Remove .get().asFile once https://github.com/gradle/gradle/issues/25824 is fixed
+        it.destinationDir = taskWorkingDir(variant, "unpackedInspectorAAR").get().asFile
     }
 }
 
@@ -139,15 +140,14 @@
     jar: TaskProvider<out Jar>
 ): TaskProvider<Zip> {
     val name = jarName ?: "${project.name}.jar"
-    val out = File(taskWorkingDir(variant, "dexedInspector"), name)
+    val output = taskWorkingDir(variant, "dexedInspector").map { it.file(name) }
 
     val dex = tasks.register(variant.taskName("dexInspector"), DexInspectorTask::class.java) {
         it.minSdkVersion = extension.defaultConfig.minSdk!!
         it.setD8(extension.sdkDirectory, extension.buildToolsVersion)
         it.setAndroidJar(extension.sdkDirectory, extension.compileSdkVersion!!)
         it.jars.from(jar.get().archiveFile)
-        it.outputFile.set(out)
-        @Suppress("UnstableApiUsage")
+        it.outputFile.set(output)
         it.compileClasspath.from(
             variant.compileConfiguration.incoming.artifactView {
                 it.attributes {
@@ -163,11 +163,10 @@
 
     return tasks.register(variant.taskName("assembleInspectorJar"), Zip::class.java) {
         it.from(zipTree(jar.map { it.archiveFile }))
-        it.from(zipTree(out))
+        it.from(dex.map { zipTree(it.outputFile) })
         it.exclude("**/*.class")
         it.archiveFileName.set(name)
         it.destinationDirectory.set(taskWorkingDir(variant, "assembleInspectorJar"))
-        it.dependsOn(dex)
         it.includeEmptyDirs = false
     }
 }
diff --git a/kruth/kruth/api/current.txt b/kruth/kruth/api/current.txt
index 2208eb3..fda56c4 100644
--- a/kruth/kruth/api/current.txt
+++ b/kruth/kruth/api/current.txt
@@ -89,6 +89,8 @@
   }
 
   public final class MapSubject<K, V> extends androidx.kruth.Subject<java.util.Map<K,? extends V>> {
+    method public androidx.kruth.Ordered containsAtLeast(kotlin.Pair<? extends K,? extends V>... entries);
+    method public androidx.kruth.Ordered containsAtLeastEntriesIn(java.util.Map<K,? extends V> expectedMap);
     method public androidx.kruth.Ordered containsExactly(kotlin.Pair<? extends K,? extends V>... entries);
     method public androidx.kruth.Ordered containsExactlyEntriesIn(java.util.Map<K,? extends V> expectedMap);
     method public void containsKey(Object? key);
@@ -161,6 +163,7 @@
   }
 
   public final class ThrowableSubject<T extends java.lang.Throwable> extends androidx.kruth.Subject<T> {
+    method public androidx.kruth.ThrowableSubject<java.lang.Throwable> hasCauseThat();
     method public androidx.kruth.StringSubject hasMessageThat();
   }
 
diff --git a/kruth/kruth/api/restricted_current.txt b/kruth/kruth/api/restricted_current.txt
index 380eaee..3e7a042 100644
--- a/kruth/kruth/api/restricted_current.txt
+++ b/kruth/kruth/api/restricted_current.txt
@@ -89,6 +89,8 @@
   }
 
   public final class MapSubject<K, V> extends androidx.kruth.Subject<java.util.Map<K,? extends V>> {
+    method public androidx.kruth.Ordered containsAtLeast(kotlin.Pair<? extends K,? extends V>... entries);
+    method public androidx.kruth.Ordered containsAtLeastEntriesIn(java.util.Map<K,? extends V> expectedMap);
     method public androidx.kruth.Ordered containsExactly(kotlin.Pair<? extends K,? extends V>... entries);
     method public androidx.kruth.Ordered containsExactlyEntriesIn(java.util.Map<K,? extends V> expectedMap);
     method public void containsKey(Object? key);
@@ -162,6 +164,7 @@
   }
 
   public final class ThrowableSubject<T extends java.lang.Throwable> extends androidx.kruth.Subject<T> {
+    method public androidx.kruth.ThrowableSubject<java.lang.Throwable> hasCauseThat();
     method public androidx.kruth.StringSubject hasMessageThat();
   }
 
diff --git a/kruth/kruth/src/commonMain/kotlin/androidx/kruth/MapSubject.kt b/kruth/kruth/src/commonMain/kotlin/androidx/kruth/MapSubject.kt
index aaa165c..fa369ff 100644
--- a/kruth/kruth/src/commonMain/kotlin/androidx/kruth/MapSubject.kt
+++ b/kruth/kruth/src/commonMain/kotlin/androidx/kruth/MapSubject.kt
@@ -67,6 +67,18 @@
             )
         )
 
+    /**
+     * Fails if the map does not contain at least the given set of key/value pairs. The arguments
+     * must not contain duplicate keys.
+     */
+    fun containsAtLeast(vararg entries: Pair<K, V>): Ordered =
+        containsAtLeastEntriesIn(
+            accumulateMap(
+                functionName = "containsAtLeast",
+                entries = entries.toList(),
+            )
+        )
+
     /** Fails if the map does not contain exactly the given set of entries in the given map. */
     fun containsExactlyEntriesIn(expectedMap: Map<K, V>): Ordered {
         requireNonNull(actual) { "Expected $expectedMap, but was null" }
@@ -84,6 +96,17 @@
         return MapInOrder(expectedMap = expectedMap, allowUnexpected = false)
     }
 
+    /** Fails if the map does not contain at least the given set of entries in the given map.  */
+    fun containsAtLeastEntriesIn(expectedMap: Map<K, V>): Ordered {
+        if (expectedMap.isEmpty()) {
+            return NoopOrdered
+        }
+
+        containsEntriesInAnyOrder(expectedMap = expectedMap, allowUnexpected = true)
+
+        return MapInOrder(expectedMap = expectedMap, allowUnexpected = true)
+    }
+
     private fun containsEntriesInAnyOrder(
         expectedMap: Map<K, V>,
         allowUnexpected: Boolean,
diff --git a/kruth/kruth/src/commonMain/kotlin/androidx/kruth/ThrowableSubject.kt b/kruth/kruth/src/commonMain/kotlin/androidx/kruth/ThrowableSubject.kt
index f613adf..a7d06c1 100644
--- a/kruth/kruth/src/commonMain/kotlin/androidx/kruth/ThrowableSubject.kt
+++ b/kruth/kruth/src/commonMain/kotlin/androidx/kruth/ThrowableSubject.kt
@@ -19,7 +19,7 @@
 /**
  * Propositions for [Throwable] subjects.
  */
-class ThrowableSubject<T : Throwable> internal constructor(
+class ThrowableSubject<out T : Throwable> internal constructor(
     actual: T?,
     private val metadata: FailureMetadata = FailureMetadata(),
 ) : Subject<T>(actual = actual, metadata = metadata) {
@@ -30,4 +30,17 @@
     fun hasMessageThat(): StringSubject {
         return StringSubject(actual = actual?.message, metadata = metadata)
     }
+
+    /**
+     * Returns a new [ThrowableSubject] that supports assertions on this [Throwable]'s direct
+     * cause. This method can be invoked repeatedly (e.g.
+     * `assertThat(e).hasCauseThat().hasCauseThat()....` to assert on a particular indirect cause.
+     */
+    fun hasCauseThat(): ThrowableSubject<Throwable> {
+        if (actual == null) {
+            asserter.fail("Causal chain is not deep enough - add a .isNotNull() check?")
+        }
+
+        return ThrowableSubject(actual = actual.cause, metadata = metadata)
+    }
 }
diff --git a/kruth/kruth/src/commonTest/kotlin/androidx/kruth/MapSubjectTest.kt b/kruth/kruth/src/commonTest/kotlin/androidx/kruth/MapSubjectTest.kt
index e1ddf10..f380fc7 100644
--- a/kruth/kruth/src/commonTest/kotlin/androidx/kruth/MapSubjectTest.kt
+++ b/kruth/kruth/src/commonTest/kotlin/androidx/kruth/MapSubjectTest.kt
@@ -220,6 +220,155 @@
     }
 
     @Test
+    fun containsAtLeastWithNullKey() {
+        val actual =
+            mapOf(
+                null to "value",
+                "unexpectedKey" to "unexpectedValue",
+            )
+
+        val expected = mapOf<String?, String>(null to "value")
+
+        assertThat(actual).containsAtLeast(null to "value")
+        assertThat(actual).containsAtLeast(null to "value").inOrder()
+        assertThat(actual).containsAtLeastEntriesIn(expected)
+        assertThat(actual).containsAtLeastEntriesIn(expected).inOrder()
+    }
+
+    @Test
+    fun containsAtLeastWithNullValue() {
+        val actual =
+            mapOf(
+                "key" to null,
+                "unexpectedKey" to "unexpectedValue",
+            )
+
+        val expected = mapOf<String, String?>("key" to null)
+
+        assertThat(actual).containsAtLeast("key" to null)
+        assertThat(actual).containsAtLeast("key" to null).inOrder()
+        assertThat(actual).containsAtLeastEntriesIn(expected)
+        assertThat(actual).containsAtLeastEntriesIn(expected).inOrder()
+    }
+
+    @Test
+    fun containsAtLeastEmpty() {
+        val actual = mapOf("key" to 1)
+        assertThat(actual).containsAtLeastEntriesIn(emptyMap())
+        assertThat(actual).containsAtLeastEntriesIn(emptyMap()).inOrder()
+    }
+
+    @Test
+    fun containsAtLeastOneEntry() {
+        val actual = mapOf("jan" to 1)
+        assertThat(actual).containsAtLeast("jan" to 1)
+        assertThat(actual).containsAtLeast("jan" to 1).inOrder()
+        assertThat(actual).containsAtLeastEntriesIn(actual)
+        assertThat(actual).containsAtLeastEntriesIn(actual).inOrder()
+    }
+
+    @Test
+    fun containsAtLeastMultipleEntries() {
+        val actual = mapOf("jan" to 1, "feb" to 2, "mar" to 3, "apr" to 4)
+        assertThat(actual).containsAtLeast("apr" to 4, "jan" to 1, "feb" to 2)
+        assertThat(actual).containsAtLeast("jan" to 1, "feb" to 2, "apr" to 4).inOrder()
+        assertThat(actual).containsAtLeastEntriesIn(mapOf("apr" to 4, "jan" to 1, "feb" to 2))
+        assertThat(actual).containsAtLeastEntriesIn(actual).inOrder()
+    }
+
+    @Test
+    fun containsAtLeastDuplicateKeys() {
+        val actual = mapOf("jan" to 1, "feb" to 2, "march" to 3)
+        try {
+            assertThat(actual).containsAtLeast("jan" to 1, "jan" to 2, "jan" to 3)
+            fail("Expected IllegalArgumentException")
+        } catch (expected: IllegalArgumentException) {
+            assertThat(expected)
+                .hasMessageThat()
+                .isEqualTo("Duplicate keys ([jan x 3]) cannot be passed to containsAtLeast().")
+        }
+    }
+
+    @Test
+    fun containsAtLeastMultipleDuplicateKeys() {
+        val actual = mapOf("jan" to 1, "feb" to 2, "march" to 3)
+        try {
+            assertThat(actual).containsAtLeast("jan" to 1, "jan" to 1, "feb" to 2, "feb" to 2)
+            fail("Expected IllegalArgumentException")
+        } catch (expected: IllegalArgumentException) {
+            assertThat(expected)
+                .hasMessageThat()
+                .isEqualTo(
+                    "Duplicate keys ([jan x 2, feb x 2]) cannot be passed to containsAtLeast()."
+                )
+        }
+    }
+
+    @Test
+    fun containsAtLeastMissingKey() {
+        val actual = mapOf("jan" to 1, "feb" to 2)
+        assertFailsWith<AssertionError> {
+            assertThat(actual).containsAtLeast("jan" to 1, "march" to 3)
+        }
+    }
+
+    @Test
+    fun containsAtLeastWrongValue() {
+        val actual = mapOf("jan" to 1, "feb" to 2, "march" to 3)
+        assertFailsWith<AssertionError> {
+            assertThat(actual).containsAtLeast("jan" to 1, "march" to 33)
+        }
+    }
+
+    @Test
+    fun containsAtLeastWrongValueWithNull() {
+        // Test for https://github.com/google/truth/issues/468
+        val actual = mapOf<String, Int?>("jan" to 1, "feb" to 2, "march" to 3)
+        assertFailsWith<AssertionError> {
+            assertThat(actual).containsAtLeast("jan" to 1, "march" to null)
+        }
+    }
+
+    @Test
+    fun containsAtLeastExtraKeyAndMissingKeyAndWrongValue() {
+        val actual = mapOf("jan" to 1, "march" to 3)
+        assertFailsWith<AssertionError> {
+            assertThat(actual).containsAtLeast("march" to 33, "feb" to 2)
+        }
+    }
+
+    @Test
+    fun containsAtLeastNotInOrder() {
+        val actual = mapOf("jan" to 1, "feb" to 2, "march" to 3)
+        assertThat(actual).containsAtLeast("march" to 3, "feb" to 2)
+        assertFailsWith<AssertionError> {
+            assertThat(actual).containsAtLeast("march" to 3, "feb" to 2).inOrder()
+        }
+    }
+
+    @Test
+    fun containsAtLeastWrongValue_sameToStringForValues() {
+        assertFailsWith<AssertionError> {
+            assertThat(mapOf<String, Any>("jan" to 1L, "feb" to 2L, "mar" to 3L))
+                .containsAtLeast("jan" to 1, "feb" to 2)
+        }
+    }
+
+    @Test
+    fun containsAtLeastWrongValue_sameToStringForKeys() {
+        assertFailsWith<AssertionError> {
+            assertThat(mapOf(1L to "jan", 1 to "feb")).containsAtLeast(1 to "jan", 1L to "feb")
+        }
+    }
+
+    @Test
+    fun containsAtLeastExtraKeyAndMissingKey_failsWithSameToStringForKeys() {
+        assertFailsWith<AssertionError> {
+            assertThat(mapOf(1L to "jan", 2 to "feb")).containsAtLeast(1 to "jan", 2 to "feb")
+        }
+    }
+
+    @Test
     fun isEmpty() {
         assertThat(mapOf<Any, Any>()).isEmpty()
     }
diff --git a/kruth/kruth/src/commonTest/kotlin/androidx/kruth/ThrowableSubjectTest.kt b/kruth/kruth/src/commonTest/kotlin/androidx/kruth/ThrowableSubjectTest.kt
index e14c7373..5c5df55 100644
--- a/kruth/kruth/src/commonTest/kotlin/androidx/kruth/ThrowableSubjectTest.kt
+++ b/kruth/kruth/src/commonTest/kotlin/androidx/kruth/ThrowableSubjectTest.kt
@@ -66,4 +66,60 @@
             assertThat(npe).hasMessageThat().isEqualTo("message")
         }
     }
+
+    @Test
+    fun hasCauseThat_message() {
+        assertThat(Exception("foobar", IllegalStateException("barfoo")))
+            .hasCauseThat()
+            .hasMessageThat()
+            .isEqualTo("barfoo")
+    }
+
+    @Test
+    fun hasCauseThat_instanceOf() {
+        assertThat(Exception("foobar", IllegalStateException("barfoo")))
+            .hasCauseThat()
+            .isInstanceOf<IllegalStateException>()
+    }
+
+    @Test
+    fun hasCauseThat_null() {
+        assertThat(Exception("foobar")).hasCauseThat().isNull()
+    }
+
+    @Test
+    fun hasCauseThat_message_failure() {
+        val actual = Exception("foobar", IllegalStateException("barfoo"))
+        assertFailsWith<AssertionError> {
+            assertThat(actual).hasCauseThat().hasMessageThat().isEqualTo("message")
+        }
+    }
+
+    @Test
+    fun hasCauseThat_instanceOf_failure() {
+        val actual = Exception("foobar", IllegalStateException("barfoo"))
+        assertFailsWith<AssertionError> {
+            assertThat(actual).hasCauseThat().isInstanceOf<NullPointerException>()
+        }
+    }
+
+    @Test
+    fun hasCauseThat_tooDeep_failure() {
+        val actual = Exception("foobar")
+        assertFailsWith<AssertionError> {
+            assertThat(actual).hasCauseThat().hasCauseThat().isNull()
+        }
+    }
+
+    @Test
+    fun hasCauseThat_deepNull_failure() {
+        val actual = Exception("foobar", RuntimeException("barfoo", IllegalStateException("buzz")))
+        assertFailsWith<AssertionError> {
+            assertThat(actual)
+                .hasCauseThat()
+                .hasCauseThat()
+                .hasMessageThat()
+                .isEqualTo("message")
+        }
+    }
 }
diff --git a/leanback/leanback-grid/src/main/java/androidx/leanback/widget/GridLayoutManager.java b/leanback/leanback-grid/src/main/java/androidx/leanback/widget/GridLayoutManager.java
index ca7116d..f31e65c 100644
--- a/leanback/leanback-grid/src/main/java/androidx/leanback/widget/GridLayoutManager.java
+++ b/leanback/leanback-grid/src/main/java/androidx/leanback/widget/GridLayoutManager.java
@@ -25,6 +25,7 @@
 import android.content.Context;
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.media.AudioManager;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -461,6 +462,8 @@
     final SparseIntArray mPositionToRowInPostLayout = new SparseIntArray();
     int[] mDisappearingPositions;
 
+    AudioManager mAudioManager;
+
     RecyclerView.Recycler mRecycler;
 
     private static final Rect sTempRect = new Rect();
@@ -1238,6 +1241,14 @@
         return p.getOpticalTop(v) + p.getAlignY();
     }
 
+    AudioManager getAudioManager() {
+        if (mAudioManager == null) {
+            mAudioManager = (AudioManager) mBaseGridView.getContext()
+                .getSystemService(Context.AUDIO_SERVICE);
+        }
+        return mAudioManager;
+    }
+
     /**
      * Save Recycler and State for convenience.  Must be paired with leaveContext().
      */
@@ -2861,6 +2872,21 @@
                 mPendingMoveSmoothScroller.decreasePendingMoves();
             }
         }
+        int soundEffect;
+        if (mOrientation == HORIZONTAL) {
+            boolean rtl = getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL;
+            if (rtl) {
+                soundEffect = forward ? AudioManager.FX_FOCUS_NAVIGATION_LEFT :
+                        AudioManager.FX_FOCUS_NAVIGATION_RIGHT;
+            } else {
+                soundEffect = forward ? AudioManager.FX_FOCUS_NAVIGATION_RIGHT :
+                        AudioManager.FX_FOCUS_NAVIGATION_LEFT;
+            }
+        } else {
+            soundEffect = forward ? AudioManager.FX_FOCUS_NAVIGATION_DOWN :
+                    AudioManager.FX_FOCUS_NAVIGATION_UP;
+        }
+        getAudioManager().playSoundEffect(soundEffect);
     }
 
     @Override
diff --git a/leanback/leanback/build.gradle b/leanback/leanback/build.gradle
index d22458a..018ffd4 100644
--- a/leanback/leanback/build.gradle
+++ b/leanback/leanback/build.gradle
@@ -25,7 +25,6 @@
     // It can be removed if appcompat library is updated to a newer version
     implementation("androidx.vectordrawable:vectordrawable-animated:1.1.0")
 
-    androidTestImplementation(libs.kotlinStdlib)
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testCore)
     androidTestImplementation(libs.testRunner)
diff --git a/leanback/leanback/lint-baseline.xml b/leanback/leanback/lint-baseline.xml
index 667d89e..6b97ced 100644
--- a/leanback/leanback/lint-baseline.xml
+++ b/leanback/leanback/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.0.0-beta03" type="baseline" client="gradle" dependencies="false" name="AGP (8.0.0-beta03)" variant="all" version="8.0.0-beta03">
+<issues format="6" by="lint 8.1.0-beta05" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-beta05)" variant="all" version="8.1.0-beta05">
 
     <issue
         id="NewApi"
@@ -409,6 +409,15 @@
     <issue
         id="BanThreadSleep"
         message="Uses Thread.sleep()"
+        errorLine1="                Thread.sleep(100);"
+        errorLine2="                       ~~~~~">
+        <location
+            file="src/androidTest/java/androidx/leanback/widget/GridWidgetTest.java"/>
+    </issue>
+
+    <issue
+        id="BanThreadSleep"
+        message="Uses Thread.sleep()"
         errorLine1="        Thread.sleep(80);"
         errorLine2="               ~~~~~">
         <location
diff --git a/leanback/leanback/src/androidTest/java/androidx/leanback/widget/GridWidgetTest.java b/leanback/leanback/src/androidTest/java/androidx/leanback/widget/GridWidgetTest.java
index 5110a5f..4ede5d5 100644
--- a/leanback/leanback/src/androidTest/java/androidx/leanback/widget/GridWidgetTest.java
+++ b/leanback/leanback/src/androidTest/java/androidx/leanback/widget/GridWidgetTest.java
@@ -39,6 +39,7 @@
 import android.text.Selection;
 import android.text.Spannable;
 import android.util.DisplayMetrics;
+import android.util.Log;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 import android.util.TypedValue;
@@ -83,6 +84,7 @@
 @RunWith(AndroidJUnit4.class)
 public class GridWidgetTest {
 
+    private static final String TAG = "GridWidgetTest";
     private static final float DELTA = 1f;
     private static final boolean HUMAN_DELAY = false;
     private static final long WAIT_FOR_SCROLL_IDLE_TIMEOUT_MS = 60000;
@@ -92,6 +94,7 @@
     @Rule
     public final AnimationActivityTestRule<GridActivity> mActivityTestRule =
             new AnimationActivityTestRule<GridActivity>(GridActivity.class, false, false);;
+
     protected GridActivity mActivity;
     protected BaseGridView mGridView;
     protected GridLayoutManager mLayoutManager;
@@ -2137,6 +2140,132 @@
         verifyBeginAligned();
     }
 
+    @Ignore // This is a long running test that can take hours, improper for a presubmit check.
+    @Test
+    @AnimationTest
+    @SuppressWarnings("unchecked")
+    public void testRandomChangeAdapterAndScroll() throws Throwable {
+        // Randomly run adapter change and scrolling repeatedly, in order to capture a potential
+        // crash.
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.horizontal_linear);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        intent.putExtra(GridActivity.EXTRA_ITEMS, new int[] {});
+        initActivity(intent);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = 1;
+
+        ArrayObjectAdapter adapter = new ArrayObjectAdapter(new TestPresenter());
+        ItemBridgeAdapter itemBridgeAdapter = new ItemBridgeAdapter();
+        itemBridgeAdapter.setAdapter(adapter);
+        mActivityTestRule.runOnUiThread(
+                new Runnable() {
+                    @Override
+                    public void run() {
+                        // Setup a fixed size RV to match exactly the number of Views we want.
+                        TestPresenter.setupHorizontalGridView(mGridView);
+                        mGridView.setAdapter(itemBridgeAdapter);
+                    }
+                }
+        );
+
+        for (int i = 0; i < 10000; i++) {
+            final List<TestPresenter.Item>[] itemsHolder = (List<TestPresenter.Item>[]) new List[1];
+            itemsHolder[0] = TestPresenter.generateItems(0, 20);
+            mActivityTestRule.runOnUiThread(
+                    new Runnable() {
+                        @Override
+                        public void run() {
+                            Log.d(TAG, "Setup initial items: " + itemsHolder[0].toString());
+                            adapter.setItems(itemsHolder[0], TestPresenter.DIFF_CALLBACK);
+                        }
+                    }
+            );
+
+            for (int j = 0; j < 20; j++) {
+                // Either change adapter or scrolling
+                boolean changeAdapter = TestPresenter.randomBoolean();
+                if (changeAdapter) {
+                    itemsHolder[0] = TestPresenter.randomChange(itemsHolder[0]);
+                    mActivityTestRule.runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            Log.d(TAG, "Random change items: " + itemsHolder[0].toString());
+                            adapter.setItems(itemsHolder[0], TestPresenter.DIFF_CALLBACK);
+                        }
+                    });
+                } else {
+                    mActivityTestRule.runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            if (TestPresenter.randomBoolean()) {
+                                Log.d(TAG, "Scrolling to first item");
+                                mGridView.setSelectedPositionSmooth(0);
+                            } else {
+                                Log.d(TAG, "Scrolling to last item");
+                                mGridView.setSelectedPositionSmooth(itemsHolder[0].size() - 1);
+                            }
+                        }
+                    });
+                }
+                Thread.sleep(100);
+            }
+        }
+    }
+
+    @Ignore // wait b/292114537 to be fixed *and* update the recyclerview version in build.gradle.
+    @Test
+    @AnimationTest
+    @SuppressWarnings("unchecked")
+    public void testCrashOnRVChildHelperBug292114537() throws Throwable {
+        // see b/292114537
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.horizontal_linear);
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        intent.putExtra(GridActivity.EXTRA_ITEMS, new int[] {});
+        initActivity(intent);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = 1;
+
+        ArrayObjectAdapter adapter = new ArrayObjectAdapter(new TestPresenter());
+        ItemBridgeAdapter itemBridgeAdapter = new ItemBridgeAdapter();
+        itemBridgeAdapter.setAdapter(adapter);
+
+        mActivityTestRule.runOnUiThread(
+                new Runnable() {
+                    @Override
+                    public void run() {
+                        // Setup a fixed size RV to match exactly the number of Views we want.
+                        TestPresenter.setupHorizontalGridView(mGridView);
+                        mGridView.setAdapter(itemBridgeAdapter);
+                        mGridView.getItemAnimator().setAddDuration(1000);
+                        mGridView.getItemAnimator().setMoveDuration(1000);
+                        mGridView.getItemAnimator().setRemoveDuration(1000);
+                        adapter.setItems(
+                                TestPresenter.generateItems(new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8}),
+                                TestPresenter.DIFF_CALLBACK);
+                    }
+                }
+        );
+        // showing 0 1(selected) 2 3 4 5(peek)
+        setSelectedPosition(1);
+
+        // swap 5 and 6, showing 0 1(selected) 2 3 4 6(slide in) 5(slide out)
+        mActivityTestRule.runOnUiThread(() ->
+                adapter.setItems(TestPresenter.generateItems(new int[]{0, 1, 2, 3, 4, 6, 5, 7, 8}),
+                        TestPresenter.DIFF_CALLBACK));
+
+        // Wait a little bit for the ItemAnimation to be started
+        waitForItemAnimationStart();
+
+        // Scroll to 0 to remove "6", this can break ChildHelper and scroll to 2 may failed to find
+        // the View and crash.
+        setSelectedPosition(0);
+        setSelectedPosition(2);
+    }
+
     @Test
     public void testItemMovedHorizontalRtl() throws Throwable {
         Intent intent = new Intent();
diff --git a/leanback/leanback/src/androidTest/java/androidx/leanback/widget/TestPresenter.java b/leanback/leanback/src/androidTest/java/androidx/leanback/widget/TestPresenter.java
new file mode 100644
index 0000000..4b30e10
--- /dev/null
+++ b/leanback/leanback/src/androidTest/java/androidx/leanback/widget/TestPresenter.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.leanback.widget;
+
+import android.graphics.Color;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Random;
+
+final class TestPresenter extends Presenter {
+
+    static final class Item {
+        String mId;
+        String mDetail;
+
+        Item(String id, String detail) {
+            mId = id;
+            mDetail = detail;
+        }
+
+        @Override
+        public String toString() {
+            return mId + "- " + mDetail;
+        }
+    }
+
+    private static Random sRandom = new Random();
+
+    private static int sIdSeed = 100;
+
+    static Item generateItem(int id) {
+        Item item = new Item("" + id, "origin");
+        return item;
+    }
+
+    static List<Item> generateItems(int startId, int endId) {
+        List<Item> list = new ArrayList<>();
+        for (int i = startId; i < endId; i++) {
+            Item item = generateItem(i);
+            list.add(item);
+        }
+        return list;
+    }
+
+    static List<Item> generateItems(int[] ids) {
+        List<Item> list = new ArrayList<>();
+        for (int i : ids) {
+            Item item = new Item("" + i, "origin");
+            list.add(item);
+        }
+        return list;
+    }
+
+    static void setupHorizontalGridView(BaseGridView gridView) {
+        ViewGroup.LayoutParams layoutParams = gridView.getLayoutParams();
+        layoutParams.width = 1920;
+        layoutParams.height = 500;
+        gridView.setLayoutParams(layoutParams);
+        gridView.setPadding(116, 16, 116, 16);
+        gridView.setHorizontalSpacing(40);
+        gridView.setItemAlignmentOffsetPercent(0f);
+        gridView.setItemAlignmentOffset(0);
+        gridView.setWindowAlignmentOffsetPercent(0);
+        gridView.setWindowAlignmentOffset(116);
+    }
+
+    static boolean randomBoolean() {
+        return sRandom.nextInt(2) == 0;
+    }
+
+    static List<Item> randomChange(List<Item> items) {
+        List<Item> newList = new ArrayList<>();
+        // 70% old items are copied cover
+        for (int i = 0; i < items.size(); i++) {
+            if (sRandom.nextInt(10) < 7) {
+                newList.add(items.get(i));
+            }
+        }
+        // Randomly shift position <= 1/3 of the old items
+        int shiftCount = sRandom.nextInt(newList.size() / 3);
+        for (int i = 0; i < shiftCount; i++) {
+            int pos1 = sRandom.nextInt(newList.size());
+            Item item1 = newList.get(pos1);
+            int pos2 = sRandom.nextInt(newList.size());
+            Item item2 = newList.get(pos2);
+            newList.set(pos1, item2);
+            newList.set(pos2, item1);
+        }
+        // Insert new items into random positions
+        int newItemsCount = items.size() - newList.size();
+        for (int i = 0; i < newItemsCount; i++) {
+            Item item = generateItem(sIdSeed++);
+            newList.add(sRandom.nextInt(newList.size()), item);
+        }
+        return newList;
+    }
+
+    @NonNull
+    @Override
+    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent) {
+        TextView tv = new TextView(parent.getContext());
+        tv.setFocusable(true);
+        tv.setFocusableInTouchMode(true);
+        tv.setLayoutParams(new ViewGroup.LayoutParams(393, 221));
+        tv.setBackgroundColor(Color.LTGRAY);
+        tv.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+            @Override
+            public void onFocusChange(View v, boolean hasFocus) {
+                if (hasFocus) {
+                    v.setBackgroundColor(Color.YELLOW);
+                } else {
+                    v.setBackgroundColor(Color.LTGRAY);
+                }
+            }
+        });
+        return new ViewHolder(tv);
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull ViewHolder viewHolder, @Nullable Object item) {
+        ((TextView) viewHolder.view).setText(item.toString());
+    }
+
+    @Override
+    public void onUnbindViewHolder(@NonNull ViewHolder viewHolder) {
+    }
+
+    static final DiffCallback<Item> DIFF_CALLBACK =
+            new DiffCallback<Item>() {
+
+                @Override
+                public boolean areItemsTheSame(
+                        @NonNull Item oldItem, @NonNull Item newItem) {
+                    return Objects.equals(oldItem.mId, newItem.mId);
+                }
+
+                @Override
+                public boolean areContentsTheSame(
+                        @NonNull Item oldItem,
+                        @NonNull Item newItem) {
+                    return Objects.equals(oldItem.mDetail, newItem.mDetail);
+                }
+            };
+}
diff --git a/libraryversions.toml b/libraryversions.toml
index e9b0d77..a8392b0 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -9,18 +9,18 @@
 ARCH_CORE = "2.3.0-alpha01"
 ASYNCLAYOUTINFLATER = "1.1.0-alpha02"
 AUTOFILL = "1.3.0-alpha02"
-BENCHMARK = "1.2.0-alpha16"
+BENCHMARK = "1.2.0-beta01"
 BIOMETRIC = "1.2.0-alpha06"
 BLUETOOTH = "1.0.0-alpha01"
-BROWSER = "1.6.0-beta01"
+BROWSER = "1.6.0-rc01"
 BUILDSRC_TESTS = "1.0.0-alpha01"
-CAMERA = "1.3.0-beta01"
+CAMERA = "1.3.0-beta02"
 CAMERA_PIPE = "1.0.0-alpha01"
 CARDVIEW = "1.1.0-alpha01"
-CAR_APP = "1.4.0-alpha01"
+CAR_APP = "1.4.0-alpha02"
 COLLECTION = "1.3.0-alpha05"
-COMPOSE = "1.6.0-alpha01"
-COMPOSE_COMPILER = "1.4.8"
+COMPOSE = "1.6.0-alpha02"
+COMPOSE_COMPILER = "1.5.1"
 COMPOSE_MATERIAL3 = "1.2.0-alpha04"
 COMPOSE_MATERIAL3_ADAPTIVE = "1.0.0-alpha01"
 COMPOSE_RUNTIME_TRACING = "1.0.0-alpha03"
@@ -29,7 +29,7 @@
 CONSTRAINTLAYOUT_CORE = "1.1.0-alpha11"
 CONTENTPAGER = "1.1.0-alpha01"
 COORDINATORLAYOUT = "1.3.0-alpha01"
-CORE = "1.12.0-alpha06"
+CORE = "1.12.0-beta01"
 CORE_ANIMATION = "1.0.0-rc01"
 CORE_ANIMATION_TESTING = "1.0.0-rc01"
 CORE_APPDIGEST = "1.0.0-alpha01"
@@ -54,12 +54,12 @@
 DYNAMICANIMATION = "1.1.0-alpha04"
 DYNAMICANIMATION_KTX = "1.0.0-alpha04"
 EMOJI = "1.2.0-alpha03"
-EMOJI2 = "1.4.0-beta05"
+EMOJI2 = "1.4.0-rc01"
 ENTERPRISE = "1.1.0-rc01"
 EXIFINTERFACE = "1.4.0-alpha01"
 FRAGMENT = "1.7.0-alpha02"
-FUTURES = "1.2.0-alpha01"
-GLANCE = "1.0.0-rc01"
+FUTURES = "1.2.0-alpha02"
+GLANCE = "1.1.0-alpha01"
 GLANCE_PREVIEW = "1.0.0-alpha06"
 GLANCE_TEMPLATE = "1.0.0-alpha06"
 GLANCE_WEAR_TILES = "1.0.0-alpha06"
@@ -68,7 +68,7 @@
 GRAPHICS_FILTERS = "1.0.0-alpha01"
 GRAPHICS_SHAPES = "1.0.0-alpha03"
 GRIDLAYOUT = "1.1.0-beta02"
-HEALTH_CONNECT = "1.1.0-alpha02"
+HEALTH_CONNECT = "1.1.0-alpha03"
 HEALTH_SERVICES_CLIENT = "1.1.0-alpha01"
 HEIFWRITER = "1.1.0-alpha02"
 HILT = "1.1.0-alpha03"
@@ -92,7 +92,7 @@
 MEDIA2 = "1.3.0-alpha01"
 MEDIAROUTER = "1.6.0-beta01"
 METRICS = "1.0.0-alpha05"
-NAVIGATION = "2.7.0-beta02"
+NAVIGATION = "2.7.0-rc01"
 PAGING = "3.3.0-alpha01"
 PALETTE = "1.1.0-alpha01"
 PERCENTLAYOUT = "1.1.0-alpha01"
@@ -100,7 +100,7 @@
 PRINT = "1.1.0-beta01"
 PRIVACYSANDBOX_ADS = "1.1.0-alpha01"
 PRIVACYSANDBOX_PLUGINS = "1.0.0-alpha02"
-PRIVACYSANDBOX_SDKRUNTIME = "1.0.0-alpha06"
+PRIVACYSANDBOX_SDKRUNTIME = "1.0.0-alpha07"
 PRIVACYSANDBOX_TOOLS = "1.0.0-alpha05"
 PRIVACYSANDBOX_UI = "1.0.0-alpha04"
 PROFILEINSTALLER = "1.4.0-alpha01"
@@ -109,7 +109,7 @@
 RECYCLERVIEW_SELECTION = "1.2.0-alpha02"
 REMOTECALLBACK = "1.0.0-alpha02"
 RESOURCEINSPECTION = "1.1.0-alpha01"
-ROOM = "2.6.0-alpha02"
+ROOM = "2.6.0-alpha03"
 SAFEPARCEL = "1.0.0-alpha01"
 SAVEDSTATE = "1.3.0-alpha01"
 SECURITY = "1.1.0-alpha07"
@@ -123,7 +123,7 @@
 SLICE_BUILDERS_KTX = "1.0.0-alpha08"
 SLICE_REMOTECALLBACK = "1.0.0-alpha01"
 SLIDINGPANELAYOUT = "1.3.0-alpha01"
-SQLITE = "2.4.0-alpha02"
+SQLITE = "2.4.0-alpha03"
 SQLITE_INSPECTOR = "2.1.0-alpha01"
 STABLE_AIDL = "1.0.0-alpha01"
 STARTUP = "1.2.0-alpha03"
@@ -133,7 +133,7 @@
 TEST_UIAUTOMATOR = "2.3.0-alpha04"
 TEXT = "1.0.0-alpha01"
 TRACING = "1.3.0-alpha02"
-TRACING_PERFETTO = "1.0.0-alpha17"
+TRACING_PERFETTO = "1.0.0-beta01"
 TRANSITION = "1.5.0-alpha01"
 TV = "1.0.0-alpha08"
 TVPROVIDER = "1.1.0-alpha02"
@@ -144,18 +144,18 @@
 VIEWPAGER = "1.1.0-alpha02"
 VIEWPAGER2 = "1.1.0-beta03"
 WEAR = "1.3.0-rc01"
-WEAR_COMPOSE = "1.3.0-alpha01"
-WEAR_COMPOSE_MATERIAL3 = "1.0.0-alpha07"
+WEAR_COMPOSE = "1.3.0-alpha02"
+WEAR_COMPOSE_MATERIAL3 = "1.0.0-alpha08"
 WEAR_INPUT = "1.2.0-alpha03"
 WEAR_INPUT_TESTING = "1.2.0-alpha03"
 WEAR_ONGOING = "1.1.0-alpha01"
 WEAR_PHONE_INTERACTIONS = "1.1.0-alpha04"
-WEAR_PROTOLAYOUT = "1.0.0-beta01"
+WEAR_PROTOLAYOUT = "1.0.0-rc01"
 WEAR_REMOTE_INTERACTIONS = "1.1.0-alpha01"
-WEAR_TILES = "1.2.0-beta01"
+WEAR_TILES = "1.2.0-rc01"
 WEAR_WATCHFACE = "1.2.0-alpha09"
-WEBKIT = "1.8.0-alpha01"
-WINDOW = "1.2.0-alpha03"
+WEBKIT = "1.8.0-beta01"
+WINDOW = "1.2.0-beta01"
 WINDOW_EXTENSIONS = "1.2.0-beta01"
 WINDOW_EXTENSIONS_CORE = "1.1.0-alpha01"
 WINDOW_SIDECAR = "1.0.0-rc01"
@@ -266,6 +266,7 @@
 TEST_UIAUTOMATOR = { group = "androidx.test.uiautomator", atomicGroupVersion = "versions.TEST_UIAUTOMATOR" }
 TEXT = { group = "androidx.text", atomicGroupVersion = "versions.TEXT" }
 TRACING = { group = "androidx.tracing", atomicGroupVersion = "versions.TRACING" }
+TRACING_PERFETTO = { group = "androidx.tracing", atomicGroupVersion = "versions.TRACING_PERFETTO", overrideInclude = [ ":tracing:tracing-perfetto", ":tracing:tracing-perfetto-binary", ":tracing:tracing-perfetto-handshake" ] }
 TRANSITION = { group = "androidx.transition", atomicGroupVersion = "versions.TRANSITION" }
 TV = { group = "androidx.tv", atomicGroupVersion = "versions.TV" }
 TVPROVIDER = { group = "androidx.tvprovider", atomicGroupVersion = "versions.TVPROVIDER" }
diff --git a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Base.java b/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Base.kt
similarity index 74%
rename from lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Base.java
rename to lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Base.kt
index fb21268..6118451 100644
--- a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Base.java
+++ b/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Base.kt
@@ -14,15 +14,13 @@
  * limitations under the License.
  */
 
-package androidx.lifecycle.observers;
+package androidx.lifecycle.observers
 
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleObserver
 
-@SuppressWarnings("deprecation")
-public class Base implements LifecycleObserver {
-
+open class Base : LifecycleObserver {
+    @Suppress("DEPRECATION")
     @androidx.lifecycle.OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
-    public void onCreate() {
-    }
+    open fun onCreate() {}
 }
diff --git a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Base_LifecycleAdapter.java b/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Base_LifecycleAdapter.java
deleted file mode 100644
index d3eacca..0000000
--- a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Base_LifecycleAdapter.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2017 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.lifecycle.observers;
-
-import androidx.annotation.NonNull;
-import androidx.lifecycle.GeneratedAdapter;
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.lifecycle.MethodCallsLogger;
-
-public class Base_LifecycleAdapter implements GeneratedAdapter {
-
-    public Base_LifecycleAdapter(Base base) {
-    }
-
-    @Override
-    public void callMethods(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event,
-            boolean onAny, MethodCallsLogger logger) {
-
-    }
-}
diff --git a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Base_LifecycleAdapter.kt b/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Base_LifecycleAdapter.kt
new file mode 100644
index 0000000..d7c69e4
--- /dev/null
+++ b/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Base_LifecycleAdapter.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 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.lifecycle.observers
+
+import androidx.lifecycle.GeneratedAdapter
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.MethodCallsLogger
+
+@Suppress("UNUSED", "UNUSED_PARAMETER")
+class Base_LifecycleAdapter(base: Base) : GeneratedAdapter {
+    override fun callMethods(
+        source: LifecycleOwner,
+        event: Lifecycle.Event,
+        onAny: Boolean,
+        logger: MethodCallsLogger?
+    ) {}
+}
diff --git a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence1.java b/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence1.kt
similarity index 83%
rename from lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence1.java
rename to lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence1.kt
index ae9c85f..221cf8e 100644
--- a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence1.java
+++ b/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence1.kt
@@ -13,11 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package androidx.lifecycle.observers
 
-package androidx.lifecycle.observers;
-
-public class DerivedSequence1 extends Base {
-
-    public void something() {
-    }
+open class DerivedSequence1 : Base() {
+    fun something() {}
 }
diff --git a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence2.java b/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence2.kt
similarity index 77%
rename from lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence2.java
rename to lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence2.kt
index aeb87da..d9937cd 100644
--- a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence2.java
+++ b/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence2.kt
@@ -14,15 +14,12 @@
  * limitations under the License.
  */
 
-package androidx.lifecycle.observers;
+package androidx.lifecycle.observers
 
-import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.Lifecycle
 
-@SuppressWarnings("deprecation")
-public class DerivedSequence2 extends DerivedSequence1 {
-
+@Suppress("DEPRECATION")
+class DerivedSequence2 : DerivedSequence1() {
     @androidx.lifecycle.OnLifecycleEvent(Lifecycle.Event.ON_STOP)
-    void onStop() {
-
-    }
+    fun onStop() {}
 }
diff --git a/lifecycle/lifecycle-runtime/src/androidTest/java/androidx/lifecycle/ViewTreeLifecycleOwnerTest.java b/lifecycle/lifecycle-runtime/src/androidTest/java/androidx/lifecycle/ViewTreeLifecycleOwnerTest.java
deleted file mode 100644
index 2056c0f..0000000
--- a/lifecycle/lifecycle-runtime/src/androidTest/java/androidx/lifecycle/ViewTreeLifecycleOwnerTest.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.lifecycle;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-
-import android.content.Context;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import androidx.annotation.NonNull;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class ViewTreeLifecycleOwnerTest {
-    /**
-     * Tests that a direct set/get on a single view survives a round trip
-     */
-    @Test
-    public void setGetSameView() {
-        final View v = new View(InstrumentationRegistry.getInstrumentation().getContext());
-        final LifecycleOwner fakeOwner = new FakeLifecycleOwner();
-
-        assertNull("initial LifecycleOwner expects null", ViewTreeLifecycleOwner.get(v));
-
-        ViewTreeLifecycleOwner.set(v, fakeOwner);
-
-        assertEquals("get the LifecycleOwner set directly", fakeOwner,
-                ViewTreeLifecycleOwner.get(v));
-    }
-
-    /**
-     * Tests that the owner set on a root of a subhierarchy is seen by both direct children
-     * and other descendants
-     */
-    @Test
-    public void getAncestorOwner() {
-        final Context context = InstrumentationRegistry.getInstrumentation().getContext();
-        final ViewGroup root = new FrameLayout(context);
-        final ViewGroup parent = new FrameLayout(context);
-        final View child = new View(context);
-        root.addView(parent);
-        parent.addView(child);
-
-        assertNull("initial LifecycleOwner expects null", ViewTreeLifecycleOwner.get(child));
-
-        final LifecycleOwner fakeOwner = new FakeLifecycleOwner();
-        ViewTreeLifecycleOwner.set(root, fakeOwner);
-
-        assertEquals("root sees owner", fakeOwner, ViewTreeLifecycleOwner.get(root));
-        assertEquals("direct child sees owner", fakeOwner, ViewTreeLifecycleOwner.get(parent));
-        assertEquals("grandchild sees owner", fakeOwner, ViewTreeLifecycleOwner.get(child));
-    }
-
-    /**
-     * Tests that a new owner set between a root and a descendant is seen by the descendant
-     * instead of the root value
-     */
-    @Test
-    public void shadowedOwner() {
-        final Context context = InstrumentationRegistry.getInstrumentation().getContext();
-        final ViewGroup root = new FrameLayout(context);
-        final ViewGroup parent = new FrameLayout(context);
-        final View child = new View(context);
-        root.addView(parent);
-        parent.addView(child);
-
-        assertNull("initial LifecycleOwner expects null", ViewTreeLifecycleOwner.get(child));
-
-        final LifecycleOwner rootFakeOwner = new FakeLifecycleOwner();
-        ViewTreeLifecycleOwner.set(root, rootFakeOwner);
-
-        final LifecycleOwner parentFakeOwner = new FakeLifecycleOwner();
-        ViewTreeLifecycleOwner.set(parent, parentFakeOwner);
-
-        assertEquals("root sees owner", rootFakeOwner, ViewTreeLifecycleOwner.get(root));
-        assertEquals("direct child sees owner", parentFakeOwner,
-                ViewTreeLifecycleOwner.get(parent));
-        assertEquals("grandchild sees owner", parentFakeOwner, ViewTreeLifecycleOwner.get(child));
-    }
-
-    static class FakeLifecycleOwner implements LifecycleOwner {
-        @NonNull
-        @Override
-        public Lifecycle getLifecycle() {
-            throw new UnsupportedOperationException("not a real LifecycleOwner");
-        }
-    }
-}
diff --git a/lifecycle/lifecycle-runtime/src/androidTest/java/androidx/lifecycle/ViewTreeLifecycleOwnerTest.kt b/lifecycle/lifecycle-runtime/src/androidTest/java/androidx/lifecycle/ViewTreeLifecycleOwnerTest.kt
new file mode 100644
index 0000000..3308c2b
--- /dev/null
+++ b/lifecycle/lifecycle-runtime/src/androidTest/java/androidx/lifecycle/ViewTreeLifecycleOwnerTest.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.lifecycle
+
+import android.view.View
+import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class ViewTreeLifecycleOwnerTest {
+    /**
+     * Tests that a direct set/get on a single view survives a round trip
+     */
+    @Test
+    fun setGetSameView() {
+        val v = View(getInstrumentation().context)
+        val fakeOwner = FakeLifecycleOwner()
+        assertNull("initial LifecycleOwner expects null", v.findViewTreeLifecycleOwner())
+        v.setViewTreeLifecycleOwner(fakeOwner)
+        assertEquals(
+            "get the LifecycleOwner set directly",
+            fakeOwner,
+            v.findViewTreeLifecycleOwner()
+        )
+    }
+
+    /**
+     * Tests that the owner set on a root of a sub-hierarchy is seen by both direct children
+     * and other descendants
+     */
+    @Test
+    fun getAncestorOwner() {
+        val context = getInstrumentation().context
+        val root = FrameLayout(context)
+        val parent = FrameLayout(context)
+        val child = View(context)
+        root.addView(parent)
+        parent.addView(child)
+        assertNull(
+            "initial LifecycleOwner expects null",
+            child.findViewTreeLifecycleOwner()
+        )
+        val fakeOwner = FakeLifecycleOwner()
+        root.setViewTreeLifecycleOwner(fakeOwner)
+        assertEquals(
+            "root sees owner",
+            fakeOwner,
+            root.findViewTreeLifecycleOwner()
+        )
+        assertEquals(
+            "direct child sees owner",
+            fakeOwner,
+            parent.findViewTreeLifecycleOwner()
+        )
+        assertEquals(
+            "grandchild sees owner",
+            fakeOwner,
+            child.findViewTreeLifecycleOwner()
+        )
+    }
+
+    /**
+     * Tests that a new owner set between a root and a descendant is seen by the descendant
+     * instead of the root value
+     */
+    @Test
+    fun shadowedOwner() {
+        val context = getInstrumentation().context
+        val root = FrameLayout(context)
+        val parent = FrameLayout(context)
+        val child = View(context)
+        root.addView(parent)
+        parent.addView(child)
+        assertNull(
+            "initial LifecycleOwner expects null",
+            child.findViewTreeLifecycleOwner()
+        )
+        val rootFakeOwner = FakeLifecycleOwner()
+        root.setViewTreeLifecycleOwner(rootFakeOwner)
+        val parentFakeOwner = FakeLifecycleOwner()
+        parent.setViewTreeLifecycleOwner(parentFakeOwner)
+        assertEquals(
+            "root sees owner",
+            rootFakeOwner,
+            root.findViewTreeLifecycleOwner()
+        )
+        assertEquals(
+            "direct child sees owner",
+            parentFakeOwner,
+            parent.findViewTreeLifecycleOwner()
+        )
+        assertEquals(
+            "grandchild sees owner",
+            parentFakeOwner,
+            child.findViewTreeLifecycleOwner()
+        )
+    }
+
+    internal class FakeLifecycleOwner : LifecycleOwner {
+        override val lifecycle = NoOpLifecycle()
+    }
+
+    internal class NoOpLifecycle : Lifecycle() {
+        override fun addObserver(observer: LifecycleObserver) {}
+        override fun removeObserver(observer: LifecycleObserver) {}
+        // use arbitrary State
+        override val currentState = State.RESUMED
+    }
+}
diff --git a/lifecycle/lifecycle-runtime/src/test/java/NoPackageObserver.java b/lifecycle/lifecycle-runtime/src/test/java/NoPackageObserver.java
deleted file mode 100644
index 60bc171..0000000
--- a/lifecycle/lifecycle-runtime/src/test/java/NoPackageObserver.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-import static androidx.lifecycle.Lifecycle.Event.ON_CREATE;
-
-import androidx.lifecycle.LifecycleObserver;
-
-@SuppressWarnings("deprecation")
-public class NoPackageObserver implements LifecycleObserver {
-    @androidx.lifecycle.OnLifecycleEvent(ON_CREATE)
-    void onCreate() {
-    }
-}
diff --git a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence2.java b/lifecycle/lifecycle-runtime/src/test/java/NoPackageObserver.kt
similarity index 68%
copy from lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence2.java
copy to lifecycle/lifecycle-runtime/src/test/java/NoPackageObserver.kt
index aeb87da..813087d 100644
--- a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence2.java
+++ b/lifecycle/lifecycle-runtime/src/test/java/NoPackageObserver.kt
@@ -14,15 +14,13 @@
  * limitations under the License.
  */
 
-package androidx.lifecycle.observers;
+@file:Suppress("DEPRECATION")
 
-import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleObserver
+import androidx.lifecycle.OnLifecycleEvent
 
-@SuppressWarnings("deprecation")
-public class DerivedSequence2 extends DerivedSequence1 {
-
-    @androidx.lifecycle.OnLifecycleEvent(Lifecycle.Event.ON_STOP)
-    void onStop() {
-
-    }
+open class NoPackageObserver : LifecycleObserver {
+    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
+    open fun onCreate() {}
 }
diff --git a/lifecycle/lifecycle-runtime/src/test/java/NoPackageTest.java b/lifecycle/lifecycle-runtime/src/test/java/NoPackageTest.java
deleted file mode 100644
index 77ba93b..0000000
--- a/lifecycle/lifecycle-runtime/src/test/java/NoPackageTest.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-import static androidx.lifecycle.Lifecycle.Event.ON_CREATE;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.lifecycle.LifecycleRegistry;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class NoPackageTest {
-
-    private LifecycleOwner mLifecycleOwner;
-    private Lifecycle mLifecycle;
-    private LifecycleRegistry mRegistry;
-
-    @Before
-    public void init() {
-        mLifecycleOwner = mock(LifecycleOwner.class);
-        mLifecycle = mock(Lifecycle.class);
-        when(mLifecycleOwner.getLifecycle()).thenReturn(mLifecycle);
-        mRegistry = LifecycleRegistry.createUnsafe(mLifecycleOwner);
-    }
-
-    @Test
-    public void testNoPackage() {
-        NoPackageObserver observer = mock(NoPackageObserver.class);
-        mRegistry.addObserver(observer);
-        mRegistry.handleLifecycleEvent(ON_CREATE);
-        verify(observer).onCreate();
-    }
-
-}
-
diff --git a/lifecycle/lifecycle-runtime/src/test/java/NoPackageTest.kt b/lifecycle/lifecycle-runtime/src/test/java/NoPackageTest.kt
new file mode 100644
index 0000000..9494d19
--- /dev/null
+++ b/lifecycle/lifecycle-runtime/src/test/java/NoPackageTest.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.LifecycleRegistry
+import androidx.lifecycle.LifecycleRegistry.Companion.createUnsafe
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+
+@RunWith(JUnit4::class)
+class NoPackageTest {
+    private lateinit var lifecycleOwner: LifecycleOwner
+    private lateinit var lifecycle: Lifecycle
+    private lateinit var registry: LifecycleRegistry
+
+    @Before
+    fun init() {
+        lifecycleOwner = mock(LifecycleOwner::class.java)
+        lifecycle = mock(Lifecycle::class.java)
+        `when`(lifecycleOwner.lifecycle).thenReturn(lifecycle)
+        registry = createUnsafe(lifecycleOwner)
+    }
+
+    @Test
+    fun testNoPackage() {
+        val observer = mock(NoPackageObserver::class.java)
+        registry.addObserver(observer)
+        registry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
+        verify(observer).onCreate()
+    }
+}
diff --git a/lifecycle/lifecycle-service/src/androidTest/java/androidx/lifecycle/ServiceLifecycleTest.java b/lifecycle/lifecycle-service/src/androidTest/java/androidx/lifecycle/ServiceLifecycleTest.java
deleted file mode 100644
index 04055db..0000000
--- a/lifecycle/lifecycle-service/src/androidTest/java/androidx/lifecycle/ServiceLifecycleTest.java
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Copyright (C) 2017 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.lifecycle;
-
-import static androidx.lifecycle.Lifecycle.Event.ON_CREATE;
-import static androidx.lifecycle.Lifecycle.Event.ON_DESTROY;
-import static androidx.lifecycle.Lifecycle.Event.ON_START;
-import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.os.IBinder;
-
-import androidx.lifecycle.Lifecycle.Event;
-import androidx.lifecycle.service.TestService;
-import androidx.localbroadcastmanager.content.LocalBroadcastManager;
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.MediumTest;
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-@SuppressWarnings("deprecation")
-@MediumTest
-@RunWith(AndroidJUnit4.class)
-public class ServiceLifecycleTest {
-
-    private static final int RETRY_NUMBER = 5;
-    private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(1);
-
-    private Intent mServiceIntent;
-
-    private volatile List<Event> mLoggerEvents;
-    private EventLogger mLogger;
-
-    @Before
-    public void setUp() {
-        Context context = ApplicationProvider.getApplicationContext();
-        mServiceIntent = new Intent(context, TestService.class);
-        LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context);
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(TestService.ACTION_LOG_EVENT);
-
-        // Overcautiousness: each EventLogger has its own events list, so one bad test won't spoil
-        // others.
-        mLoggerEvents = new ArrayList<>();
-        mLogger = new EventLogger(mLoggerEvents);
-        localBroadcastManager.registerReceiver(mLogger, intentFilter);
-
-    }
-
-    @After
-    public void tearDown() {
-        Context context = ApplicationProvider.getApplicationContext();
-        LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context);
-        localBroadcastManager.unregisterReceiver(mLogger);
-        mLogger = null;
-        mLoggerEvents = null;
-    }
-
-    @Test
-    public void testUnboundedService() throws TimeoutException, InterruptedException {
-        Context context = ApplicationProvider.getApplicationContext();
-        context.startService(mServiceIntent);
-        awaitAndAssertEvents(ON_CREATE, ON_START);
-        context.stopService(mServiceIntent);
-        awaitAndAssertEvents(ON_CREATE, ON_START, ON_STOP, ON_DESTROY);
-    }
-
-    @Test
-    public void testBoundedService() throws TimeoutException, InterruptedException {
-        ServiceConnection connection = bindToService();
-        awaitAndAssertEvents(ON_CREATE, ON_START);
-        ApplicationProvider.getApplicationContext().unbindService(connection);
-        awaitAndAssertEvents(ON_CREATE, ON_START, ON_STOP, ON_DESTROY);
-    }
-
-    @Test
-    public void testStartBindUnbindStop() throws InterruptedException {
-        Context context = ApplicationProvider.getApplicationContext();
-        context.startService(mServiceIntent);
-        awaitAndAssertEvents(ON_CREATE, ON_START);
-
-        ServiceConnection connection = bindToService();
-        // Precaution: give a chance to dispatch events
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-        // still the same events
-        awaitAndAssertEvents(ON_CREATE, ON_START);
-
-        context.unbindService(connection);
-        // Precaution: give a chance to dispatch events
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-        // service is still started (stopServices/stopSelf weren't called)
-        awaitAndAssertEvents(ON_CREATE, ON_START);
-
-        context.stopService(mServiceIntent);
-        awaitAndAssertEvents(ON_CREATE, ON_START, ON_STOP, ON_DESTROY);
-    }
-
-    @Test
-    public void testStartBindStopUnbind() throws InterruptedException {
-        Context context = ApplicationProvider.getApplicationContext();
-        context.startService(mServiceIntent);
-        awaitAndAssertEvents(ON_CREATE, ON_START);
-
-        ServiceConnection connection = bindToService();
-        // Precaution: give a chance to dispatch events
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-        // still the same events
-        awaitAndAssertEvents(ON_CREATE, ON_START);
-
-        context.stopService(mServiceIntent);
-        // Precaution: give a chance to dispatch events
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-        // service is still bound
-        awaitAndAssertEvents(ON_CREATE, ON_START);
-
-        context.unbindService(connection);
-        awaitAndAssertEvents(ON_CREATE, ON_START,
-                ON_STOP, ON_DESTROY);
-    }
-
-    @Test
-    public void testBindStartUnbindStop() throws InterruptedException {
-        Context context = ApplicationProvider.getApplicationContext();
-        ServiceConnection connection = bindToService();
-        awaitAndAssertEvents(ON_CREATE, ON_START);
-
-
-        context.startService(mServiceIntent);
-        // Precaution: give a chance to dispatch events
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-        // still the same events
-        awaitAndAssertEvents(ON_CREATE, ON_START);
-
-        context.unbindService(connection);
-        // Precaution: give a chance to dispatch events
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-        // service is still started (stopServices/stopSelf weren't called)
-        awaitAndAssertEvents(ON_CREATE, ON_START);
-
-        context.stopService(mServiceIntent);
-        awaitAndAssertEvents(ON_CREATE, ON_START,
-                ON_STOP, ON_DESTROY);
-    }
-
-    @Test
-    public void testBindStartStopUnbind() throws InterruptedException {
-        Context context = ApplicationProvider.getApplicationContext();
-        ServiceConnection connection = bindToService();
-        awaitAndAssertEvents(ON_CREATE, ON_START);
-
-        context.startService(mServiceIntent);
-        // Precaution: give a chance to dispatch events
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-        // still the same events
-        awaitAndAssertEvents(ON_CREATE, ON_START);
-
-        context.stopService(mServiceIntent);
-        // Precaution: give a chance to dispatch events
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-        // service is still bound
-        awaitAndAssertEvents(ON_CREATE, ON_START);
-
-        context.unbindService(connection);
-        awaitAndAssertEvents(ON_CREATE, ON_START,
-                ON_STOP, ON_DESTROY);
-    }
-
-    // can't use ServiceTestRule because it proxies connection, so we can't use unbindService method
-    private ServiceConnection bindToService() throws InterruptedException {
-        Context context = ApplicationProvider.getApplicationContext();
-        final CountDownLatch latch = new CountDownLatch(1);
-        ServiceConnection connection = new ServiceConnection() {
-            @Override
-            public void onServiceConnected(ComponentName name, IBinder service) {
-                latch.countDown();
-            }
-
-            @Override
-            public void onServiceDisconnected(ComponentName name) {
-
-            }
-        };
-
-        boolean success = context.bindService(mServiceIntent, connection, Context.BIND_AUTO_CREATE);
-        assertThat(success, is(true));
-        boolean awaited = latch.await(TIMEOUT, TimeUnit.MILLISECONDS);
-        assertThat(awaited, is(true));
-        return connection;
-    }
-
-    private void awaitAndAssertEvents(Event... events) throws InterruptedException {
-        //noinspection SynchronizeOnNonFinalField
-        synchronized (mLoggerEvents) {
-            int retryCount = 0;
-            while (mLoggerEvents.size() < events.length && retryCount++ < RETRY_NUMBER) {
-                mLoggerEvents.wait(TIMEOUT);
-            }
-            assertThat(mLoggerEvents, is(Arrays.asList(events)));
-        }
-    }
-
-    private static class EventLogger extends BroadcastReceiver {
-        private final List<Event> mLoggerEvents;
-
-        private EventLogger(List<Event> loggerEvents) {
-            mLoggerEvents = loggerEvents;
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            synchronized (mLoggerEvents) {
-                mLoggerEvents.add((Event) intent.getSerializableExtra(TestService.EXTRA_KEY_EVENT));
-                mLoggerEvents.notifyAll();
-            }
-        }
-    }
-}
diff --git a/lifecycle/lifecycle-service/src/androidTest/java/androidx/lifecycle/ServiceLifecycleTest.kt b/lifecycle/lifecycle-service/src/androidTest/java/androidx/lifecycle/ServiceLifecycleTest.kt
new file mode 100644
index 0000000..439fdc0
--- /dev/null
+++ b/lifecycle/lifecycle-service/src/androidTest/java/androidx/lifecycle/ServiceLifecycleTest.kt
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2017 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.lifecycle
+
+import android.content.BroadcastReceiver
+import android.content.ComponentName
+import android.content.Context
+import android.content.Context.BIND_AUTO_CREATE
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.ServiceConnection
+import android.os.IBinder
+import androidx.lifecycle.Lifecycle.Event
+import androidx.lifecycle.Lifecycle.Event.ON_CREATE
+import androidx.lifecycle.Lifecycle.Event.ON_DESTROY
+import androidx.lifecycle.Lifecycle.Event.ON_START
+import androidx.lifecycle.Lifecycle.Event.ON_STOP
+import androidx.lifecycle.service.TestService
+import androidx.lifecycle.service.TestService.ACTION_LOG_EVENT
+import androidx.lifecycle.service.TestService.EXTRA_KEY_EVENT
+import androidx.localbroadcastmanager.content.LocalBroadcastManager.getInstance
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit.MILLISECONDS
+import java.util.concurrent.TimeUnit.SECONDS
+import java.util.concurrent.TimeoutException
+import java.util.concurrent.locks.ReentrantLock
+import kotlin.concurrent.withLock
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@Suppress("deprecation")
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class ServiceLifecycleTest {
+    private lateinit var serviceIntent: Intent
+
+    @Volatile
+    private var loggerEvents = mutableListOf<Event?>()
+    private lateinit var logger: EventLogger
+
+    @Before
+    fun setUp() {
+        val context = getApplicationContext<Context>()
+        serviceIntent = Intent(context, TestService::class.java)
+        val localBroadcastManager = getInstance(context)
+        val intentFilter = IntentFilter()
+        intentFilter.addAction(ACTION_LOG_EVENT)
+
+        // Over-cautiousness: each EventLogger has its own events list, so one bad test won't spoil
+        // others.
+        loggerEvents = ArrayList()
+        logger = EventLogger(loggerEvents)
+        localBroadcastManager.registerReceiver(logger, intentFilter)
+    }
+
+    @After
+    fun tearDown() {
+        val context = getApplicationContext<Context>()
+        val localBroadcastManager = getInstance(context)
+        localBroadcastManager.unregisterReceiver(logger)
+        loggerEvents.clear()
+    }
+
+    @Test
+    @Throws(TimeoutException::class, InterruptedException::class)
+    fun testUnboundedService() {
+        val context = getApplicationContext<Context>()
+        context.startService(serviceIntent)
+        awaitAndAssertEvents(ON_CREATE, ON_START)
+        context.stopService(serviceIntent)
+        awaitAndAssertEvents(ON_CREATE, ON_START, ON_STOP, ON_DESTROY)
+    }
+
+    @Test
+    @Throws(TimeoutException::class, InterruptedException::class)
+    fun testBoundedService() {
+        val connection = bindToService()
+        awaitAndAssertEvents(ON_CREATE, ON_START)
+        getApplicationContext<Context>().unbindService(connection)
+        awaitAndAssertEvents(ON_CREATE, ON_START, ON_STOP, ON_DESTROY)
+    }
+
+    @Test
+    @Throws(InterruptedException::class)
+    fun testStartBindUnbindStop() {
+        val context = getApplicationContext<Context>()
+        context.startService(serviceIntent)
+        awaitAndAssertEvents(ON_CREATE, ON_START)
+        val connection = bindToService()
+        // Precaution: give a chance to dispatch events
+        getInstrumentation().waitForIdleSync()
+        // still the same events
+        awaitAndAssertEvents(ON_CREATE, ON_START)
+        context.unbindService(connection)
+        // Precaution: give a chance to dispatch events
+        getInstrumentation().waitForIdleSync()
+        // service is still started (stopServices/stopSelf weren't called)
+        awaitAndAssertEvents(ON_CREATE, ON_START)
+        context.stopService(serviceIntent)
+        awaitAndAssertEvents(ON_CREATE, ON_START, ON_STOP, ON_DESTROY)
+    }
+
+    @Test
+    @Throws(InterruptedException::class)
+    fun testStartBindStopUnbind() {
+        val context = getApplicationContext<Context>()
+        context.startService(serviceIntent)
+        awaitAndAssertEvents(ON_CREATE, ON_START)
+        val connection = bindToService()
+        // Precaution: give a chance to dispatch events
+        getInstrumentation().waitForIdleSync()
+        // still the same events
+        awaitAndAssertEvents(ON_CREATE, ON_START)
+        context.stopService(serviceIntent)
+        // Precaution: give a chance to dispatch events
+        getInstrumentation().waitForIdleSync()
+        // service is still bound
+        awaitAndAssertEvents(ON_CREATE, ON_START)
+        context.unbindService(connection)
+        awaitAndAssertEvents(ON_CREATE, ON_START, ON_STOP, ON_DESTROY)
+    }
+
+    @Test
+    @Throws(InterruptedException::class)
+    fun testBindStartUnbindStop() {
+        val context = getApplicationContext<Context>()
+        val connection = bindToService()
+        awaitAndAssertEvents(ON_CREATE, ON_START)
+        context.startService(serviceIntent)
+        // Precaution: give a chance to dispatch events
+        getInstrumentation().waitForIdleSync()
+        // still the same events
+        awaitAndAssertEvents(ON_CREATE, ON_START)
+        context.unbindService(connection)
+        // Precaution: give a chance to dispatch events
+        getInstrumentation().waitForIdleSync()
+        // service is still started (stopServices/stopSelf weren't called)
+        awaitAndAssertEvents(ON_CREATE, ON_START)
+        context.stopService(serviceIntent)
+        awaitAndAssertEvents(ON_CREATE, ON_START, ON_STOP, ON_DESTROY)
+    }
+
+    @Test
+    @Throws(InterruptedException::class)
+    fun testBindStartStopUnbind() {
+        val context = getApplicationContext<Context>()
+        val connection = bindToService()
+        awaitAndAssertEvents(ON_CREATE, ON_START)
+        context.startService(serviceIntent)
+        // Precaution: give a chance to dispatch events
+        getInstrumentation().waitForIdleSync()
+        // still the same events
+        awaitAndAssertEvents(ON_CREATE, ON_START)
+        context.stopService(serviceIntent)
+        // Precaution: give a chance to dispatch events
+        getInstrumentation().waitForIdleSync()
+        // service is still bound
+        awaitAndAssertEvents(ON_CREATE, ON_START)
+        context.unbindService(connection)
+        awaitAndAssertEvents(ON_CREATE, ON_START, ON_STOP, ON_DESTROY)
+    }
+
+    // can't use ServiceTestRule because it proxies connection, so we can't use unbindService method
+    @Throws(InterruptedException::class)
+    private fun bindToService(): ServiceConnection {
+        val context = getApplicationContext<Context>()
+        val latch = CountDownLatch(1)
+        val connection = object : ServiceConnection {
+            override fun onServiceConnected(name: ComponentName, service: IBinder) {
+                latch.countDown()
+            }
+
+            override fun onServiceDisconnected(name: ComponentName) {}
+        }
+        val success = context.bindService(serviceIntent, connection, BIND_AUTO_CREATE)
+        assertThat(success, `is`(true))
+        val awaited = latch.await(TIMEOUT, MILLISECONDS)
+        assertThat(awaited, `is`(true))
+        return connection
+    }
+
+    @Throws(InterruptedException::class)
+    private fun awaitAndAssertEvents(vararg events: Event) {
+        lock.withLock {
+            var retryCount = 0
+            while (loggerEvents.size < events.size && retryCount++ < RETRY_NUMBER) {
+                condition.await(TIMEOUT, SECONDS)
+            }
+            assertThat(loggerEvents, `is`(listOf(*events)))
+        }
+    }
+
+    private class EventLogger(private val loggerEvents: MutableList<Event?>) :
+        BroadcastReceiver() {
+        override fun onReceive(context: Context, intent: Intent) {
+            lock.withLock {
+                loggerEvents.add(intent.getSerializableExtra(EXTRA_KEY_EVENT) as Event)
+                condition.signalAll()
+            }
+        }
+    }
+
+    companion object {
+        private const val RETRY_NUMBER = 5
+        private val TIMEOUT = SECONDS.toMillis(1)
+        private val lock = ReentrantLock()
+        private val condition = lock.newCondition()
+    }
+}
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/SavedStateHandleParcelingTest.java b/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/SavedStateHandleParcelingTest.java
deleted file mode 100644
index 71dc7a2..0000000
--- a/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/SavedStateHandleParcelingTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.lifecycle;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-import android.os.Bundle;
-import android.os.Parcel;
-
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Arrays;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class SavedStateHandleParcelingTest {
-
-    @UiThreadTest
-    @Test
-    public void test() {
-        SavedStateHandle handle = new SavedStateHandle();
-        handle.<String>getLiveData("livedata").setValue("para");
-        handle.set("notlive", 261);
-        handle.set("array", new int[]{2, 3, 9});
-        Bundle savedState = handle.savedStateProvider().saveState();
-        Parcel parcel = Parcel.obtain();
-        savedState.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-        Bundle newBundle = Bundle.CREATOR.createFromParcel(parcel);
-        SavedStateHandle newHandle = SavedStateHandle.createHandle(newBundle, null);
-        assertThat(newHandle.<String>get("livedata"), is("para"));
-        assertThat(newHandle.<Integer>get("notlive"), is(261));
-        assertThat(Arrays.equals(newHandle.<int[]>get("array"), new int[]{2, 3, 9}), is(true));
-    }
-
-    @UiThreadTest
-    @Test
-    public void testRemoveFromDefault() {
-        Bundle defaultState = new Bundle();
-        defaultState.putString("string", "default");
-        SavedStateHandle handle = SavedStateHandle.createHandle(null, defaultState);
-        assertThat(handle.contains("string"), is(true));
-        handle.remove("string");
-        assertThat(handle.contains("string"), is(false));
-
-        Bundle savedState = handle.savedStateProvider().saveState();
-        SavedStateHandle newHandle = SavedStateHandle.createHandle(savedState, defaultState);
-        assertThat(newHandle.contains("string"), is(false));
-    }
-}
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/SavedStateHandleParcelingTest.kt b/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/SavedStateHandleParcelingTest.kt
new file mode 100644
index 0000000..d4fac80
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-savedstate/src/androidTest/java/androidx/lifecycle/SavedStateHandleParcelingTest.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.lifecycle
+
+import android.os.Bundle
+import android.os.Bundle.CREATOR
+import android.os.Parcel.obtain
+import androidx.lifecycle.SavedStateHandle.Companion.createHandle
+import androidx.test.annotation.UiThreadTest
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import java.util.Arrays.equals
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SavedStateHandleParcelingTest {
+    @UiThreadTest
+    @Test
+    fun test() {
+        val handle = SavedStateHandle()
+        handle.getLiveData<String>("livedata").value = "para"
+        handle["notlive"] = 261
+        handle["array"] = intArrayOf(2, 3, 9)
+        val savedState = handle.savedStateProvider().saveState()
+        val parcel = obtain()
+        savedState.writeToParcel(parcel, 0)
+        parcel.setDataPosition(0)
+        val newBundle = CREATOR.createFromParcel(parcel)
+        val newHandle: SavedStateHandle = createHandle(newBundle, null)
+        assertThat<String>(newHandle["livedata"], `is`("para"))
+        assertThat<Int>(newHandle["notlive"], `is`(261))
+        assertThat(
+            equals(
+                newHandle["array"],
+                intArrayOf(2, 3, 9)
+            ),
+            `is`(true)
+        )
+    }
+
+    @UiThreadTest
+    @Test
+    fun testRemoveFromDefault() {
+        val defaultState = Bundle()
+        defaultState.putString("string", "default")
+        val handle = createHandle(null, defaultState)
+        assertThat(handle.contains("string"), `is`(true))
+        handle.remove<Any>("string")
+        assertThat(handle.contains("string"), `is`(false))
+        val savedState = handle.savedStateProvider().saveState()
+        val newHandle = createHandle(savedState, defaultState)
+        assertThat(newHandle.contains("string"), `is`(false))
+    }
+}
diff --git a/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/ViewModelStoreTest.java b/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/ViewModelStoreTest.java
deleted file mode 100644
index a0ca24e..0000000
--- a/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/ViewModelStoreTest.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2017 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.lifecycle;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class ViewModelStoreTest {
-
-    @Test
-    public void testClear() {
-        ViewModelStore store = new ViewModelStore();
-        TestViewModel viewModel1 = new TestViewModel();
-        TestViewModel viewModel2 = new TestViewModel();
-        TestViewModel mockViewModel = mock(TestViewModel.class);
-        store.put("a", viewModel1);
-        store.put("b", viewModel2);
-        store.put("mock", mockViewModel);
-        assertThat(viewModel1.mCleared, is(false));
-        assertThat(viewModel2.mCleared, is(false));
-        store.clear();
-        assertThat(viewModel1.mCleared, is(true));
-        assertThat(viewModel2.mCleared, is(true));
-        verify(mockViewModel).onCleared();
-        verifyNoMoreInteractions(mockViewModel);
-        assertThat(store.get("a"), nullValue());
-        assertThat(store.get("b"), nullValue());
-    }
-
-    static class TestViewModel extends ViewModel {
-        boolean mCleared = false;
-
-        @Override
-        protected void onCleared() {
-            mCleared = true;
-        }
-    }
-}
diff --git a/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/ViewModelStoreTest.kt b/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/ViewModelStoreTest.kt
new file mode 100644
index 0000000..6e048f6
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/ViewModelStoreTest.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 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.lifecycle
+
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.CoreMatchers.nullValue
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+
+@RunWith(JUnit4::class)
+class ViewModelStoreTest {
+    @Test
+    fun testClear() {
+        val store = ViewModelStore()
+        val viewModel1 = TestViewModel()
+        val viewModel2 = TestViewModel()
+        val mockViewModel = mock(TestViewModel::class.java)
+        store.put("a", viewModel1)
+        store.put("b", viewModel2)
+        store.put("mock", mockViewModel)
+        assertThat(viewModel1.cleared, `is`(false))
+        assertThat(viewModel2.cleared, `is`(false))
+        store.clear()
+        assertThat(viewModel1.cleared, `is`(true))
+        assertThat(viewModel2.cleared, `is`(true))
+        verify(mockViewModel).onCleared()
+        verifyNoMoreInteractions(mockViewModel)
+        assertThat(store["a"], nullValue())
+        assertThat(store["b"], nullValue())
+    }
+
+    internal open class TestViewModel : ViewModel() {
+        var cleared = false
+        public override fun onCleared() {
+            cleared = true
+        }
+    }
+}
diff --git a/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/ViewModelTest.java b/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/ViewModelTest.java
deleted file mode 100644
index 76fd7b0..0000000
--- a/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/ViewModelTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.lifecycle;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import androidx.annotation.NonNull;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import org.mockito.Mockito;
-
-import java.io.Closeable;
-
-@RunWith(JUnit4.class)
-public class ViewModelTest {
-
-    static class CloseableImpl implements Closeable {
-        boolean mWasClosed;
-        @Override
-        public void close() {
-            mWasClosed = true;
-        }
-    }
-
-    static class ViewModel extends androidx.lifecycle.ViewModel {
-    }
-
-    static class ConstructorArgViewModel extends androidx.lifecycle.ViewModel {
-        ConstructorArgViewModel(@NonNull Closeable closeable) {
-            super(closeable);
-        }
-    }
-
-    @Test
-    public void testCloseableTag() {
-        ViewModel vm = new ViewModel();
-        CloseableImpl impl = new CloseableImpl();
-        vm.setTagIfAbsent("totally_not_coroutine_context", impl);
-        vm.clear();
-        assertTrue(impl.mWasClosed);
-    }
-
-    @Test
-    public void testCloseableTagAlreadyClearedVM() {
-        ViewModel vm = new ViewModel();
-        vm.clear();
-        CloseableImpl impl = new CloseableImpl();
-        vm.setTagIfAbsent("key", impl);
-        assertTrue(impl.mWasClosed);
-
-    }
-
-    @Test
-    public void testAlreadyAssociatedKey() {
-        ViewModel vm = new ViewModel();
-        assertThat(vm.setTagIfAbsent("key", "first"), is("first"));
-        assertThat(vm.setTagIfAbsent("key", "second"), is("first"));
-    }
-
-    @Test
-    public void testMockedGetTag() {
-        ViewModel vm = Mockito.mock(ViewModel.class);
-        assertThat(vm.getTag("Careless mocks =|"), nullValue());
-    }
-
-    @Test
-    public void testAddCloseable() {
-        ViewModel vm = new ViewModel();
-        CloseableImpl impl = new CloseableImpl();
-        vm.addCloseable(impl);
-        vm.clear();
-        assertTrue(impl.mWasClosed);
-    }
-
-    @Test
-    public void testConstructorCloseable() {
-        CloseableImpl impl = new CloseableImpl();
-        ConstructorArgViewModel vm = new ConstructorArgViewModel(impl);
-        vm.clear();
-        assertTrue(impl.mWasClosed);
-    }
-
-    @Test
-    public void testMockedAddCloseable() {
-        ViewModel vm = Mockito.mock(ViewModel.class);
-        CloseableImpl impl = new CloseableImpl();
-        // This shouldn't crash, even on a mocked object
-        vm.addCloseable(impl);
-    }
-}
diff --git a/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/ViewModelTest.kt b/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/ViewModelTest.kt
new file mode 100644
index 0000000..6e68344
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel/src/test/java/androidx/lifecycle/ViewModelTest.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.lifecycle
+
+import java.io.Closeable
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.CoreMatchers.nullValue
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mockito
+
+@RunWith(JUnit4::class)
+class ViewModelTest {
+
+    internal class CloseableImpl : Closeable {
+        var wasClosed = false
+        override fun close() {
+            wasClosed = true
+        }
+    }
+
+    internal open class ViewModel : androidx.lifecycle.ViewModel()
+    internal class ConstructorArgViewModel(closeable: Closeable) :
+        androidx.lifecycle.ViewModel(closeable)
+
+    @Test
+    fun testCloseableTag() {
+        val vm = ViewModel()
+        val impl = CloseableImpl()
+        vm.setTagIfAbsent("totally_not_coroutine_context", impl)
+        vm.clear()
+        assertTrue(impl.wasClosed)
+    }
+
+    @Test
+    fun testCloseableTagAlreadyClearedVM() {
+        val vm = ViewModel()
+        vm.clear()
+        val impl = CloseableImpl()
+        vm.setTagIfAbsent("key", impl)
+        assertTrue(impl.wasClosed)
+    }
+
+    @Test
+    fun testAlreadyAssociatedKey() {
+        val vm = ViewModel()
+        assertThat(vm.setTagIfAbsent("key", "first"), `is`("first"))
+        assertThat(vm.setTagIfAbsent("key", "second"), `is`("first"))
+    }
+
+    @Test
+    fun testMockedGetTag() {
+        val vm = Mockito.mock(ViewModel::class.java)
+        assertThat(vm.getTag("Careless mocks =|"), nullValue())
+    }
+
+    @Test
+    fun testAddCloseable() {
+        val vm = ViewModel()
+        val impl = CloseableImpl()
+        vm.addCloseable(impl)
+        vm.clear()
+        assertTrue(impl.wasClosed)
+    }
+
+    @Test
+    fun testConstructorCloseable() {
+        val impl = CloseableImpl()
+        val vm = ConstructorArgViewModel(impl)
+        vm.clear()
+        assertTrue(impl.wasClosed)
+    }
+
+    @Test
+    fun testMockedAddCloseable() {
+        val vm = Mockito.mock(ViewModel::class.java)
+        val impl = CloseableImpl()
+        // This shouldn't crash, even on a mocked object
+        vm.addCloseable(impl)
+    }
+}
diff --git a/media/OWNERS b/media/OWNERS
index 5e87314..1cdf81b 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -1,4 +1,4 @@
-# Bug component: 461042
+# Bug component: 188488
 
 # Media Playback:
 [email protected]
diff --git a/media/media/build.gradle b/media/media/build.gradle
index 1523950..b1b873f 100644
--- a/media/media/build.gradle
+++ b/media/media/build.gradle
@@ -39,6 +39,10 @@
     buildFeatures {
         aidl = true
     }
+    lint {
+        // Temporarily disabled due to flakiness (see b/291607684).
+        disable "RequireUnstableAidlAnnotation"
+    }
     sourceSets {
         main.java.srcDirs += [
         ]
diff --git a/media/media/lint-baseline.xml b/media/media/lint-baseline.xml
index 0d822ce..de43ef9 100644
--- a/media/media/lint-baseline.xml
+++ b/media/media/lint-baseline.xml
@@ -2,114 +2,6 @@
 <issues format="6" by="lint 8.1.0-beta05" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-beta05)" variant="all" version="8.1.0-beta05">
 
     <issue
-        id="PrereleaseSdkCoreDependency"
-        message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
-        errorLine1="            if (BuildCompat.isAtLeastU()) {"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/media/app/NotificationCompat.java"/>
-    </issue>
-
-    <issue
-        id="PrereleaseSdkCoreDependency"
-        message="Prelease SDK check isAtLeastU cannot be called as this project has a versioned dependency on androidx.core:core"
-        errorLine1="            if (BuildCompat.isAtLeastU()) {"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/media/app/NotificationCompat.java"/>
-    </issue>
-
-    <issue
-        id="RequireUnstableAidlAnnotation"
-        message="Unstable AIDL files must be annotated with `@RequiresOptIn` marker"
-        errorLine1="oneway interface IMediaControllerCallback {"
-        errorLine2="^">
-        <location
-            file="src/main/aidl/android/support/v4/media/session/IMediaControllerCallback.aidl"/>
-    </issue>
-
-    <issue
-        id="RequireUnstableAidlAnnotation"
-        message="Unstable AIDL files must be annotated with `@RequiresOptIn` marker"
-        errorLine1="interface IMediaSession {"
-        errorLine2="^">
-        <location
-            file="src/main/aidl/android/support/v4/media/session/IMediaSession.aidl"/>
-    </issue>
-
-    <issue
-        id="RequireUnstableAidlAnnotation"
-        message="Unstable AIDL files must be annotated with `@RequiresOptIn` marker"
-        errorLine1="@JavaOnlyStableParcelable parcelable MediaDescriptionCompat;"
-        errorLine2="                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/aidl/android/support/v4/media/MediaDescriptionCompat.aidl"/>
-    </issue>
-
-    <issue
-        id="RequireUnstableAidlAnnotation"
-        message="Unstable AIDL files must be annotated with `@RequiresOptIn` marker"
-        errorLine1="@JavaOnlyStableParcelable parcelable MediaMetadataCompat;"
-        errorLine2="                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/aidl/android/support/v4/media/MediaMetadataCompat.aidl"/>
-    </issue>
-
-    <issue
-        id="RequireUnstableAidlAnnotation"
-        message="Unstable AIDL files must be annotated with `@RequiresOptIn` marker"
-        errorLine1="@JavaOnlyStableParcelable parcelable MediaSessionCompat.Token;"
-        errorLine2="                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/aidl/android/support/v4/media/session/MediaSessionCompat.aidl"/>
-    </issue>
-
-    <issue
-        id="RequireUnstableAidlAnnotation"
-        message="Unstable AIDL files must be annotated with `@RequiresOptIn` marker"
-        errorLine1="@JavaOnlyStableParcelable parcelable MediaSessionCompat.QueueItem;"
-        errorLine2="                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/aidl/android/support/v4/media/session/MediaSessionCompat.aidl"/>
-    </issue>
-
-    <issue
-        id="RequireUnstableAidlAnnotation"
-        message="Unstable AIDL files must be annotated with `@RequiresOptIn` marker"
-        errorLine1="@JavaOnlyStableParcelable parcelable MediaSessionCompat.ResultReceiverWrapper;"
-        errorLine2="                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/aidl/android/support/v4/media/session/MediaSessionCompat.aidl"/>
-    </issue>
-
-    <issue
-        id="RequireUnstableAidlAnnotation"
-        message="Unstable AIDL files must be annotated with `@RequiresOptIn` marker"
-        errorLine1="@JavaOnlyStableParcelable parcelable ParcelableVolumeInfo;"
-        errorLine2="                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/aidl/android/support/v4/media/session/ParcelableVolumeInfo.aidl"/>
-    </issue>
-
-    <issue
-        id="RequireUnstableAidlAnnotation"
-        message="Unstable AIDL files must be annotated with `@RequiresOptIn` marker"
-        errorLine1="@JavaOnlyStableParcelable parcelable PlaybackStateCompat;"
-        errorLine2="                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/aidl/android/support/v4/media/session/PlaybackStateCompat.aidl"/>
-    </issue>
-
-    <issue
-        id="RequireUnstableAidlAnnotation"
-        message="Unstable AIDL files must be annotated with `@RequiresOptIn` marker"
-        errorLine1="@JavaOnlyStableParcelable parcelable RatingCompat;"
-        errorLine2="                          ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/aidl/android/support/v4/media/RatingCompat.aidl"/>
-    </issue>
-
-    <issue
         id="LambdaLast"
         message="Functional interface parameters (such as parameter 1, &quot;listener&quot;, in androidx.media.AudioFocusRequestCompat.Builder.setOnAudioFocusChangeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions"
         errorLine1="                @NonNull OnAudioFocusChangeListener listener, @NonNull Handler handler) {"
diff --git a/media2/media2-session/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/tests/MediaSessionServiceTest.java b/media2/media2-session/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/tests/MediaSessionServiceTest.java
index bdcfc8f..3ba9782 100644
--- a/media2/media2-session/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/tests/MediaSessionServiceTest.java
+++ b/media2/media2-session/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/tests/MediaSessionServiceTest.java
@@ -46,6 +46,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 
 import java.util.ArrayList;
@@ -163,6 +164,7 @@
      * can return different sessions for different controllers.
      */
     @Test
+    @Ignore("Flaky: b/291281118")
     public void onGetSession_returnsDifferentSessions() {
         final List<SessionToken> tokens = new ArrayList<>();
         TestServiceRegistry.getInstance().setOnGetSessionHandler(
diff --git a/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouter2TestActivity.java b/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouter2TestActivity.java
index 660fe42..74cd202 100644
--- a/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouter2TestActivity.java
+++ b/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouter2TestActivity.java
@@ -19,6 +19,7 @@
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
+import android.os.Build;
 import android.os.Bundle;
 import android.view.WindowManager;
 
@@ -44,8 +45,10 @@
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        setTurnScreenOn(true);
-        setShowWhenLocked(true);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
+            setTurnScreenOn(true);
+            setShowWhenLocked(true);
+        }
         getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
     }
 }
diff --git a/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouterInitializationTest.java b/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouterInitializationTest.java
index a1c7ea0..f455714 100644
--- a/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouterInitializationTest.java
+++ b/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouterInitializationTest.java
@@ -43,24 +43,23 @@
     @Test
     @MediumTest
     public void testEmptyUserRoute() throws Exception {
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                final Context context = getApplicationContext();
-                android.media.MediaRouter router =
-                        (android.media.MediaRouter) context.getSystemService(
-                                Context.MEDIA_ROUTER_SERVICE);
+        getInstrumentation()
+                .runOnMainSync(
+                        () -> {
+                            final Context context = getApplicationContext();
+                            android.media.MediaRouter router =
+                                    (android.media.MediaRouter)
+                                            context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
 
-                // Add empty user route
-                android.media.MediaRouter.RouteCategory category =
-                        router.createRouteCategory("", false);
-                android.media.MediaRouter.UserRouteInfo routeInfo =
-                        router.createUserRoute(category);
-                router.addUserRoute(routeInfo);
+                            // Add empty user route
+                            android.media.MediaRouter.RouteCategory category =
+                                    router.createRouteCategory("", false);
+                            android.media.MediaRouter.UserRouteInfo routeInfo =
+                                    router.createUserRoute(category);
+                            router.addUserRoute(routeInfo);
 
-                MediaRouter mediaRouter = MediaRouter.getInstance(context);
-                assertTrue(mediaRouter.getDefaultRoute() != null);
-            }
-        });
+                            MediaRouter mediaRouter = MediaRouter.getInstance(context);
+                            assertTrue(mediaRouter.getDefaultRoute() != null);
+                        });
     }
 }
diff --git a/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouterTest.java b/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouterTest.java
index 0df2780..4f37053 100644
--- a/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouterTest.java
+++ b/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouterTest.java
@@ -72,26 +72,20 @@
     @Before
     public void setUp() throws Exception {
         resetActiveAndPassiveScanCountDownLatches();
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mContext = getApplicationContext();
-                mRouter = MediaRouter.getInstance(mContext);
-                mSession = new MediaSessionCompat(mContext, SESSION_TAG);
-                mProvider = new MediaRouteProviderImpl(mContext);
-            }
-        });
+        getInstrumentation()
+                .runOnMainSync(
+                        () -> {
+                            mContext = getApplicationContext();
+                            mRouter = MediaRouter.getInstance(mContext);
+                            mSession = new MediaSessionCompat(mContext, SESSION_TAG);
+                            mProvider = new MediaRouteProviderImpl(mContext);
+                        });
     }
 
     @After
     public void tearDown() throws Exception {
         mSession.release();
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                MediaRouterTestHelper.resetMediaRouter();
-            }
-        });
+        getInstrumentation().runOnMainSync(() -> MediaRouterTestHelper.resetMediaRouter());
     }
 
     /**
@@ -101,13 +95,12 @@
     @Test
     @SmallTest
     public void setMediaSessionCompat_receivesCallbacks() throws Exception {
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mSession.setCallback(mSessionCallback);
-                mRouter.setMediaSessionCompat(mSession);
-            }
-        });
+        getInstrumentation()
+                .runOnMainSync(
+                        () -> {
+                            mSession.setCallback(mSessionCallback);
+                            mRouter.setMediaSessionCompat(mSession);
+                        });
 
         MediaControllerCompat controller = mSession.getController();
         MediaControllerCompat.TransportControls controls = controller.getTransportControls();
@@ -205,14 +198,15 @@
 
         // Add the provider and callback.
         resetActiveAndPassiveScanCountDownLatches();
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mRouter.addProvider(mProvider);
-                mRouter.addCallback(selector, callback,
-                        MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
-            }
-        });
+        getInstrumentation()
+                .runOnMainSync(
+                        () -> {
+                            mRouter.addProvider(mProvider);
+                            mRouter.addCallback(
+                                    selector,
+                                    callback,
+                                    MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
+                        });
 
         // Active scan should be true.
         assertTrue(mActiveScanCountDownLatch.await(TIME_OUT_MS, TimeUnit.MILLISECONDS));
@@ -224,13 +218,13 @@
 
         // Add the same callback again.
         resetActiveAndPassiveScanCountDownLatches();
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mRouter.addCallback(selector, callback,
-                        MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
-            }
-        });
+        getInstrumentation()
+                .runOnMainSync(
+                        () ->
+                                mRouter.addCallback(
+                                        selector,
+                                        callback,
+                                        MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN));
 
         // Active scan should be true.
         assertTrue(mActiveScanCountDownLatch.await(TIME_OUT_MS, TimeUnit.MILLISECONDS));
@@ -252,24 +246,25 @@
         MediaRouterCallbackImpl callback2 = new MediaRouterCallbackImpl();
 
         // Add the provider and the first callback.
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mRouter.addProvider(mProvider);
-                mRouter.addCallback(selector, callback1,
-                        MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
-            }
-        });
+        getInstrumentation()
+                .runOnMainSync(
+                        () -> {
+                            mRouter.addProvider(mProvider);
+                            mRouter.addCallback(
+                                    selector,
+                                    callback1,
+                                    MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
+                        });
 
         // Wait for 5 seconds, add the second callback.
         Thread.sleep(5000);
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mRouter.addCallback(selector, callback2,
-                        MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
-            }
-        });
+        getInstrumentation()
+                .runOnMainSync(
+                        () ->
+                                mRouter.addCallback(
+                                        selector,
+                                        callback2,
+                                        MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN));
 
         resetActiveAndPassiveScanCountDownLatches();
         // Wait for active scan duration to nearly end, active scan flag should be true.
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java
index ebdbaf3..3091883 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java
@@ -27,7 +27,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.net.Uri;
 import android.os.Build;
@@ -100,8 +99,12 @@
     static final String TAG = "MediaRouter";
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    @IntDef({UNSELECT_REASON_UNKNOWN, UNSELECT_REASON_DISCONNECTED, UNSELECT_REASON_STOPPED,
-            UNSELECT_REASON_ROUTE_CHANGED})
+    @IntDef({
+        UNSELECT_REASON_UNKNOWN,
+        UNSELECT_REASON_DISCONNECTED,
+        UNSELECT_REASON_STOPPED,
+        UNSELECT_REASON_ROUTE_CHANGED
+    })
     @Retention(RetentionPolicy.SOURCE)
     @interface UnselectReason {}
 
@@ -149,14 +152,14 @@
     final Context mContext;
     final ArrayList<CallbackRecord> mCallbackRecords = new ArrayList<CallbackRecord>();
 
-    @IntDef(flag = true,
+    @IntDef(
+            flag = true,
             value = {
-                    CALLBACK_FLAG_PERFORM_ACTIVE_SCAN,
-                    CALLBACK_FLAG_REQUEST_DISCOVERY,
-                    CALLBACK_FLAG_UNFILTERED_EVENTS,
-                    CALLBACK_FLAG_FORCE_DISCOVERY
-            }
-    )
+                CALLBACK_FLAG_PERFORM_ACTIVE_SCAN,
+                CALLBACK_FLAG_REQUEST_DISCOVERY,
+                CALLBACK_FLAG_UNFILTERED_EVENTS,
+                CALLBACK_FLAG_FORCE_DISCOVERY
+            })
     @Retention(RetentionPolicy.SOURCE)
     private @interface CallbackFlags {}
 
@@ -313,7 +316,6 @@
      *     <li>{@link androidx.mediarouter.app.MediaRouteControllerDialog}
      *     <li>{@link androidx.mediarouter.app.MediaRouteDiscoveryFragment}
      * </ul>
-     *
      */
     @RestrictTo(LIBRARY_GROUP)
     public static void resetGlobalRouter() {
@@ -350,14 +352,6 @@
                 globalMediaRouter.getRoutes();
     }
 
-    @MainThread
-    @Nullable
-    RouteInfo getRoute(String uniqueId) {
-        checkCallingThread();
-        GlobalMediaRouter globalMediaRouter = getGlobalRouter();
-        return globalMediaRouter == null ? null : globalMediaRouter.getRoute(uniqueId);
-    }
-
     /**
      * Gets information about the {@link MediaRouter.ProviderInfo route providers}
      * currently known to this media router.
@@ -447,7 +441,6 @@
      * <p>Must be called on the main thread.
      *
      * @return The selected route, which is guaranteed to never be null.
-     *
      * @see RouteInfo#getControlFilters
      * @see RouteInfo#supportsControlCategory
      * @see RouteInfo#supportsControlRequest
@@ -469,7 +462,6 @@
      * @param selector The selector to match.
      * @return The previously selected route if it matched the selector, otherwise the
      * newly selected default route which is guaranteed to never be null.
-     *
      * @see MediaRouteSelector
      * @see RouteInfo#matchesSelector
      */
@@ -586,26 +578,23 @@
 
     /**
      * Returns true if there is a route that matches the specified selector.
-     * <p>
-     * This method returns true if there are any available routes that match the
-     * selector regardless of whether they are enabled or disabled. If the
-     * {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} flag is specified, then
-     * the method will only consider non-default routes.
-     * </p>
-     * <p class="note">
-     * On {@link ActivityManager#isLowRamDevice low-RAM devices} this method
-     * will return true if it is possible to discover a matching route even if
-     * discovery is not in progress or if no matching route has yet been found.
-     * Use {@link #AVAILABILITY_FLAG_REQUIRE_MATCH} to require an actual match.
-     * </p>
+     *
+     * <p>This method returns true if there are any available routes that match the selector
+     * regardless of whether they are enabled or disabled. If the {@link
+     * #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} flag is specified, then the method will only
+     * consider non-default routes.
+     *
+     * <p class="note">On {@link ActivityManager#isLowRamDevice low-RAM devices} this method will
+     * return true if it is possible to discover a matching route even if discovery is not in
+     * progress or if no matching route has yet been found. Use {@link
+     * #AVAILABILITY_FLAG_REQUIRE_MATCH} to require an actual match.
      *
      * <p>Must be called on the main thread.
      *
      * @param selector The selector to match.
-     * @param flags Flags to control the determination of whether a route may be
-     *            available. May be zero or some combination of
-     *            {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} and
-     *            {@link #AVAILABILITY_FLAG_REQUIRE_MATCH}.
+     * @param flags Flags to control the determination of whether a route may be available. May be
+     *     zero or some combination of {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} and {@link
+     *     #AVAILABILITY_FLAG_REQUIRE_MATCH}.
      * @return True if a matching route may be available.
      */
     @MainThread
@@ -618,17 +607,16 @@
     }
 
     /**
-     * Registers a callback to discover routes that match the selector and to receive
-     * events when they change.
-     * <p>
-     * This is a convenience method that has the same effect as calling
-     * {@link #addCallback(MediaRouteSelector, Callback, int)} without flags.
-     * </p>
+     * Registers a callback to discover routes that match the selector and to receive events when
+     * they change.
+     *
+     * <p>This is a convenience method that has the same effect as calling {@link
+     * #addCallback(MediaRouteSelector, Callback, int)} without flags.
      *
      * <p>Must be called on the main thread.
      *
-     * @param selector A route selector that indicates the kinds of routes that the
-     * callback would like to discover.
+     * @param selector A route selector that indicates the kinds of routes that the callback would
+     *     like to discover.
      * @param callback The callback to add.
      * @see #removeCallback
      */
@@ -638,49 +626,46 @@
     }
 
     /**
-     * Registers a callback to discover routes that match the selector and to receive
-     * events when they change.
-     * <p>
-     * The selector describes the kinds of routes that the application wants to
-     * discover.  For example, if the application wants to use
-     * live audio routes then it should include the
-     * {@link MediaControlIntent#CATEGORY_LIVE_AUDIO live audio media control intent category}
-     * in its selector when it adds a callback to the media router.
-     * The selector may include any number of categories.
-     * </p><p>
-     * If the callback has already been registered, then the selector is added to
-     * the set of selectors being monitored by the callback.
-     * </p><p>
-     * By default, the callback will only be invoked for events that affect routes
-     * that match the specified selector.  Event filtering may be disabled by specifying
-     * the {@link #CALLBACK_FLAG_UNFILTERED_EVENTS} flag when the callback is registered.
-     * </p><p>
-     * Applications should use the {@link #isRouteAvailable} method to determine
-     * whether is it possible to discover a route with the desired capabilities
-     * and therefore whether the media route button should be shown to the user.
-     * </p><p>
-     * The {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} flag should be used while the application
-     * is in the foreground to request that passive discovery be performed if there are
-     * sufficient resources to allow continuous passive discovery.
-     * On {@link ActivityManager#isLowRamDevice low-RAM devices} this flag will be
-     * ignored to conserve resources.
-     * </p><p>
-     * The {@link #CALLBACK_FLAG_FORCE_DISCOVERY} flag should be used when
-     * passive discovery absolutely must be performed, even on low-RAM devices.
-     * This flag has a significant performance impact on low-RAM devices
-     * since it may cause many media route providers to be started simultaneously.
-     * It is much better to use {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} instead to avoid
-     * performing passive discovery on these devices altogether.
-     * </p><p>
-     * The {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} flag should be used when the
-     * media route chooser dialog is showing to confirm the presence of available
-     * routes that the user may connect to.  This flag may use substantially more
-     * power. Once active scan is requested, it will be effective for 30 seconds and will be
-     * suppressed after the delay. If you need active scan after this duration, you have to add
-     * your callback again with the {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} flag.
-     * </p>
+     * Registers a callback to discover routes that match the selector and to receive events when
+     * they change.
+     *
+     * <p>The selector describes the kinds of routes that the application wants to discover. For
+     * example, if the application wants to use live audio routes then it should include the {@link
+     * MediaControlIntent#CATEGORY_LIVE_AUDIO live audio media control intent category} in its
+     * selector when it adds a callback to the media router. The selector may include any number of
+     * categories.
+     *
+     * <p>If the callback has already been registered, then the selector is added to the set of
+     * selectors being monitored by the callback.
+     *
+     * <p>By default, the callback will only be invoked for events that affect routes that match the
+     * specified selector. Event filtering may be disabled by specifying the {@link
+     * #CALLBACK_FLAG_UNFILTERED_EVENTS} flag when the callback is registered.
+     *
+     * <p>Applications should use the {@link #isRouteAvailable} method to determine whether is it
+     * possible to discover a route with the desired capabilities and therefore whether the media
+     * route button should be shown to the user.
+     *
+     * <p>The {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} flag should be used while the application is
+     * in the foreground to request that passive discovery be performed if there are sufficient
+     * resources to allow continuous passive discovery. On {@link ActivityManager#isLowRamDevice
+     * low-RAM devices} this flag will be ignored to conserve resources.
+     *
+     * <p>The {@link #CALLBACK_FLAG_FORCE_DISCOVERY} flag should be used when passive discovery
+     * absolutely must be performed, even on low-RAM devices. This flag has a significant
+     * performance impact on low-RAM devices since it may cause many media route providers to be
+     * started simultaneously. It is much better to use {@link #CALLBACK_FLAG_REQUEST_DISCOVERY}
+     * instead to avoid performing passive discovery on these devices altogether.
+     *
+     * <p>The {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} flag should be used when the media route
+     * chooser dialog is showing to confirm the presence of available routes that the user may
+     * connect to. This flag may use substantially more power. Once active scan is requested, it
+     * will be effective for 30 seconds and will be suppressed after the delay. If you need active
+     * scan after this duration, you have to add your callback again with the {@link
+     * #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} flag.
      *
      * <h3>Example</h3>
+     *
      * <pre>
      * public class MyActivity extends Activity {
      *     private MediaRouter mRouter;
@@ -738,17 +723,18 @@
      *
      * <p>Must be called on the main thread.
      *
-     * @param selector A route selector that indicates the kinds of routes that the
-     * callback would like to discover.
+     * @param selector A route selector that indicates the kinds of routes that the callback would
+     *     like to discover.
      * @param callback The callback to add.
-     * @param flags Flags to control the behavior of the callback.
-     * May be zero or a combination of {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} and
-     * {@link #CALLBACK_FLAG_UNFILTERED_EVENTS}.
+     * @param flags Flags to control the behavior of the callback. May be zero or a combination of
+     *     {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} and {@link #CALLBACK_FLAG_UNFILTERED_EVENTS}.
      * @see #removeCallback
      */
     // TODO: Change the usages of addCallback() for changing flags when setCallbackFlags() is added.
     @MainThread
-    public void addCallback(@NonNull MediaRouteSelector selector, @NonNull Callback callback,
+    public void addCallback(
+            @NonNull MediaRouteSelector selector,
+            @NonNull Callback callback,
             @CallbackFlags int flags) {
         if (selector == null) {
             throw new IllegalArgumentException("selector must not be null");
@@ -854,7 +840,6 @@
      * <p>Must be called on the main thread.
      *
      * @param providerInstance The media route provider instance to add.
-     *
      * @see MediaRouteProvider
      * @see #removeCallback
      */
@@ -881,7 +866,6 @@
      * <p>Must be called on the main thread.
      *
      * @param providerInstance The media route provider instance to remove.
-     *
      * @see MediaRouteProvider
      * @see #addCallback
      */
@@ -921,7 +905,8 @@
         if (DEBUG) {
             Log.d(TAG, "addRemoteControlClient: " + remoteControlClient);
         }
-        getGlobalRouter().addRemoteControlClient(remoteControlClient);
+        getGlobalRouter()
+                .addRemoteControlClient((android.media.RemoteControlClient) remoteControlClient);
     }
 
     /**
@@ -929,8 +914,7 @@
      *
      * <p>Must be called on the main thread.
      *
-     * @param remoteControlClient The {@link android.media.RemoteControlClient}
-     *            to unregister.
+     * @param remoteControlClient The {@link android.media.RemoteControlClient} to unregister.
      */
     @MainThread
     public void removeRemoteControlClient(@NonNull Object remoteControlClient) {
@@ -942,7 +926,8 @@
         if (DEBUG) {
             Log.d(TAG, "removeRemoteControlClient: " + remoteControlClient);
         }
-        getGlobalRouter().removeRemoteControlClient(remoteControlClient);
+        getGlobalRouter()
+                .removeRemoteControlClient((android.media.RemoteControlClient) remoteControlClient);
     }
 
     /**
@@ -1071,8 +1056,6 @@
         return getGlobalRouter().isMediaTransferEnabled();
     }
 
-    /**
-     */
     @RestrictTo(LIBRARY)
     public static boolean isGroupVolumeUxEnabled() {
         if (sGlobal == null) {
@@ -1134,8 +1117,11 @@
         private List<RouteInfo> mMemberRoutes = new ArrayList<>();
         private Map<String, DynamicRouteDescriptor> mDynamicGroupDescriptors;
 
-        @IntDef({CONNECTION_STATE_DISCONNECTED, CONNECTION_STATE_CONNECTING,
-                CONNECTION_STATE_CONNECTED})
+        @IntDef({
+            CONNECTION_STATE_DISCONNECTED,
+            CONNECTION_STATE_CONNECTING,
+            CONNECTION_STATE_CONNECTED
+        })
         @Retention(RetentionPolicy.SOURCE)
         private @interface ConnectionState {}
 
@@ -1161,7 +1147,7 @@
          */
         public static final int CONNECTION_STATE_CONNECTED = 2;
 
-        @IntDef({PLAYBACK_TYPE_LOCAL,PLAYBACK_TYPE_REMOTE})
+        @IntDef({PLAYBACK_TYPE_LOCAL, PLAYBACK_TYPE_REMOTE})
         @Retention(RetentionPolicy.SOURCE)
         private @interface PlaybackType {}
 
@@ -1290,7 +1276,7 @@
          */
         public static final int DEVICE_TYPE_GROUP = 1000;
 
-        @IntDef({PLAYBACK_VOLUME_FIXED,PLAYBACK_VOLUME_VARIABLE})
+        @IntDef({PLAYBACK_VOLUME_FIXED, PLAYBACK_VOLUME_VARIABLE})
         @Retention(RetentionPolicy.SOURCE)
         private @interface PlaybackVolume {}
 
@@ -1436,7 +1422,6 @@
          * <p>Must be called on the main thread.
          *
          * @return True if this route is currently selected.
-         *
          * @see MediaRouter#getSelectedRoute
          */
         // Note: Only one representative route can return true. For instance:
@@ -1455,7 +1440,6 @@
          * <p>Must be called on the main thread.
          *
          * @return True if this route is the default route.
-         *
          * @see MediaRouter#getDefaultRoute
          */
         @MainThread
@@ -1470,7 +1454,6 @@
          * <p>Must be called on the main thread.
          *
          * @return True if this route is a bluetooth route.
-         *
          * @see MediaRouter#getBluetoothRoute
          */
         @MainThread
@@ -1498,7 +1481,6 @@
          *
          * @return A list of intent filters that specifies the media control intents that
          * this route supports.
-         *
          * @see MediaControlIntent
          * @see #supportsControlCategory
          * @see #supportsControlRequest
@@ -1528,22 +1510,20 @@
         }
 
         /**
-         * Returns true if the route supports the specified
-         * {@link MediaControlIntent media control} category.
-         * <p>
-         * Media control categories describe the capabilities of this route
-         * such as whether it supports live audio streaming or remote playback.
-         * </p>
+         * Returns true if the route supports the specified {@link MediaControlIntent media control}
+         * category.
+         *
+         * <p>Media control categories describe the capabilities of this route such as whether it
+         * supports live audio streaming or remote playback.
          *
          * <p>Must be called on the main thread.
          *
-         * @param category A {@link MediaControlIntent media control} category
-         * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO},
-         * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO},
-         * {@link MediaControlIntent#CATEGORY_REMOTE_PLAYBACK}, or a provider-defined
-         * media control category.
+         * @param category A {@link MediaControlIntent media control} category such as {@link
+         *     MediaControlIntent#CATEGORY_LIVE_AUDIO}, {@link
+         *     MediaControlIntent#CATEGORY_LIVE_VIDEO}, {@link
+         *     MediaControlIntent#CATEGORY_REMOTE_PLAYBACK}, or a provider-defined media control
+         *     category.
          * @return True if the route supports the specified intent category.
-         *
          * @see MediaControlIntent
          * @see #getControlFilters
          */
@@ -1554,9 +1534,8 @@
             }
             checkCallingThread();
 
-            int count = mControlFilters.size();
-            for (int i = 0; i < count; i++) {
-                if (mControlFilters.get(i).hasCategory(category)) {
+            for (IntentFilter intentFilter: mControlFilters) {
+                if (intentFilter.hasCategory(category)) {
                     return true;
                 }
             }
@@ -1564,24 +1543,22 @@
         }
 
         /**
-         * Returns true if the route supports the specified
-         * {@link MediaControlIntent media control} category and action.
-         * <p>
-         * Media control actions describe specific requests that an application
-         * can ask a route to perform.
-         * </p>
+         * Returns true if the route supports the specified {@link MediaControlIntent media control}
+         * category and action.
+         *
+         * <p>Media control actions describe specific requests that an application can ask a route
+         * to perform.
          *
          * <p>Must be called on the main thread.
          *
-         * @param category A {@link MediaControlIntent media control} category
-         * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO},
-         * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO},
-         * {@link MediaControlIntent#CATEGORY_REMOTE_PLAYBACK}, or a provider-defined
-         * media control category.
-         * @param action A {@link MediaControlIntent media control} action
-         * such as {@link MediaControlIntent#ACTION_PLAY}.
+         * @param category A {@link MediaControlIntent media control} category such as {@link
+         *     MediaControlIntent#CATEGORY_LIVE_AUDIO}, {@link
+         *     MediaControlIntent#CATEGORY_LIVE_VIDEO}, {@link
+         *     MediaControlIntent#CATEGORY_REMOTE_PLAYBACK}, or a provider-defined media control
+         *     category.
+         * @param action A {@link MediaControlIntent media control} action such as {@link
+         *     MediaControlIntent#ACTION_PLAY}.
          * @return True if the route supports the specified intent action.
-         *
          * @see MediaControlIntent
          * @see #getControlFilters
          */
@@ -1595,10 +1572,8 @@
             }
             checkCallingThread();
 
-            int count = mControlFilters.size();
-            for (int i = 0; i < count; i++) {
-                IntentFilter filter = mControlFilters.get(i);
-                if (filter.hasCategory(category) && filter.hasAction(action)) {
+            for (IntentFilter intentFilter: mControlFilters) {
+                if (intentFilter.hasCategory(category) && intentFilter.hasAction(action)) {
                     return true;
                 }
             }
@@ -1617,7 +1592,6 @@
          *
          * @param intent A {@link MediaControlIntent media control intent}.
          * @return True if the route can handle the specified intent.
-         *
          * @see MediaControlIntent
          * @see #getControlFilters
          */
@@ -1629,9 +1603,8 @@
             checkCallingThread();
 
             ContentResolver contentResolver = getGlobalRouter().getContentResolver();
-            int count = mControlFilters.size();
-            for (int i = 0; i < count; i++) {
-                if (mControlFilters.get(i).match(contentResolver, intent, true, TAG) >= 0) {
+            for (IntentFilter intentFilter: mControlFilters) {
+                if (intentFilter.match(contentResolver, intent, true, TAG) >= 0) {
                     return true;
                 }
             }
@@ -1639,27 +1612,25 @@
         }
 
         /**
-         * Sends a {@link MediaControlIntent media control} request to be performed
-         * asynchronously by the route's destination.
-         * <p>
-         * Media control requests are used to request the route to perform
-         * actions such as starting remote playback of a media item.
-         * </p><p>
-         * This function may only be called on a selected route.  Control requests
-         * sent to unselected routes will fail.
-         * </p>
+         * Sends a {@link MediaControlIntent media control} request to be performed asynchronously
+         * by the route's destination.
+         *
+         * <p>Media control requests are used to request the route to perform actions such as
+         * starting remote playback of a media item.
+         *
+         * <p>This function may only be called on a selected route. Control requests sent to
+         * unselected routes will fail.
          *
          * <p>Must be called on the main thread.
          *
          * @param intent A {@link MediaControlIntent media control intent}.
-         * @param callback A {@link ControlRequestCallback} to invoke with the result
-         * of the request, or null if no result is required.
-         *
+         * @param callback A {@link ControlRequestCallback} to invoke with the result of the
+         *     request, or null if no result is required.
          * @see MediaControlIntent
          */
         @MainThread
-        public void sendControlRequest(@NonNull Intent intent,
-                @Nullable ControlRequestCallback callback) {
+        public void sendControlRequest(
+                @NonNull Intent intent, @Nullable ControlRequestCallback callback) {
             if (intent == null) {
                 throw new IllegalArgumentException("intent must not be null");
             }
@@ -1698,8 +1669,7 @@
             return mDeviceType;
         }
 
-        /**
-         */
+        /** */
         @RestrictTo(LIBRARY)
         public boolean isDefaultOrBluetooth() {
             if (isDefault() || mDeviceType == DEVICE_TYPE_BLUETOOTH) {
@@ -1761,11 +1731,9 @@
         }
 
         /**
-         * Gets whether this route supports disconnecting without interrupting
-         * playback.
+         * Gets whether this route supports disconnecting without interrupting playback.
          *
-         * @return True if this route can disconnect without stopping playback,
-         *         false otherwise.
+         * @return True if this route can disconnect without stopping playback, false otherwise.
          */
         public boolean canDisconnect() {
             return mCanDisconnect;
@@ -1834,7 +1802,6 @@
          *
          * @return The preferred presentation display to use when this route is
          * selected or null if none.
-         *
          * @see MediaControlIntent#CATEGORY_LIVE_VIDEO
          * @see android.app.Presentation
          */
@@ -2180,6 +2147,7 @@
             DynamicGroupState(DynamicRouteDescriptor descriptor) {
                 mDynamicDescriptor = descriptor;
             }
+
             /**
              * Gets the selection state of the route when the {@link MediaRouteProvider} of the
              * route supports
@@ -2194,22 +2162,16 @@
                         : DynamicRouteDescriptor.UNSELECTED;
             }
 
-            /**
-             */
             @RestrictTo(LIBRARY)
             public boolean isUnselectable() {
                 return mDynamicDescriptor == null || mDynamicDescriptor.isUnselectable();
             }
 
-            /**
-             */
             @RestrictTo(LIBRARY)
             public boolean isGroupable() {
                 return mDynamicDescriptor != null && mDynamicDescriptor.isGroupable();
             }
 
-            /**
-             */
             @RestrictTo(LIBRARY)
             public boolean isTransferable() {
                 return mDynamicDescriptor != null && mDynamicDescriptor.isTransferable();
@@ -2302,10 +2264,9 @@
         }
 
         RouteInfo findRouteByDescriptorId(String id) {
-            final int count = mRoutes.size();
-            for (int i = 0; i < count; i++) {
-                if (mRoutes.get(i).mDescriptorId.equals(id)) {
-                    return mRoutes.get(i);
+            for (RouteInfo route: mRoutes) {
+                if (route.mDescriptorId.equals(id)) {
+                    return route;
                 }
             }
             return null;
@@ -2318,8 +2279,7 @@
         @NonNull
         @Override
         public String toString() {
-            return "MediaRouter.RouteProviderInfo{ packageName=" + getPackageName()
-                    + " }";
+            return "MediaRouter.RouteProviderInfo{ packageName=" + getPackageName() + " }";
         }
     }
 
@@ -2344,80 +2304,82 @@
          * @deprecated Use {@link #onRouteSelected(MediaRouter, RouteInfo, int)} instead.
          */
         @Deprecated
-        public void onRouteSelected(@NonNull MediaRouter router, @NonNull RouteInfo route) {
-        }
+        public void onRouteSelected(@NonNull MediaRouter router, @NonNull RouteInfo route) {}
 
         /**
          * Called when the supplied media route becomes selected as the active route.
-         * <p>
-         * The reason provided will be one of the following:
+         *
+         * <p>The reason provided will be one of the following:
+         *
          * <ul>
-         * <li>{@link MediaRouter#UNSELECT_REASON_UNKNOWN}</li>
-         * <li>{@link MediaRouter#UNSELECT_REASON_DISCONNECTED}</li>
-         * <li>{@link MediaRouter#UNSELECT_REASON_STOPPED}</li>
-         * <li>{@link MediaRouter#UNSELECT_REASON_ROUTE_CHANGED}</li>
+         *   <li>{@link MediaRouter#UNSELECT_REASON_UNKNOWN}
+         *   <li>{@link MediaRouter#UNSELECT_REASON_DISCONNECTED}
+         *   <li>{@link MediaRouter#UNSELECT_REASON_STOPPED}
+         *   <li>{@link MediaRouter#UNSELECT_REASON_ROUTE_CHANGED}
          * </ul>
          *
          * @param router The media router reporting the event.
          * @param route The route that has been selected.
          * @param reason The reason for unselecting the previous route.
          */
-        public void onRouteSelected(@NonNull MediaRouter router, @NonNull RouteInfo route,
-                @UnselectReason int reason) {
+        public void onRouteSelected(
+                @NonNull MediaRouter router, @NonNull RouteInfo route, @UnselectReason int reason) {
             onRouteSelected(router, route);
         }
 
-        //TODO: Revise the comment when we have a feature that enables dynamic grouping on pre-R
+        // TODO: Revise the comment when we have a feature that enables dynamic grouping on pre-R
         // devices.
+
         /**
-         * Called when the supplied media route becomes selected as the active route, which
-         * may be different from the route requested by {@link #selectRoute(RouteInfo)}.
-         * That can happen when {@link MediaTransferReceiver media transfer feature} is enabled.
-         * The default implementation calls {@link #onRouteSelected(MediaRouter, RouteInfo, int)}
-         * with the actually selected route.
+         * Called when the supplied media route becomes selected as the active route, which may be
+         * different from the route requested by {@link #selectRoute(RouteInfo)}. That can happen
+         * when {@link MediaTransferReceiver media transfer feature} is enabled. The default
+         * implementation calls {@link #onRouteSelected(MediaRouter, RouteInfo, int)} with the
+         * actually selected route.
          *
          * @param router The media router reporting the event.
          * @param selectedRoute The route that has been selected.
          * @param reason The reason for unselecting the previous route.
          * @param requestedRoute The route that was requested to be selected.
          */
-        public void onRouteSelected(@NonNull MediaRouter router,
-                @NonNull RouteInfo selectedRoute, @UnselectReason int reason,
+        public void onRouteSelected(
+                @NonNull MediaRouter router,
+                @NonNull RouteInfo selectedRoute,
+                @UnselectReason int reason,
                 @NonNull RouteInfo requestedRoute) {
             onRouteSelected(router, selectedRoute, reason);
         }
 
         /**
-         * Called when the supplied media route becomes unselected as the active route.
-         * For detailed reason, override {@link #onRouteUnselected(MediaRouter, RouteInfo, int)}
-         * instead.
+         * Called when the supplied media route becomes unselected as the active route. For detailed
+         * reason, override {@link #onRouteUnselected(MediaRouter, RouteInfo, int)} instead.
          *
          * @param router The media router reporting the event.
          * @param route The route that has been unselected.
          * @deprecated Use {@link #onRouteUnselected(MediaRouter, RouteInfo, int)} instead.
          */
         @Deprecated
-        public void onRouteUnselected(@NonNull MediaRouter router, @NonNull RouteInfo route) {
-        }
+        public void onRouteUnselected(@NonNull MediaRouter router, @NonNull RouteInfo route) {}
 
         /**
-         * Called when the supplied media route becomes unselected as the active route.
-         * The default implementation calls {@link #onRouteUnselected}.
-         * <p>
-         * The reason provided will be one of the following:
+         * Called when the supplied media route becomes unselected as the active route. The default
+         * implementation calls {@link #onRouteUnselected}.
+         *
+         * <p>The reason provided will be one of the following:
+         *
          * <ul>
-         * <li>{@link MediaRouter#UNSELECT_REASON_UNKNOWN}</li>
-         * <li>{@link MediaRouter#UNSELECT_REASON_DISCONNECTED}</li>
-         * <li>{@link MediaRouter#UNSELECT_REASON_STOPPED}</li>
-         * <li>{@link MediaRouter#UNSELECT_REASON_ROUTE_CHANGED}</li>
+         *   <li>{@link MediaRouter#UNSELECT_REASON_UNKNOWN}
+         *   <li>{@link MediaRouter#UNSELECT_REASON_DISCONNECTED}
+         *   <li>{@link MediaRouter#UNSELECT_REASON_STOPPED}
+         *   <li>{@link MediaRouter#UNSELECT_REASON_ROUTE_CHANGED}
          * </ul>
          *
          * @param router The media router reporting the event.
          * @param route The route that has been unselected.
          * @param reason The reason for unselecting the route.
          */
-        public void onRouteUnselected(@NonNull MediaRouter router, @NonNull RouteInfo route,
-                @UnselectReason int reason) {
+        public void onRouteUnselected(
+                @NonNull MediaRouter router, @NonNull RouteInfo route, @UnselectReason int reason) {
             onRouteUnselected(router, route);
         }
 
@@ -2427,8 +2389,7 @@
          * @param router The media router reporting the event.
          * @param route The route that has become available for use.
          */
-        public void onRouteAdded(@NonNull MediaRouter router, @NonNull RouteInfo route) {
-        }
+        public void onRouteAdded(@NonNull MediaRouter router, @NonNull RouteInfo route) {}
 
         /**
          * Called when a media route has been removed.
@@ -2436,8 +2397,7 @@
          * @param router The media router reporting the event.
          * @param route The route that has been removed from availability.
          */
-        public void onRouteRemoved(@NonNull MediaRouter router, @NonNull RouteInfo route) {
-        }
+        public void onRouteRemoved(@NonNull MediaRouter router, @NonNull RouteInfo route) {}
 
         /**
          * Called when a property of the indicated media route has changed.
@@ -2445,8 +2405,7 @@
          * @param router The media router reporting the event.
          * @param route The route that was changed.
          */
-        public void onRouteChanged(@NonNull MediaRouter router, @NonNull RouteInfo route) {
-        }
+        public void onRouteChanged(@NonNull MediaRouter router, @NonNull RouteInfo route) {}
 
         /**
          * Called when a media route's volume changes.
@@ -2454,24 +2413,20 @@
          * @param router The media router reporting the event.
          * @param route The route whose volume changed.
          */
-        public void onRouteVolumeChanged(@NonNull MediaRouter router, @NonNull RouteInfo route) {
-        }
+        public void onRouteVolumeChanged(@NonNull MediaRouter router, @NonNull RouteInfo route) {}
 
         /**
          * Called when a media route's presentation display changes.
-         * <p>
-         * This method is called whenever the route's presentation display becomes
-         * available, is removed or has changes to some of its properties (such as its size).
-         * </p>
+         *
+         * <p>This method is called whenever the route's presentation display becomes available, is
+         * removed or has changes to some of its properties (such as its size).
          *
          * @param router The media router reporting the event.
          * @param route The route whose presentation display changed.
-         *
          * @see RouteInfo#getPresentationDisplay()
          */
-        public void onRoutePresentationDisplayChanged(@NonNull MediaRouter router,
-                @NonNull RouteInfo route) {
-        }
+        public void onRoutePresentationDisplayChanged(
+                @NonNull MediaRouter router, @NonNull RouteInfo route) {}
 
         /**
          * Called when a media route provider has been added.
@@ -2479,8 +2434,7 @@
          * @param router The media router reporting the event.
          * @param provider The provider that has become available for use.
          */
-        public void onProviderAdded(@NonNull MediaRouter router, @NonNull ProviderInfo provider) {
-        }
+        public void onProviderAdded(@NonNull MediaRouter router, @NonNull ProviderInfo provider) {}
 
         /**
          * Called when a media route provider has been removed.
@@ -2488,8 +2442,8 @@
          * @param router The media router reporting the event.
          * @param provider The provider that has been removed from availability.
          */
-        public void onProviderRemoved(@NonNull MediaRouter router, @NonNull ProviderInfo provider) {
-        }
+        public void onProviderRemoved(
+                @NonNull MediaRouter router, @NonNull ProviderInfo provider) {}
 
         /**
          * Called when a property of the indicated media route provider has changed.
@@ -2497,15 +2451,13 @@
          * @param router The media router reporting the event.
          * @param provider The provider that was changed.
          */
-        public void onProviderChanged(@NonNull MediaRouter router, @NonNull ProviderInfo provider) {
-        }
+        public void onProviderChanged(
+                @NonNull MediaRouter router, @NonNull ProviderInfo provider) {}
 
-        /**
-         */
+        /** */
         @RestrictTo(LIBRARY)
-        public void onRouterParamsChanged(@NonNull MediaRouter router,
-                @Nullable MediaRouterParams params) {
-        }
+        public void onRouterParamsChanged(
+                @NonNull MediaRouter router, @Nullable MediaRouterParams params) {}
     }
 
     /**
@@ -2516,28 +2468,27 @@
     public interface OnPrepareTransferListener {
         /**
          * Implement this to handle transfer seamlessly.
-         * <p>
-         * Setting the listener will defer stopping the previous route, from which you may
-         * get the media status to resume media seamlessly on the new route.
-         * When the transfer is prepared, set the returned future to stop media being played
-         * on the previous route and release resources.
-         * This method is called on the main thread.
-         * <p>
-         * {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} and
-         * {@link Callback#onRouteSelected(MediaRouter, RouteInfo, int)} are called after
-         * the future is done.
+         *
+         * <p>Setting the listener will defer stopping the previous route, from which you may get
+         * the media status to resume media seamlessly on the new route. When the transfer is
+         * prepared, set the returned future to stop media being played on the previous route and
+         * release resources. This method is called on the main thread.
+         *
+         * <p>{@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} and {@link
+         * Callback#onRouteSelected(MediaRouter, RouteInfo, int)} are called after the future is
+         * done.
          *
          * @param fromRoute The route that is about to be unselected.
          * @param toRoute The route that is about to be selected.
-         * @return A {@link ListenableFuture} whose completion indicates that the
-         * transfer is prepared or {@code null} to indicate that no preparation is needed.
-         * If a future is returned, until the future is completed,
-         * the media continues to be played on the previous route.
+         * @return A {@link ListenableFuture} whose completion indicates that the transfer is
+         *     prepared or {@code null} to indicate that no preparation is needed. If a future is
+         *     returned, until the future is completed, the media continues to be played on the
+         *     previous route.
          */
         @MainThread
         @Nullable
-        ListenableFuture<Void> onPrepareTransfer(@NonNull RouteInfo fromRoute,
-                @NonNull RouteInfo toRoute);
+        ListenableFuture<Void> onPrepareTransfer(
+                @NonNull RouteInfo fromRoute, @NonNull RouteInfo toRoute);
     }
 
     /**
@@ -2549,22 +2500,20 @@
         /**
          * Called when a media control request succeeds.
          *
-         * @param data Result data, or null if none.
-         * Contents depend on the {@link MediaControlIntent media control action}.
+         * @param data Result data, or null if none. Contents depend on the {@link
+         *     MediaControlIntent media control action}.
          */
-        public void onResult(@Nullable Bundle data) {
-        }
+        public void onResult(@Nullable Bundle data) {}
 
         /**
          * Called when a media control request fails.
          *
-         * @param error A localized error message which may be shown to the user, or null
-         * if the cause of the error is unclear.
-         * @param data Error data, or null if none.
-         * Contents depend on the {@link MediaControlIntent media control action}.
+         * @param error A localized error message which may be shown to the user, or null if the
+         *     cause of the error is unclear.
+         * @param data Error data, or null if none. Contents depend on the {@link MediaControlIntent
+         *     media control action}.
          */
-        public void onError(@Nullable String error, @Nullable Bundle data) {
-        }
+        public void onError(@Nullable String error, @Nullable Bundle data) {}
     }
 
     private static final class CallbackRecord {
@@ -2612,7 +2561,7 @@
             implements SystemMediaRouteProvider.SyncCallback,
             RegisteredMediaRouteProviderWatcher.Callback {
         final Context mApplicationContext;
-        boolean mIsInitialized;
+        private boolean mIsInitialized;
 
         SystemMediaRouteProvider mSystemProvider;
         @VisibleForTesting
@@ -2650,31 +2599,32 @@
         private int mCallbackCount;
         OnPrepareTransferListener mOnPrepareTransferListener;
         PrepareTransferNotifier mTransferNotifier;
-        RouteInfo mTransferredRoute;
-        RouteController mTransferredRouteController;
-
         private MediaSessionRecord mMediaSession;
         MediaSessionCompat mRccMediaSession;
         private MediaSessionCompat mCompatSession;
         private final MediaSessionCompat.OnActiveChangeListener mSessionActiveListener =
                 new MediaSessionCompat.OnActiveChangeListener() {
-            @Override
-            public void onActiveChanged() {
-                if(mRccMediaSession != null) {
-                    if (mRccMediaSession.isActive()) {
-                        addRemoteControlClient(mRccMediaSession.getRemoteControlClient());
-                    } else {
-                        removeRemoteControlClient(mRccMediaSession.getRemoteControlClient());
+                    @Override
+                    public void onActiveChanged() {
+                        if (mRccMediaSession != null) {
+                            android.media.RemoteControlClient remoteControlClient =
+                                    (android.media.RemoteControlClient)
+                                            mRccMediaSession.getRemoteControlClient();
+                            if (mRccMediaSession.isActive()) {
+                                addRemoteControlClient(remoteControlClient);
+                            } else {
+                                removeRemoteControlClient(remoteControlClient);
+                            }
+                        }
                     }
-                }
-            }
-        };
+                };
 
         GlobalMediaRouter(Context applicationContext) {
             mApplicationContext = applicationContext;
-            mLowRam = ActivityManagerCompat.isLowRamDevice(
-                    (ActivityManager)applicationContext.getSystemService(
-                            Context.ACTIVITY_SERVICE));
+            mLowRam =
+                    ActivityManagerCompat.isLowRamDevice(
+                            (ActivityManager)
+                                    applicationContext.getSystemService(Context.ACTIVITY_SERVICE));
         }
 
         @SuppressLint({"NewApi", "SyntheticAccessor"})
@@ -2706,13 +2656,8 @@
 
         private void start() {
             // Using lambda would break some apps.
-            mActiveScanThrottlingHelper = new MediaRouterActiveScanThrottlingHelper(
-                    new Runnable() {
-                        @Override
-                        public void run() {
-                            updateDiscoveryRequest();
-                        }
-                    });
+            mActiveScanThrottlingHelper =
+                    new MediaRouterActiveScanThrottlingHelper(this::updateDiscoveryRequest);
             addProvider(mSystemProvider, /* treatRouteDescriptorIdsAsUnique= */ true);
             if (mMr2Provider != null) {
                 addProvider(mMr2Provider, /* treatRouteDescriptorIdsAsUnique= */ true);
@@ -2757,7 +2702,7 @@
                 }
             }
             router = new MediaRouter(context);
-            mRouters.add(new WeakReference<MediaRouter>(router));
+            mRouters.add(new WeakReference<>(router));
             return router;
         }
 
@@ -2765,18 +2710,6 @@
             return mApplicationContext.getContentResolver();
         }
 
-        public Context getProviderContext(String packageName) {
-            if (packageName.equals(SystemMediaRouteProvider.PACKAGE_NAME)) {
-                return mApplicationContext;
-            }
-            try {
-                return mApplicationContext.createPackageContext(
-                        packageName, Context.CONTEXT_RESTRICTED);
-            } catch (NameNotFoundException ex) {
-                return null;
-            }
-        }
-
         public Display getDisplay(int displayId) {
             if (mDisplayManager == null) {
                 mDisplayManager = DisplayManagerCompat.getInstance(mApplicationContext);
@@ -2891,7 +2824,8 @@
             return mProviders;
         }
 
-        @NonNull RouteInfo getDefaultRoute() {
+        @NonNull
+        RouteInfo getDefaultRoute() {
             if (mDefaultRoute == null) {
                 // This should never happen once the media router has been fully
                 // initialized but it is good to check for the error in case there
@@ -2906,7 +2840,8 @@
             return mBluetoothRoute;
         }
 
-        @NonNull RouteInfo getSelectedRoute() {
+        @NonNull
+        RouteInfo getSelectedRoute() {
             if (mSelectedRoute == null) {
                 // This should never happen once the media router has been fully
                 // initialized but it is good to check for the error in case there
@@ -3065,7 +3000,7 @@
 
             boolean activeScan =
                     mActiveScanThrottlingHelper
-                    .finalizeActiveScanAndScheduleSuppressActiveScanRunnable();
+                            .finalizeActiveScanAndScheduleSuppressActiveScanRunnable();
 
             mCallbackCount = callbackCount;
             MediaRouteSelector selector = discover ? builder.build() : MediaRouteSelector.EMPTY;
@@ -3101,9 +3036,8 @@
             }
 
             // Notify providers.
-            final int providerCount = mProviders.size();
-            for (int i = 0; i < providerCount; i++) {
-                MediaRouteProvider provider = mProviders.get(i).mProviderInstance;
+            for (ProviderInfo providerInfo: mProviders) {
+                MediaRouteProvider provider = providerInfo.mProviderInstance;
                 if (provider == mMr2Provider) {
                     // MediaRoute2Provider is handled by updateMr2ProviderDiscoveryRequest().
                     continue;
@@ -3159,11 +3093,11 @@
             return mRouterParams.isTransferToLocalEnabled();
         }
 
-        /**
-         */
+        /** */
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         public boolean isGroupVolumeUxEnabled() {
-            return mRouterParams == null || mRouterParams.mExtras == null
+            return mRouterParams == null
+                    || mRouterParams.mExtras == null
                     || mRouterParams.mExtras.getBoolean(
                             MediaRouterParams.ENABLE_GROUP_VOLUME_UX, true);
         }
@@ -3219,7 +3153,7 @@
             if (mSelectedRouteController == controller) {
                 selectRoute(chooseFallbackRoute(), UNSELECT_REASON_STOPPED);
             }
-            //TODO: Maybe release a member route controller if the given controller is a member of
+            // TODO: Maybe release a member route controller if the given controller is a member of
             // the selected route.
         }
 
@@ -3233,10 +3167,9 @@
         }
 
         private ProviderInfo findProviderInfo(MediaRouteProvider providerInstance) {
-            final int count = mProviders.size();
-            for (int i = 0; i < count; i++) {
-                if (mProviders.get(i).mProviderInstance == providerInstance) {
-                    return mProviders.get(i);
+            for (ProviderInfo providerInfo: mProviders) {
+                if (providerInfo.mProviderInstance == providerInstance) {
+                    return providerInfo;
                 }
             }
             return null;
@@ -3497,8 +3430,7 @@
 
         private boolean isSystemDefaultRoute(RouteInfo route) {
             return route.getProviderInstance() == mSystemProvider
-                    && route.mDescriptorId.equals(
-                            SystemMediaRouteProvider.DEFAULT_ROUTE_ID);
+                    && route.mDescriptorId.equals(SystemMediaRouteProvider.DEFAULT_ROUTE_ID);
         }
 
         void selectRouteInternal(@NonNull RouteInfo route,
@@ -3574,11 +3506,18 @@
             if (mSelectedRoute == null) {
                 mSelectedRoute = route;
                 mSelectedRouteController = routeController;
-                mCallbackHandler.post(GlobalMediaRouter.CallbackHandler.MSG_ROUTE_SELECTED,
-                            new Pair<>(null, route), unselectReason);
+                mCallbackHandler.post(
+                        GlobalMediaRouter.CallbackHandler.MSG_ROUTE_SELECTED,
+                        new Pair<>(null, route),
+                        unselectReason);
             } else {
-                notifyTransfer(this, route, routeController, unselectReason,
-                                /*requestedRoute=*/null, /*memberRoutes=*/null);
+                notifyTransfer(
+                        this,
+                        route,
+                        routeController,
+                        unselectReason,
+                        /* requestedRoute= */ null,
+                        /* memberRoutes= */ null);
             }
         }
 
@@ -3691,7 +3630,7 @@
             }
         }
 
-        public void addRemoteControlClient(Object rcc) {
+        public void addRemoteControlClient(android.media.RemoteControlClient rcc) {
             int index = findRemoteControlClientRecord(rcc);
             if (index < 0) {
                 RemoteControlClientRecord record = new RemoteControlClientRecord(rcc);
@@ -3699,7 +3638,7 @@
             }
         }
 
-        public void removeRemoteControlClient(Object rcc) {
+        public void removeRemoteControlClient(android.media.RemoteControlClient rcc) {
             int index = findRemoteControlClientRecord(rcc);
             if (index >= 0) {
                 RemoteControlClientRecord record = mRemoteControlClients.remove(index);
@@ -3717,14 +3656,18 @@
                 setMediaSessionRecord(session != null ? new MediaSessionRecord(session) : null);
             } else {
                 if (mRccMediaSession != null) {
-                    removeRemoteControlClient(mRccMediaSession.getRemoteControlClient());
+                    removeRemoteControlClient(
+                            (android.media.RemoteControlClient)
+                                    mRccMediaSession.getRemoteControlClient());
                     mRccMediaSession.removeOnActiveChangeListener(mSessionActiveListener);
                 }
                 mRccMediaSession = session;
                 if (session != null) {
                     session.addOnActiveChangeListener(mSessionActiveListener);
                     if (session.isActive()) {
-                        addRemoteControlClient(session.getRemoteControlClient());
+                        addRemoteControlClient(
+                                (android.media.RemoteControlClient)
+                                        session.getRemoteControlClient());
                     }
                 }
             }
@@ -3749,7 +3692,7 @@
             return null;
         }
 
-        private int findRemoteControlClientRecord(Object rcc) {
+        private int findRemoteControlClientRecord(android.media.RemoteControlClient rcc) {
             final int count = mRemoteControlClients.size();
             for (int i = 0; i < count; i++) {
                 RemoteControlClientRecord record = mRemoteControlClients.get(i);
@@ -3776,10 +3719,8 @@
                     mPlaybackInfo.volumeControlId = null;
                 }
 
-                final int count = mRemoteControlClients.size();
-                for (int i = 0; i < count; i++) {
-                    RemoteControlClientRecord record = mRemoteControlClients.get(i);
-                    record.updatePlaybackInfo();
+                for (RemoteControlClientRecord remoteControlClientRecord: mRemoteControlClients) {
+                    remoteControlClientRecord.updatePlaybackInfo();
                 }
                 if (mMediaSession != null) {
                     if (mSelectedRoute == getDefaultRoute()
@@ -3942,13 +3883,13 @@
             private final RemoteControlClientCompat mRccCompat;
             private boolean mDisconnected;
 
-            public RemoteControlClientRecord(Object rcc) {
+            RemoteControlClientRecord(android.media.RemoteControlClient rcc) {
                 mRccCompat = RemoteControlClientCompat.obtain(mApplicationContext, rcc);
                 mRccCompat.setVolumeCallback(this);
                 updatePlaybackInfo();
             }
 
-            public Object getRemoteControlClient() {
+            public android.media.RemoteControlClient getRemoteControlClient() {
                 return mRccCompat.getRemoteControlClient();
             }
 
@@ -4093,10 +4034,12 @@
                     case MSG_TYPE_ROUTE: {
                         final RouteInfo route =
                                 (what == MSG_ROUTE_ANOTHER_SELECTED || what == MSG_ROUTE_SELECTED)
-                                ? ((Pair<RouteInfo, RouteInfo>) obj).second : (RouteInfo) obj;
+                                        ? ((Pair<RouteInfo, RouteInfo>) obj).second
+                                        : (RouteInfo) obj;
                         final RouteInfo optionalRoute =
                                 (what == MSG_ROUTE_ANOTHER_SELECTED || what == MSG_ROUTE_SELECTED)
-                                ? ((Pair<RouteInfo, RouteInfo>) obj).first : null;
+                                        ? ((Pair<RouteInfo, RouteInfo>) obj).first
+                                        : null;
                         if (route == null || !record.filterRouteEvent(
                                 route, what, optionalRoute, arg)) {
                             break;
@@ -4130,7 +4073,7 @@
                         break;
                     }
                     case MSG_TYPE_PROVIDER: {
-                        final ProviderInfo provider = (ProviderInfo)obj;
+                        final ProviderInfo provider = (ProviderInfo) obj;
                         switch (what) {
                             case MSG_PROVIDER_ADDED:
                                 callback.onProviderAdded(router, provider);
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouterJellybean.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouterJellybean.java
index f1e7a65..0494663 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouterJellybean.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouterJellybean.java
@@ -18,7 +18,7 @@
 
 import android.annotation.SuppressLint;
 import android.content.Context;
-import android.graphics.drawable.Drawable;
+import android.media.MediaRouter;
 import android.os.Build;
 import android.util.Log;
 
@@ -35,11 +35,6 @@
 final class MediaRouterJellybean {
     private static final String TAG = "MediaRouterJellybean";
 
-    // android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP = 0x80;
-    // android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100;
-    // android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200;
-    public static final int DEVICE_OUT_BLUETOOTH = 0x80 | 0x100 | 0x200;
-
     public static final int ROUTE_TYPE_LIVE_AUDIO = 0x1;
     public static final int ROUTE_TYPE_LIVE_VIDEO = 0x2;
     public static final int ROUTE_TYPE_USER = 0x00800000;
@@ -49,274 +44,200 @@
                     | MediaRouterJellybean.ROUTE_TYPE_LIVE_VIDEO
                     | MediaRouterJellybean.ROUTE_TYPE_USER;
 
-    public static Object getMediaRouter(Context context) {
-        return context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
+    public static android.media.MediaRouter getMediaRouter(Context context) {
+        return (android.media.MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
     }
 
-    @SuppressWarnings({"rawtypes", "unchecked"})
-    public static List getRoutes(Object routerObj) {
-        final android.media.MediaRouter router = (android.media.MediaRouter) routerObj;
+    public static List<MediaRouter.RouteInfo> getRoutes(android.media.MediaRouter router) {
         final int count = router.getRouteCount();
-        List out = new ArrayList(count);
+        List<MediaRouter.RouteInfo> out = new ArrayList<>(count);
         for (int i = 0; i < count; i++) {
             out.add(router.getRouteAt(i));
         }
         return out;
     }
 
-    @SuppressWarnings({"rawtypes", "unchecked"})
-    public static List getCategories(Object routerObj) {
-        final android.media.MediaRouter router = (android.media.MediaRouter) routerObj;
-        final int count = router.getCategoryCount();
-        List out = new ArrayList(count);
-        for (int i = 0; i < count; i++) {
-            out.add(router.getCategoryAt(i));
-        }
-        return out;
+    public static MediaRouter.RouteInfo getSelectedRoute(android.media.MediaRouter router,
+            int type) {
+        return router.getSelectedRoute(type);
     }
 
-    public static Object getSelectedRoute(Object routerObj, int type) {
-        return ((android.media.MediaRouter) routerObj).getSelectedRoute(type);
+    public static void selectRoute(android.media.MediaRouter router, int types,
+            android.media.MediaRouter.RouteInfo route) {
+        router.selectRoute(types, route);
     }
 
-    public static void selectRoute(Object routerObj, int types, Object routeObj) {
-        ((android.media.MediaRouter) routerObj).selectRoute(types,
-                (android.media.MediaRouter.RouteInfo) routeObj);
+    public static void addCallback(android.media.MediaRouter router, int types,
+            android.media.MediaRouter.Callback callback) {
+        router.addCallback(types, callback);
     }
 
-    public static void addCallback(Object routerObj, int types, Object callbackObj) {
-        ((android.media.MediaRouter) routerObj).addCallback(types,
-                (android.media.MediaRouter.Callback) callbackObj);
+    public static void removeCallback(android.media.MediaRouter router,
+            android.media.MediaRouter.Callback callback) {
+        router.removeCallback(callback);
     }
 
-    public static void removeCallback(Object routerObj, Object callbackObj) {
-        ((android.media.MediaRouter) routerObj).removeCallback(
-                (android.media.MediaRouter.Callback) callbackObj);
-    }
-
-    public static Object createRouteCategory(Object routerObj,
+    public static android.media.MediaRouter.RouteCategory createRouteCategory(
+            android.media.MediaRouter router,
             String name, boolean isGroupable) {
-        return ((android.media.MediaRouter) routerObj).createRouteCategory(name, isGroupable);
+        return router.createRouteCategory(name, isGroupable);
     }
 
-    public static Object createUserRoute(Object routerObj, Object categoryObj) {
-        return ((android.media.MediaRouter) routerObj).createUserRoute(
-                (android.media.MediaRouter.RouteCategory) categoryObj);
+    public static MediaRouter.UserRouteInfo createUserRoute(android.media.MediaRouter router,
+            android.media.MediaRouter.RouteCategory category) {
+        return router.createUserRoute(category);
     }
 
-    public static void addUserRoute(Object routerObj, Object routeObj) {
-        ((android.media.MediaRouter) routerObj).addUserRoute(
-                (android.media.MediaRouter.UserRouteInfo) routeObj);
+    public static void addUserRoute(android.media.MediaRouter router,
+            android.media.MediaRouter.UserRouteInfo route) {
+        router.addUserRoute(route);
     }
 
-    public static void removeUserRoute(Object routerObj, Object routeObj) {
+    public static void removeUserRoute(android.media.MediaRouter router,
+            android.media.MediaRouter.UserRouteInfo route) {
         try {
-            ((android.media.MediaRouter) routerObj).removeUserRoute(
-                    (android.media.MediaRouter.UserRouteInfo) routeObj);
+            router.removeUserRoute(route);
         } catch (IllegalArgumentException e) {
             // Work around for https://issuetracker.google.com/issues/202931542.
             Log.w(TAG, "Failed to remove user route", e);
         }
     }
 
-    public static Object createCallback(Callback callback) {
-        return new CallbackProxy<Callback>(callback);
+    public static CallbackProxy<Callback> createCallback(Callback callback) {
+        return new CallbackProxy<>(callback);
     }
 
-    public static Object createVolumeCallback(VolumeCallback callback) {
-        return new VolumeCallbackProxy<VolumeCallback>(callback);
+    public static VolumeCallbackProxy<VolumeCallback> createVolumeCallback(
+            VolumeCallback callback) {
+        return new VolumeCallbackProxy<>(callback);
     }
 
     public static final class RouteInfo {
         @NonNull
-        public static CharSequence getName(@NonNull Object routeObj, @NonNull Context context) {
-            return ((android.media.MediaRouter.RouteInfo) routeObj).getName(context);
+        public static CharSequence getName(@NonNull android.media.MediaRouter.RouteInfo route,
+                @NonNull Context context) {
+            return route.getName(context);
         }
 
-        @NonNull
-        public static CharSequence getStatus(@NonNull Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo) routeObj).getStatus();
+        public static int getSupportedTypes(@NonNull android.media.MediaRouter.RouteInfo route) {
+            return route.getSupportedTypes();
         }
 
-        public static int getSupportedTypes(@NonNull Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo) routeObj).getSupportedTypes();
+        public static int getPlaybackType(@NonNull android.media.MediaRouter.RouteInfo route) {
+            return route.getPlaybackType();
+        }
+
+        public static int getPlaybackStream(@NonNull android.media.MediaRouter.RouteInfo route) {
+            return route.getPlaybackStream();
+        }
+
+        public static int getVolume(@NonNull android.media.MediaRouter.RouteInfo route) {
+            return route.getVolume();
+        }
+
+        public static int getVolumeMax(@NonNull android.media.MediaRouter.RouteInfo route) {
+            return route.getVolumeMax();
+        }
+
+        public static int getVolumeHandling(@NonNull android.media.MediaRouter.RouteInfo route) {
+            return route.getVolumeHandling();
         }
 
         @Nullable
-        public static Object getCategory(@NonNull Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo) routeObj).getCategory();
+        public static Object getTag(@NonNull android.media.MediaRouter.RouteInfo route) {
+            return route.getTag();
         }
 
-        @Nullable
-        public static Drawable getIconDrawable(@NonNull Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo) routeObj).getIconDrawable();
+        public static void setTag(@NonNull android.media.MediaRouter.RouteInfo route,
+                @Nullable Object tag) {
+            route.setTag(tag);
         }
 
-        public static int getPlaybackType(@NonNull Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo) routeObj).getPlaybackType();
+        public static void requestSetVolume(@NonNull android.media.MediaRouter.RouteInfo route,
+                int volume) {
+            route.requestSetVolume(volume);
         }
 
-        public static int getPlaybackStream(@NonNull Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo) routeObj).getPlaybackStream();
-        }
-
-        public static int getVolume(@NonNull Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo) routeObj).getVolume();
-        }
-
-        public static int getVolumeMax(@NonNull Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo) routeObj).getVolumeMax();
-        }
-
-        public static int getVolumeHandling(@NonNull Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo) routeObj).getVolumeHandling();
-        }
-
-        @Nullable
-        public static Object getTag(@NonNull Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo) routeObj).getTag();
-        }
-
-        public static void setTag(@NonNull Object routeObj, @Nullable Object tag) {
-            ((android.media.MediaRouter.RouteInfo) routeObj).setTag(tag);
-        }
-
-        public static void requestSetVolume(@NonNull Object routeObj, int volume) {
-            ((android.media.MediaRouter.RouteInfo) routeObj).requestSetVolume(volume);
-        }
-
-        public static void requestUpdateVolume(@NonNull Object routeObj, int direction) {
-            ((android.media.MediaRouter.RouteInfo) routeObj).requestUpdateVolume(direction);
-        }
-
-        @Nullable
-        public static Object getGroup(@NonNull Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo) routeObj).getGroup();
-        }
-
-        public static boolean isGroup(@NonNull Object routeObj) {
-            return routeObj instanceof android.media.MediaRouter.RouteGroup;
+        public static void requestUpdateVolume(
+                @NonNull android.media.MediaRouter.RouteInfo route, int direction) {
+            route.requestUpdateVolume(direction);
         }
 
         private RouteInfo() {
         }
     }
 
-    public static final class RouteGroup {
-        @SuppressWarnings({"rawtypes", "unchecked"})
-        @NonNull
-        public static List getGroupedRoutes(@NonNull Object groupObj) {
-            final android.media.MediaRouter.RouteGroup group =
-                    (android.media.MediaRouter.RouteGroup) groupObj;
-            final int count = group.getRouteCount();
-            List out = new ArrayList(count);
-            for (int i = 0; i < count; i++) {
-                out.add(group.getRouteAt(i));
-            }
-            return out;
-        }
-
-        private RouteGroup() {
-        }
-    }
-
     public static final class UserRouteInfo {
-        public static void setName(@NonNull Object routeObj, @NonNull CharSequence name) {
-            ((android.media.MediaRouter.UserRouteInfo) routeObj).setName(name);
+        public static void setName(@NonNull android.media.MediaRouter.UserRouteInfo route,
+                @NonNull CharSequence name) {
+            route.setName(name);
         }
 
-        public static void setStatus(@NonNull Object routeObj, @NonNull CharSequence status) {
-            ((android.media.MediaRouter.UserRouteInfo) routeObj).setStatus(status);
+        public static void setPlaybackType(
+                @NonNull android.media.MediaRouter.UserRouteInfo route, int type) {
+            route.setPlaybackType(type);
         }
 
-        public static void setIconDrawable(@NonNull Object routeObj, @Nullable Drawable icon) {
-            ((android.media.MediaRouter.UserRouteInfo) routeObj).setIconDrawable(icon);
+        public static void setPlaybackStream(
+                @NonNull android.media.MediaRouter.UserRouteInfo route, int stream) {
+            route.setPlaybackStream(stream);
         }
 
-        public static void setPlaybackType(@NonNull Object routeObj, int type) {
-            ((android.media.MediaRouter.UserRouteInfo) routeObj).setPlaybackType(type);
+        public static void setVolume(@NonNull android.media.MediaRouter.UserRouteInfo route,
+                int volume) {
+            route.setVolume(volume);
         }
 
-        public static void setPlaybackStream(@NonNull Object routeObj, int stream) {
-            ((android.media.MediaRouter.UserRouteInfo) routeObj).setPlaybackStream(stream);
+        public static void setVolumeMax(@NonNull android.media.MediaRouter.UserRouteInfo route,
+                int volumeMax) {
+            route.setVolumeMax(volumeMax);
         }
 
-        public static void setVolume(@NonNull Object routeObj, int volume) {
-            ((android.media.MediaRouter.UserRouteInfo) routeObj).setVolume(volume);
+        public static void setVolumeHandling(
+                @NonNull android.media.MediaRouter.UserRouteInfo route, int volumeHandling) {
+            route.setVolumeHandling(volumeHandling);
         }
 
-        public static void setVolumeMax(@NonNull Object routeObj, int volumeMax) {
-            ((android.media.MediaRouter.UserRouteInfo) routeObj).setVolumeMax(volumeMax);
+        public static void setVolumeCallback(@NonNull android.media.MediaRouter.UserRouteInfo route,
+                @NonNull android.media.MediaRouter.VolumeCallback volumeCallback) {
+            route.setVolumeCallback(volumeCallback);
         }
 
-        public static void setVolumeHandling(@NonNull Object routeObj, int volumeHandling) {
-            ((android.media.MediaRouter.UserRouteInfo) routeObj).setVolumeHandling(volumeHandling);
-        }
-
-        public static void setVolumeCallback(@NonNull Object routeObj,
-                @NonNull Object volumeCallbackObj) {
-            ((android.media.MediaRouter.UserRouteInfo) routeObj).setVolumeCallback(
-                    (android.media.MediaRouter.VolumeCallback) volumeCallbackObj);
-        }
-
-        public static void setRemoteControlClient(@NonNull Object routeObj,
-                @Nullable Object rccObj) {
-            ((android.media.MediaRouter.UserRouteInfo) routeObj).setRemoteControlClient(
-                    (android.media.RemoteControlClient) rccObj);
+        public static void setRemoteControlClient(
+                @NonNull android.media.MediaRouter.UserRouteInfo route,
+                @Nullable android.media.RemoteControlClient rcc) {
+            route.setRemoteControlClient(rcc);
         }
 
         private UserRouteInfo() {
         }
     }
 
-    public static final class RouteCategory {
-        @Nullable
-        public static CharSequence getName(@NonNull Object categoryObj, @NonNull Context context) {
-            return ((android.media.MediaRouter.RouteCategory) categoryObj).getName(context);
-        }
-
-        @SuppressWarnings({"rawtypes", "unchecked"})
-        @NonNull
-        public static List getRoutes(@NonNull Object categoryObj) {
-            ArrayList out = new ArrayList();
-            ((android.media.MediaRouter.RouteCategory) categoryObj).getRoutes(out);
-            return out;
-        }
-
-        public static int getSupportedTypes(@NonNull Object categoryObj) {
-            return ((android.media.MediaRouter.RouteCategory) categoryObj).getSupportedTypes();
-        }
-
-        public static boolean isGroupable(@NonNull Object categoryObj) {
-            return ((android.media.MediaRouter.RouteCategory) categoryObj).isGroupable();
-        }
-
-        private RouteCategory() {
-        }
-    }
-
     public interface Callback {
-        void onRouteSelected(int type, @NonNull Object routeObj);
+        void onRouteSelected(int type, @NonNull android.media.MediaRouter.RouteInfo route);
 
-        void onRouteUnselected(int type, @NonNull Object routeObj);
+        void onRouteUnselected(int type, @NonNull android.media.MediaRouter.RouteInfo route);
 
-        void onRouteAdded(@NonNull Object routeObj);
+        void onRouteAdded(@NonNull android.media.MediaRouter.RouteInfo route);
 
-        void onRouteRemoved(@NonNull Object routeObj);
+        void onRouteRemoved(@NonNull android.media.MediaRouter.RouteInfo route);
 
-        void onRouteChanged(@NonNull Object routeObj);
+        void onRouteChanged(@NonNull android.media.MediaRouter.RouteInfo route);
 
-        void onRouteGrouped(@NonNull Object routeObj, @NonNull Object groupObj, int index);
+        void onRouteGrouped(@NonNull android.media.MediaRouter.RouteInfo route,
+                @NonNull android.media.MediaRouter.RouteGroup group, int index);
 
-        void onRouteUngrouped(@NonNull Object routeObj, @NonNull Object groupObj);
+        void onRouteUngrouped(@NonNull android.media.MediaRouter.RouteInfo route,
+                @NonNull android.media.MediaRouter.RouteGroup group);
 
-        void onRouteVolumeChanged(@NonNull Object routeObj);
+        void onRouteVolumeChanged(@NonNull android.media.MediaRouter.RouteInfo route);
     }
 
     public interface VolumeCallback {
-        void onVolumeSetRequest(@NonNull Object routeObj, int volume);
+        void onVolumeSetRequest(@NonNull android.media.MediaRouter.RouteInfo route, int volume);
 
-        void onVolumeUpdateRequest(@NonNull Object routeObj, int direction);
+        void onVolumeUpdateRequest(@NonNull android.media.MediaRouter.RouteInfo route,
+                int direction);
     }
 
     /**
@@ -341,11 +262,8 @@
         // code: the reflection is used for a specific Android version and the real Android API
         // check is happening in the class' constructor and in SystemMediaRouteProvider#obtain
         @SuppressLint("BanUncheckedReflection")
-        public void selectRoute(@NonNull Object routerObj, int types, @NonNull Object routeObj) {
-            android.media.MediaRouter router = (android.media.MediaRouter) routerObj;
-            android.media.MediaRouter.RouteInfo route =
-                    (android.media.MediaRouter.RouteInfo) routeObj;
-
+        public void selectRoute(@NonNull android.media.MediaRouter router, int types,
+                @NonNull android.media.MediaRouter.RouteInfo route) {
             int routeTypes = route.getSupportedTypes();
             if ((routeTypes & ROUTE_TYPE_USER) == 0) {
                 // Handle non-user routes.
@@ -399,9 +317,7 @@
         // check is happening in the class' constructor and in SystemMediaRouteProvider#obtain
         @SuppressLint("BanUncheckedReflection")
         @NonNull
-        public Object getDefaultRoute(@NonNull Object routerObj) {
-            android.media.MediaRouter router = (android.media.MediaRouter) routerObj;
-
+        public Object getDefaultRoute(@NonNull android.media.MediaRouter router) {
             if (mGetSystemAudioRouteMethod != null) {
                 try {
                     return mGetSystemAudioRouteMethod.invoke(router);
@@ -484,8 +400,7 @@
         }
 
         @Override
-        public void onVolumeSetRequest(android.media.MediaRouter.RouteInfo route,
-                int volume) {
+        public void onVolumeSetRequest(android.media.MediaRouter.RouteInfo route, int volume) {
             mCallback.onVolumeSetRequest(route, volume);
         }
 
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouterJellybeanMr1.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouterJellybeanMr1.java
index 6dc606d..0d6642f 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouterJellybeanMr1.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouterJellybeanMr1.java
@@ -36,21 +36,22 @@
 final class MediaRouterJellybeanMr1 {
     private static final String TAG = "MediaRouterJellybeanMr1";
 
-    public static Object createCallback(Callback callback) {
-        return new CallbackProxy<Callback>(callback);
+    public static CallbackProxy<Callback> createCallback(Callback callback) {
+        return new CallbackProxy<>(callback);
     }
 
     public static final class RouteInfo {
-        public static boolean isEnabled(@NonNull Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo) routeObj).isEnabled();
+        public static boolean isEnabled(@NonNull android.media.MediaRouter.RouteInfo route) {
+            return route.isEnabled();
         }
 
         @Nullable
-        public static Display getPresentationDisplay(@NonNull Object routeObj) {
+        public static Display getPresentationDisplay(
+                @NonNull android.media.MediaRouter.RouteInfo route) {
             // android.media.MediaRouter.RouteInfo.getPresentationDisplay() was
             // added in API 17. However, some factory releases of JB MR1 missed it.
             try {
-                return ((android.media.MediaRouter.RouteInfo) routeObj).getPresentationDisplay();
+                return route.getPresentationDisplay();
             } catch (NoSuchMethodError ex) {
                 Log.w(TAG, "Cannot get presentation display for the route.", ex);
             }
@@ -62,7 +63,7 @@
     }
 
     public interface Callback extends MediaRouterJellybean.Callback {
-        void onRoutePresentationDisplayChanged(@NonNull Object routeObj);
+        void onRoutePresentationDisplayChanged(@NonNull android.media.MediaRouter.RouteInfo route);
     }
 
     /**
@@ -175,10 +176,7 @@
         // code: the reflection is used for a specific Android version and the real Android API
         // check is happening in the class' constructor and in SystemMediaRouteProvider#obtain
         @SuppressLint("BanUncheckedReflection")
-        public boolean isConnecting(@NonNull Object routeObj) {
-            android.media.MediaRouter.RouteInfo route =
-                    (android.media.MediaRouter.RouteInfo) routeObj;
-
+        public boolean isConnecting(@NonNull android.media.MediaRouter.RouteInfo route) {
             if (mGetStatusCodeMethod != null) {
                 try {
                     int statusCode = (Integer) mGetStatusCodeMethod.invoke(route);
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/RemoteControlClientCompat.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/RemoteControlClientCompat.java
index cfc8cfd..4033297 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/RemoteControlClientCompat.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/RemoteControlClientCompat.java
@@ -33,22 +33,23 @@
  */
 abstract class RemoteControlClientCompat {
     protected final Context mContext;
-    protected final Object mRcc;
+    protected final android.media.RemoteControlClient mRcc;
     protected VolumeCallback mVolumeCallback;
 
-    protected RemoteControlClientCompat(Context context, Object rcc) {
+    protected RemoteControlClientCompat(Context context, android.media.RemoteControlClient rcc) {
         mContext = context;
         mRcc = rcc;
     }
 
-    public static RemoteControlClientCompat obtain(Context context, Object rcc) {
+    public static RemoteControlClientCompat obtain(
+            Context context, android.media.RemoteControlClient rcc) {
         if (Build.VERSION.SDK_INT >= 16) {
             return new JellybeanImpl(context, rcc);
         }
         return new LegacyImpl(context, rcc);
     }
 
-    public Object getRemoteControlClient() {
+    public android.media.RemoteControlClient getRemoteControlClient() {
         return mRcc;
     }
 
@@ -101,7 +102,7 @@
          * Called when the volume for the route should be set to the given value.
          *
          * @param volume An integer indicating the new volume value that should be used,
-         * always between 0 and the value set by {@link PlaybackInfo#volumeMax}.
+         *               always between 0 and the value set by {@link PlaybackInfo#volumeMax}.
          */
         void onVolumeSetRequest(int volume);
     }
@@ -111,7 +112,7 @@
      * Does nothing.
      */
     static class LegacyImpl extends RemoteControlClientCompat {
-        public LegacyImpl(Context context, Object rcc) {
+        LegacyImpl(Context context, android.media.RemoteControlClient rcc) {
             super(context, rcc);
         }
     }
@@ -126,40 +127,40 @@
      */
     @RequiresApi(16)
     static class JellybeanImpl extends RemoteControlClientCompat {
-        private final Object mRouterObj;
-        private final Object mUserRouteCategoryObj;
-        private final Object mUserRouteObj;
+        private final android.media.MediaRouter mRouter;
+        private final android.media.MediaRouter.RouteCategory mUserRouteCategory;
+        private final android.media.MediaRouter.UserRouteInfo mUserRoute;
         private boolean mRegistered;
 
-        public JellybeanImpl(Context context, Object rcc) {
+        JellybeanImpl(Context context, android.media.RemoteControlClient rcc) {
             super(context, rcc);
 
-            mRouterObj = MediaRouterJellybean.getMediaRouter(context);
-            mUserRouteCategoryObj = MediaRouterJellybean.createRouteCategory(
-                    mRouterObj, "", false);
-            mUserRouteObj = MediaRouterJellybean.createUserRoute(
-                    mRouterObj, mUserRouteCategoryObj);
+            mRouter = MediaRouterJellybean.getMediaRouter(context);
+            mUserRouteCategory = MediaRouterJellybean.createRouteCategory(
+                    mRouter, "", false);
+            mUserRoute = MediaRouterJellybean.createUserRoute(
+                    mRouter, mUserRouteCategory);
         }
 
         @Override
         public void setPlaybackInfo(PlaybackInfo info) {
             MediaRouterJellybean.UserRouteInfo.setVolume(
-                    mUserRouteObj, info.volume);
+                    mUserRoute, info.volume);
             MediaRouterJellybean.UserRouteInfo.setVolumeMax(
-                    mUserRouteObj, info.volumeMax);
+                    mUserRoute, info.volumeMax);
             MediaRouterJellybean.UserRouteInfo.setVolumeHandling(
-                    mUserRouteObj, info.volumeHandling);
+                    mUserRoute, info.volumeHandling);
             MediaRouterJellybean.UserRouteInfo.setPlaybackStream(
-                    mUserRouteObj, info.playbackStream);
+                    mUserRoute, info.playbackStream);
             MediaRouterJellybean.UserRouteInfo.setPlaybackType(
-                    mUserRouteObj, info.playbackType);
+                    mUserRoute, info.playbackType);
 
             if (!mRegistered) {
                 mRegistered = true;
-                MediaRouterJellybean.UserRouteInfo.setVolumeCallback(mUserRouteObj,
+                MediaRouterJellybean.UserRouteInfo.setVolumeCallback(mUserRoute,
                         MediaRouterJellybean.createVolumeCallback(
                                 new VolumeCallbackWrapper(this)));
-                MediaRouterJellybean.UserRouteInfo.setRemoteControlClient(mUserRouteObj, mRcc);
+                MediaRouterJellybean.UserRouteInfo.setRemoteControlClient(mUserRoute, mRcc);
             }
         }
 
@@ -172,11 +173,12 @@
             private final WeakReference<JellybeanImpl> mImplWeak;
 
             public VolumeCallbackWrapper(JellybeanImpl impl) {
-                mImplWeak = new WeakReference<JellybeanImpl>(impl);
+                mImplWeak = new WeakReference<>(impl);
             }
 
             @Override
-            public void onVolumeUpdateRequest(@NonNull Object routeObj, int direction) {
+            public void onVolumeUpdateRequest(@NonNull android.media.MediaRouter.RouteInfo route,
+                    int direction) {
                 JellybeanImpl impl = mImplWeak.get();
                 if (impl != null && impl.mVolumeCallback != null) {
                     impl.mVolumeCallback.onVolumeUpdateRequest(direction);
@@ -184,7 +186,8 @@
             }
 
             @Override
-            public void onVolumeSetRequest(@NonNull Object routeObj, int volume) {
+            public void onVolumeSetRequest(@NonNull android.media.MediaRouter.RouteInfo route,
+                    int volume) {
                 JellybeanImpl impl = mImplWeak.get();
                 if (impl != null && impl.mVolumeCallback != null) {
                     impl.mVolumeCallback.onVolumeSetRequest(volume);
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/SystemMediaRouteProvider.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/SystemMediaRouteProvider.java
index 74c12ed..d8925fb 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/SystemMediaRouteProvider.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/SystemMediaRouteProvider.java
@@ -220,7 +220,7 @@
             IntentFilter f = new IntentFilter();
             f.addCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO);
 
-            LIVE_AUDIO_CONTROL_FILTERS = new ArrayList<IntentFilter>();
+            LIVE_AUDIO_CONTROL_FILTERS = new ArrayList<>();
             LIVE_AUDIO_CONTROL_FILTERS.add(f);
         }
 
@@ -230,16 +230,16 @@
             IntentFilter f = new IntentFilter();
             f.addCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO);
 
-            LIVE_VIDEO_CONTROL_FILTERS = new ArrayList<IntentFilter>();
+            LIVE_VIDEO_CONTROL_FILTERS = new ArrayList<>();
             LIVE_VIDEO_CONTROL_FILTERS.add(f);
         }
 
         private final SyncCallback mSyncCallback;
 
-        protected final Object mRouterObj;
-        protected final Object mCallbackObj;
-        protected final Object mVolumeCallbackObj;
-        protected final Object mUserRouteCategoryObj;
+        protected final android.media.MediaRouter mRouter;
+        protected final android.media.MediaRouter.Callback mCallback;
+        protected final android.media.MediaRouter.VolumeCallback mVolumeCallback;
+        protected final android.media.MediaRouter.RouteCategory mUserRouteCategory;
         protected int mRouteTypes;
         protected boolean mActiveScan;
         protected boolean mCallbackRegistered;
@@ -249,11 +249,11 @@
         // have published its own user routes to the framework media router and already
         // used the tag for its own purposes.
         protected final ArrayList<SystemRouteRecord> mSystemRouteRecords =
-                new ArrayList<SystemRouteRecord>();
+                new ArrayList<>();
 
         // Maintains an association from support library routes to framework routes.
         protected final ArrayList<UserRouteRecord> mUserRouteRecords =
-                new ArrayList<UserRouteRecord>();
+                new ArrayList<>();
 
         private MediaRouterJellybean.SelectRouteWorkaround mSelectRouteWorkaround;
         private MediaRouterJellybean.GetDefaultRouteWorkaround mGetDefaultRouteWorkaround;
@@ -261,13 +261,13 @@
         public JellybeanImpl(Context context, SyncCallback syncCallback) {
             super(context);
             mSyncCallback = syncCallback;
-            mRouterObj = MediaRouterJellybean.getMediaRouter(context);
-            mCallbackObj = createCallbackObj();
-            mVolumeCallbackObj = createVolumeCallbackObj();
+            mRouter = MediaRouterJellybean.getMediaRouter(context);
+            mCallback = createCallback();
+            mVolumeCallback = createVolumeCallback();
 
             Resources r = context.getResources();
-            mUserRouteCategoryObj = MediaRouterJellybean.createRouteCategory(
-                    mRouterObj, r.getString(R.string.mr_user_route_category_name), false);
+            mUserRouteCategory = MediaRouterJellybean.createRouteCategory(
+                    mRouter, r.getString(R.string.mr_user_route_category_name), false);
 
             updateSystemRoutes();
         }
@@ -277,7 +277,7 @@
             int index = findSystemRouteRecordByDescriptorId(routeId);
             if (index >= 0) {
                 SystemRouteRecord record = mSystemRouteRecords.get(index);
-                return new SystemRouteController(record.mRouteObj);
+                return new SystemRouteController(record.mRoute);
             }
             return null;
         }
@@ -311,8 +311,8 @@
         }
 
         @Override
-        public void onRouteAdded(@NonNull Object routeObj) {
-            if (addSystemRouteNoPublish(routeObj)) {
+        public void onRouteAdded(@NonNull android.media.MediaRouter.RouteInfo route) {
+            if (addSystemRouteNoPublish(route)) {
                 publishRoutes();
             }
         }
@@ -320,19 +320,19 @@
         private void updateSystemRoutes() {
             updateCallback();
             boolean changed = false;
-            for (Object routeObj : MediaRouterJellybean.getRoutes(mRouterObj)) {
-                changed |= addSystemRouteNoPublish(routeObj);
+            for (android.media.MediaRouter.RouteInfo route : MediaRouterJellybean.getRoutes(
+                    mRouter)) {
+                changed |= addSystemRouteNoPublish(route);
             }
             if (changed) {
                 publishRoutes();
             }
         }
 
-        private boolean addSystemRouteNoPublish(Object routeObj) {
-            if (getUserRouteRecord(routeObj) == null
-                    && findSystemRouteRecord(routeObj) < 0) {
-                String id = assignRouteId(routeObj);
-                SystemRouteRecord record = new SystemRouteRecord(routeObj, id);
+        private boolean addSystemRouteNoPublish(android.media.MediaRouter.RouteInfo route) {
+            if (getUserRouteRecord(route) == null && findSystemRouteRecord(route) < 0) {
+                String id = assignRouteId(route);
+                SystemRouteRecord record = new SystemRouteRecord(route, id);
                 updateSystemRouteDescriptor(record);
                 mSystemRouteRecords.add(record);
                 return true;
@@ -340,13 +340,13 @@
             return false;
         }
 
-        private String assignRouteId(Object routeObj) {
+        private String assignRouteId(android.media.MediaRouter.RouteInfo route) {
             // TODO: The framework media router should supply a unique route id that
             // we can use here.  For now we use a hash of the route name and take care
             // to dedupe it.
-            boolean isDefault = (getDefaultRoute() == routeObj);
+            boolean isDefault = (getDefaultRoute() == route);
             String id = isDefault ? DEFAULT_ROUTE_ID :
-                    String.format(Locale.US, "ROUTE_%08x", getRouteName(routeObj).hashCode());
+                    String.format(Locale.US, "ROUTE_%08x", getRouteName(route).hashCode());
             if (findSystemRouteRecordByDescriptorId(id) < 0) {
                 return id;
             }
@@ -359,9 +359,9 @@
         }
 
         @Override
-        public void onRouteRemoved(@NonNull Object routeObj) {
-            if (getUserRouteRecord(routeObj) == null) {
-                int index = findSystemRouteRecord(routeObj);
+        public void onRouteRemoved(@NonNull android.media.MediaRouter.RouteInfo route) {
+            if (getUserRouteRecord(route) == null) {
+                int index = findSystemRouteRecord(route);
                 if (index >= 0) {
                     mSystemRouteRecords.remove(index);
                     publishRoutes();
@@ -370,9 +370,9 @@
         }
 
         @Override
-        public void onRouteChanged(@NonNull Object routeObj) {
-            if (getUserRouteRecord(routeObj) == null) {
-                int index = findSystemRouteRecord(routeObj);
+        public void onRouteChanged(@NonNull android.media.MediaRouter.RouteInfo route) {
+            if (getUserRouteRecord(route) == null) {
+                int index = findSystemRouteRecord(route);
                 if (index >= 0) {
                     SystemRouteRecord record = mSystemRouteRecords.get(index);
                     updateSystemRouteDescriptor(record);
@@ -382,12 +382,12 @@
         }
 
         @Override
-        public void onRouteVolumeChanged(@NonNull Object routeObj) {
-            if (getUserRouteRecord(routeObj) == null) {
-                int index = findSystemRouteRecord(routeObj);
+        public void onRouteVolumeChanged(@NonNull android.media.MediaRouter.RouteInfo route) {
+            if (getUserRouteRecord(route) == null) {
+                int index = findSystemRouteRecord(route);
                 if (index >= 0) {
                     SystemRouteRecord record = mSystemRouteRecords.get(index);
-                    int newVolume = MediaRouterJellybean.RouteInfo.getVolume(routeObj);
+                    int newVolume = MediaRouterJellybean.RouteInfo.getVolume(route);
                     if (newVolume != record.mRouteDescriptor.getVolume()) {
                         record.mRouteDescriptor =
                                 new MediaRouteDescriptor.Builder(record.mRouteDescriptor)
@@ -400,21 +400,22 @@
         }
 
         @Override
-        public void onRouteSelected(int type, @NonNull Object routeObj) {
-            if (routeObj != MediaRouterJellybean.getSelectedRoute(mRouterObj,
+        public void onRouteSelected(int type,
+                @NonNull android.media.MediaRouter.RouteInfo route) {
+            if (route != MediaRouterJellybean.getSelectedRoute(mRouter,
                     MediaRouterJellybean.ALL_ROUTE_TYPES)) {
                 // The currently selected route has already changed so this callback
                 // is stale.  Drop it to prevent getting into sync loops.
                 return;
             }
 
-            UserRouteRecord userRouteRecord = getUserRouteRecord(routeObj);
+            UserRouteRecord userRouteRecord = getUserRouteRecord(route);
             if (userRouteRecord != null) {
                 userRouteRecord.mRoute.select();
             } else {
                 // Select the route if it already exists in the compat media router.
                 // If not, we will select it instead when the route is added.
-                int index = findSystemRouteRecord(routeObj);
+                int index = findSystemRouteRecord(route);
                 if (index >= 0) {
                     SystemRouteRecord record = mSystemRouteRecords.get(index);
                     mSyncCallback.onSystemRouteSelectedByDescriptorId(record.mRouteDescriptorId);
@@ -423,32 +424,37 @@
         }
 
         @Override
-        public void onRouteUnselected(int type, @NonNull Object routeObj) {
+        public void onRouteUnselected(int type,
+                @NonNull android.media.MediaRouter.RouteInfo route) {
             // Nothing to do when a route is unselected.
             // We only need to handle when a route is selected.
         }
 
         @Override
-        public void onRouteGrouped(@NonNull Object routeObj, @NonNull Object groupObj, int index) {
+        public void onRouteGrouped(@NonNull android.media.MediaRouter.RouteInfo route,
+                @NonNull android.media.MediaRouter.RouteGroup group, int index) {
             // Route grouping is deprecated and no longer supported.
         }
 
         @Override
-        public void onRouteUngrouped(@NonNull Object routeObj, @NonNull Object groupObj) {
+        public void onRouteUngrouped(@NonNull android.media.MediaRouter.RouteInfo route,
+                @NonNull android.media.MediaRouter.RouteGroup group) {
             // Route grouping is deprecated and no longer supported.
         }
 
         @Override
-        public void onVolumeSetRequest(@NonNull Object routeObj, int volume) {
-            UserRouteRecord record = getUserRouteRecord(routeObj);
+        public void onVolumeSetRequest(@NonNull android.media.MediaRouter.RouteInfo route,
+                int volume) {
+            UserRouteRecord record = getUserRouteRecord(route);
             if (record != null) {
                 record.mRoute.requestSetVolume(volume);
             }
         }
 
         @Override
-        public void onVolumeUpdateRequest(@NonNull Object routeObj, int direction) {
-            UserRouteRecord record = getUserRouteRecord(routeObj);
+        public void onVolumeUpdateRequest(@NonNull android.media.MediaRouter.RouteInfo route,
+                int direction) {
+            UserRouteRecord record = getUserRouteRecord(route);
             if (record != null) {
                 record.mRoute.requestUpdateVolume(direction);
             }
@@ -457,20 +463,21 @@
         @Override
         public void onSyncRouteAdded(MediaRouter.RouteInfo route) {
             if (route.getProviderInstance() != this) {
-                Object routeObj = MediaRouterJellybean.createUserRoute(
-                        mRouterObj, mUserRouteCategoryObj);
-                UserRouteRecord record = new UserRouteRecord(route, routeObj);
-                MediaRouterJellybean.RouteInfo.setTag(routeObj, record);
-                MediaRouterJellybean.UserRouteInfo.setVolumeCallback(routeObj, mVolumeCallbackObj);
+                android.media.MediaRouter.UserRouteInfo userRoute =
+                        MediaRouterJellybean.createUserRoute(mRouter, mUserRouteCategory);
+                UserRouteRecord record = new UserRouteRecord(route, userRoute);
+                MediaRouterJellybean.RouteInfo.setTag(userRoute, record);
+                MediaRouterJellybean.UserRouteInfo.setVolumeCallback(userRoute, mVolumeCallback);
                 updateUserRouteProperties(record);
                 mUserRouteRecords.add(record);
-                MediaRouterJellybean.addUserRoute(mRouterObj, routeObj);
+                MediaRouterJellybean.addUserRoute(mRouter, userRoute);
             } else {
                 // If the newly added route is the counterpart of the currently selected
                 // route in the framework media router then ensure it is selected in
                 // the compat media router.
-                Object routeObj = MediaRouterJellybean.getSelectedRoute(
-                        mRouterObj, MediaRouterJellybean.ALL_ROUTE_TYPES);
+                android.media.MediaRouter.RouteInfo routeObj =
+                        MediaRouterJellybean.getSelectedRoute(
+                                mRouter, MediaRouterJellybean.ALL_ROUTE_TYPES);
                 int index = findSystemRouteRecord(routeObj);
                 if (index >= 0) {
                     SystemRouteRecord record = mSystemRouteRecords.get(index);
@@ -487,9 +494,9 @@
                 int index = findUserRouteRecord(route);
                 if (index >= 0) {
                     UserRouteRecord record = mUserRouteRecords.remove(index);
-                    MediaRouterJellybean.RouteInfo.setTag(record.mRouteObj, null);
-                    MediaRouterJellybean.UserRouteInfo.setVolumeCallback(record.mRouteObj, null);
-                    MediaRouterJellybean.removeUserRoute(mRouterObj, record.mRouteObj);
+                    MediaRouterJellybean.RouteInfo.setTag(record.mUserRoute, null);
+                    MediaRouterJellybean.UserRouteInfo.setVolumeCallback(record.mUserRoute, null);
+                    MediaRouterJellybean.removeUserRoute(mRouter, record.mUserRoute);
                 }
             }
         }
@@ -517,13 +524,13 @@
                 int index = findUserRouteRecord(route);
                 if (index >= 0) {
                     UserRouteRecord record = mUserRouteRecords.get(index);
-                    selectRoute(record.mRouteObj);
+                    selectRoute(record.mUserRoute);
                 }
             } else {
                 int index = findSystemRouteRecordByDescriptorId(route.getDescriptorId());
                 if (index >= 0) {
                     SystemRouteRecord record = mSystemRouteRecords.get(index);
-                    selectRoute(record.mRouteObj);
+                    selectRoute(record.mRoute);
                 }
             }
         }
@@ -539,10 +546,10 @@
             setDescriptor(builder.build());
         }
 
-        protected int findSystemRouteRecord(Object routeObj) {
+        protected int findSystemRouteRecord(android.media.MediaRouter.RouteInfo route) {
             final int count = mSystemRouteRecords.size();
             for (int i = 0; i < count; i++) {
-                if (mSystemRouteRecords.get(i).mRouteObj == routeObj) {
+                if (mSystemRouteRecords.get(i).mRoute == route) {
                     return i;
                 }
             }
@@ -569,8 +576,8 @@
             return -1;
         }
 
-        protected UserRouteRecord getUserRouteRecord(Object routeObj) {
-            Object tag = MediaRouterJellybean.RouteInfo.getTag(routeObj);
+        protected UserRouteRecord getUserRouteRecord(android.media.MediaRouter.RouteInfo route) {
+            Object tag = MediaRouterJellybean.RouteInfo.getTag(route);
             return tag instanceof UserRouteRecord ? (UserRouteRecord) tag : null;
         }
 
@@ -578,24 +585,24 @@
             // We must always recreate the route descriptor when making any changes
             // because they are intended to be immutable once published.
             MediaRouteDescriptor.Builder builder = new MediaRouteDescriptor.Builder(
-                    record.mRouteDescriptorId, getRouteName(record.mRouteObj));
+                    record.mRouteDescriptorId, getRouteName(record.mRoute));
             onBuildSystemRouteDescriptor(record, builder);
             record.mRouteDescriptor = builder.build();
         }
 
-        protected String getRouteName(Object routeObj) {
+        protected String getRouteName(android.media.MediaRouter.RouteInfo route) {
             // Routes should not have null names but it may happen for badly configured
             // user routes.  We tolerate this by using an empty name string here but
             // such unnamed routes will be discarded by the media router upstream
             // (with a log message so we can track down the problem).
-            CharSequence name = MediaRouterJellybean.RouteInfo.getName(routeObj, getContext());
+            CharSequence name = MediaRouterJellybean.RouteInfo.getName(route, getContext());
             return name != null ? name.toString() : "";
         }
 
         protected void onBuildSystemRouteDescriptor(SystemRouteRecord record,
                 MediaRouteDescriptor.Builder builder) {
             int supportedTypes = MediaRouterJellybean.RouteInfo.getSupportedTypes(
-                    record.mRouteObj);
+                    record.mRoute);
             if ((supportedTypes & MediaRouterJellybean.ROUTE_TYPE_LIVE_AUDIO) != 0) {
                 builder.addControlFilters(LIVE_AUDIO_CONTROL_FILTERS);
             }
@@ -604,65 +611,65 @@
             }
 
             builder.setPlaybackType(
-                    MediaRouterJellybean.RouteInfo.getPlaybackType(record.mRouteObj));
+                    MediaRouterJellybean.RouteInfo.getPlaybackType(record.mRoute));
             builder.setPlaybackStream(
-                    MediaRouterJellybean.RouteInfo.getPlaybackStream(record.mRouteObj));
+                    MediaRouterJellybean.RouteInfo.getPlaybackStream(record.mRoute));
             builder.setVolume(
-                    MediaRouterJellybean.RouteInfo.getVolume(record.mRouteObj));
+                    MediaRouterJellybean.RouteInfo.getVolume(record.mRoute));
             builder.setVolumeMax(
-                    MediaRouterJellybean.RouteInfo.getVolumeMax(record.mRouteObj));
+                    MediaRouterJellybean.RouteInfo.getVolumeMax(record.mRoute));
             builder.setVolumeHandling(
-                    MediaRouterJellybean.RouteInfo.getVolumeHandling(record.mRouteObj));
+                    MediaRouterJellybean.RouteInfo.getVolumeHandling(record.mRoute));
         }
 
         protected void updateUserRouteProperties(UserRouteRecord record) {
             MediaRouterJellybean.UserRouteInfo.setName(
-                    record.mRouteObj, record.mRoute.getName());
+                    record.mUserRoute, record.mRoute.getName());
             MediaRouterJellybean.UserRouteInfo.setPlaybackType(
-                    record.mRouteObj, record.mRoute.getPlaybackType());
+                    record.mUserRoute, record.mRoute.getPlaybackType());
             MediaRouterJellybean.UserRouteInfo.setPlaybackStream(
-                    record.mRouteObj, record.mRoute.getPlaybackStream());
+                    record.mUserRoute, record.mRoute.getPlaybackStream());
             MediaRouterJellybean.UserRouteInfo.setVolume(
-                    record.mRouteObj, record.mRoute.getVolume());
+                    record.mUserRoute, record.mRoute.getVolume());
             MediaRouterJellybean.UserRouteInfo.setVolumeMax(
-                    record.mRouteObj, record.mRoute.getVolumeMax());
+                    record.mUserRoute, record.mRoute.getVolumeMax());
             MediaRouterJellybean.UserRouteInfo.setVolumeHandling(
-                    record.mRouteObj, record.mRoute.getVolumeHandling());
+                    record.mUserRoute, record.mRoute.getVolumeHandling());
         }
 
         protected void updateCallback() {
             if (mCallbackRegistered) {
                 mCallbackRegistered = false;
-                MediaRouterJellybean.removeCallback(mRouterObj, mCallbackObj);
+                MediaRouterJellybean.removeCallback(mRouter, mCallback);
             }
 
             if (mRouteTypes != 0) {
                 mCallbackRegistered = true;
-                MediaRouterJellybean.addCallback(mRouterObj, mRouteTypes, mCallbackObj);
+                MediaRouterJellybean.addCallback(mRouter, mRouteTypes, mCallback);
             }
         }
 
-        protected Object createCallbackObj() {
+        protected android.media.MediaRouter.Callback createCallback() {
             return MediaRouterJellybean.createCallback(this);
         }
 
-        protected Object createVolumeCallbackObj() {
+        protected android.media.MediaRouter.VolumeCallback createVolumeCallback() {
             return MediaRouterJellybean.createVolumeCallback(this);
         }
 
-        protected void selectRoute(Object routeObj) {
+        protected void selectRoute(android.media.MediaRouter.RouteInfo route) {
             if (mSelectRouteWorkaround == null) {
                 mSelectRouteWorkaround = new MediaRouterJellybean.SelectRouteWorkaround();
             }
-            mSelectRouteWorkaround.selectRoute(mRouterObj,
-                    MediaRouterJellybean.ALL_ROUTE_TYPES, routeObj);
+            mSelectRouteWorkaround.selectRoute(mRouter,
+                    MediaRouterJellybean.ALL_ROUTE_TYPES, route);
         }
 
         protected Object getDefaultRoute() {
             if (mGetDefaultRouteWorkaround == null) {
                 mGetDefaultRouteWorkaround = new MediaRouterJellybean.GetDefaultRouteWorkaround();
             }
-            return mGetDefaultRouteWorkaround.getDefaultRoute(mRouterObj);
+            return mGetDefaultRouteWorkaround.getDefaultRoute(mRouter);
         }
 
         /**
@@ -670,12 +677,12 @@
          * and published by this route provider to the support library media router.
          */
         protected static final class SystemRouteRecord {
-            public final Object mRouteObj;
+            public final android.media.MediaRouter.RouteInfo mRoute;
             public final String mRouteDescriptorId;
             public MediaRouteDescriptor mRouteDescriptor; // assigned immediately after creation
 
-            public SystemRouteRecord(Object routeObj, String id) {
-                mRouteObj = routeObj;
+            public SystemRouteRecord(android.media.MediaRouter.RouteInfo route, String id) {
+                mRoute = route;
                 mRouteDescriptorId = id;
             }
         }
@@ -686,29 +693,30 @@
          */
         protected static final class UserRouteRecord {
             public final MediaRouter.RouteInfo mRoute;
-            public final Object mRouteObj;
+            public final android.media.MediaRouter.UserRouteInfo mUserRoute;
 
-            public UserRouteRecord(MediaRouter.RouteInfo route, Object routeObj) {
+            public UserRouteRecord(MediaRouter.RouteInfo route,
+                    android.media.MediaRouter.UserRouteInfo userRoute) {
                 mRoute = route;
-                mRouteObj = routeObj;
+                mUserRoute = userRoute;
             }
         }
 
         protected static final class SystemRouteController extends RouteController {
-            private final Object mRouteObj;
+            private final android.media.MediaRouter.RouteInfo mRoute;
 
-            public SystemRouteController(Object routeObj) {
-                mRouteObj = routeObj;
+            public SystemRouteController(android.media.MediaRouter.RouteInfo route) {
+                mRoute = route;
             }
 
             @Override
             public void onSetVolume(int volume) {
-                MediaRouterJellybean.RouteInfo.requestSetVolume(mRouteObj, volume);
+                MediaRouterJellybean.RouteInfo.requestSetVolume(mRoute, volume);
             }
 
             @Override
             public void onUpdateVolume(int delta) {
-                MediaRouterJellybean.RouteInfo.requestUpdateVolume(mRouteObj, delta);
+                MediaRouterJellybean.RouteInfo.requestUpdateVolume(mRoute, delta);
             }
         }
     }
@@ -727,12 +735,13 @@
         }
 
         @Override
-        public void onRoutePresentationDisplayChanged(@NonNull Object routeObj) {
-            int index = findSystemRouteRecord(routeObj);
+        public void onRoutePresentationDisplayChanged(
+                @NonNull android.media.MediaRouter.RouteInfo route) {
+            int index = findSystemRouteRecord(route);
             if (index >= 0) {
                 SystemRouteRecord record = mSystemRouteRecords.get(index);
                 Display newPresentationDisplay =
-                        MediaRouterJellybeanMr1.RouteInfo.getPresentationDisplay(routeObj);
+                        MediaRouterJellybeanMr1.RouteInfo.getPresentationDisplay(route);
                 int newPresentationDisplayId = (newPresentationDisplay != null
                         ? newPresentationDisplay.getDisplayId() : -1);
                 if (newPresentationDisplayId
@@ -751,7 +760,7 @@
                 MediaRouteDescriptor.Builder builder) {
             super.onBuildSystemRouteDescriptor(record, builder);
 
-            if (!MediaRouterJellybeanMr1.RouteInfo.isEnabled(record.mRouteObj)) {
+            if (!MediaRouterJellybeanMr1.RouteInfo.isEnabled(record.mRoute)) {
                 builder.setEnabled(false);
             }
 
@@ -760,7 +769,7 @@
             }
 
             Display presentationDisplay =
-                    MediaRouterJellybeanMr1.RouteInfo.getPresentationDisplay(record.mRouteObj);
+                    MediaRouterJellybeanMr1.RouteInfo.getPresentationDisplay(record.mRoute);
             if (presentationDisplay != null) {
                 builder.setPresentationDisplayId(presentationDisplay.getDisplayId());
             }
@@ -778,7 +787,7 @@
         }
 
         @Override
-        protected Object createCallbackObj() {
+        protected android.media.MediaRouter.Callback createCallback() {
             return MediaRouterJellybeanMr1.createCallback(this);
         }
 
@@ -786,7 +795,7 @@
             if (mIsConnectingWorkaround == null) {
                 mIsConnectingWorkaround = new MediaRouterJellybeanMr1.IsConnectingWorkaround();
             }
-            return mIsConnectingWorkaround.isConnecting(record.mRouteObj);
+            return mIsConnectingWorkaround.isConnecting(record.mRoute);
         }
     }
 
@@ -805,8 +814,7 @@
                 MediaRouteDescriptor.Builder builder) {
             super.onBuildSystemRouteDescriptor(record, builder);
 
-            CharSequence description =
-                    ((android.media.MediaRouter.RouteInfo) record.mRouteObj).getDescription();
+            CharSequence description = record.mRoute.getDescription();
             if (description != null) {
                 builder.setDescription(description.toString());
             }
@@ -814,43 +822,40 @@
 
         @DoNotInline
         @Override
-        protected void selectRoute(Object routeObj) {
-            MediaRouterJellybean.selectRoute(mRouterObj,
-                    MediaRouterJellybean.ALL_ROUTE_TYPES, routeObj);
+        protected void selectRoute(android.media.MediaRouter.RouteInfo route) {
+            MediaRouterJellybean.selectRoute(mRouter, MediaRouterJellybean.ALL_ROUTE_TYPES, route);
         }
 
         @DoNotInline
         @Override
-        protected Object getDefaultRoute() {
-            return ((android.media.MediaRouter) mRouterObj).getDefaultRoute();
+        protected android.media.MediaRouter.RouteInfo getDefaultRoute() {
+            return mRouter.getDefaultRoute();
         }
 
         @DoNotInline
         @Override
         protected void updateUserRouteProperties(UserRouteRecord record) {
             super.updateUserRouteProperties(record);
-            ((android.media.MediaRouter.UserRouteInfo) record.mRouteObj).setDescription(
-                    record.mRoute.getDescription());
+            record.mUserRoute.setDescription(record.mRoute.getDescription());
         }
 
         @DoNotInline
         @Override
         protected void updateCallback() {
             if (mCallbackRegistered) {
-                MediaRouterJellybean.removeCallback(mRouterObj, mCallbackObj);
+                MediaRouterJellybean.removeCallback(mRouter, mCallback);
             }
 
             mCallbackRegistered = true;
             int flags = MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS
                     | (mActiveScan ? MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN : 0);
-            ((android.media.MediaRouter) mRouterObj).addCallback(mRouteTypes,
-                    (android.media.MediaRouter.Callback) mCallbackObj, flags);
+            mRouter.addCallback(mRouteTypes, mCallback, flags);
         }
 
         @DoNotInline
         @Override
         protected boolean isConnecting(SystemRouteRecord record) {
-            return ((android.media.MediaRouter.RouteInfo) record.mRouteObj).isConnecting();
+            return record.mRoute.isConnecting();
         }
     }
 
@@ -869,8 +874,7 @@
         protected void onBuildSystemRouteDescriptor(SystemRouteRecord record,
                 MediaRouteDescriptor.Builder builder) {
             super.onBuildSystemRouteDescriptor(record, builder);
-            builder.setDeviceType(
-                    ((android.media.MediaRouter.RouteInfo) record.mRouteObj).getDeviceType());
+            builder.setDeviceType(record.mRoute.getDeviceType());
         }
     }
 }
diff --git a/navigation/navigation-compose/build.gradle b/navigation/navigation-compose/build.gradle
index ebc4b00..0ecac1e 100644
--- a/navigation/navigation-compose/build.gradle
+++ b/navigation/navigation-compose/build.gradle
@@ -27,12 +27,12 @@
 dependencies {
 
     implementation(libs.kotlinStdlib)
-    implementation("androidx.compose.foundation:foundation-layout:1.5.0-beta03")
+    implementation("androidx.compose.foundation:foundation-layout:1.5.0-rc01")
     api("androidx.activity:activity-compose:1.7.0")
-    api("androidx.compose.animation:animation:1.5.0-beta03")
-    api("androidx.compose.runtime:runtime:1.5.0-beta03")
-    api("androidx.compose.runtime:runtime-saveable:1.5.0-beta03")
-    api("androidx.compose.ui:ui:1.5.0-beta03")
+    api("androidx.compose.animation:animation:1.5.0-rc01")
+    api("androidx.compose.runtime:runtime:1.5.0-rc01")
+    api("androidx.compose.runtime:runtime-saveable:1.5.0-rc01")
+    api("androidx.compose.ui:ui:1.5.0-rc01")
     api("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1")
     api(projectOrArtifact(":navigation:navigation-runtime-ktx"))
 
diff --git a/navigation/navigation-safe-args-generator/build.gradle b/navigation/navigation-safe-args-generator/build.gradle
index c63d57d..4a019c6 100644
--- a/navigation/navigation-safe-args-generator/build.gradle
+++ b/navigation/navigation-safe-args-generator/build.gradle
@@ -24,6 +24,14 @@
 
 androidx.configureAarAsJarForConfiguration("testImplementation")
 
+// android.jar and xmlpull has the same classes, but android.jar has stubs instead of real
+// implementation, so we remove org.xmlpull.* from android.jar used for tests
+tasks.register("strippedAndroidJar", Jar).configure {
+    it.from(zipTree(SdkHelperKt.getSdkDependency(project).getFiles().first()))
+    it.exclude("org/xmlpull/**")
+    it.archiveFileName.set("stripped-android.jar")
+}
+
 dependencies {
     implementation(libs.xpp3)
     implementation(libs.xmlpull)
@@ -37,24 +45,16 @@
     testImplementation(projectOrArtifact(":room:room-compiler-processing-testing"), {
         exclude group: "androidx.room", module: "room-compiler-processing"
     })
-    testImplementation(SdkHelperKt.getSdkDependency(project))
     testImplementationAarAsJar(project(":navigation:navigation-common"))
     testImplementationAarAsJar(project(":lifecycle:lifecycle-viewmodel-savedstate"))
+    testImplementation(tasks.named("strippedAndroidJar").map { it.outputs.files })
 }
 
-tasks.findByName("test").doFirst {
-    // android.jar and xmlpull has the same classes, but android.jar has stubs instead of real
-    // implementation, so we move android.jar to end of classpath
-    def classpath = it.classpath.getFiles()
-    def androidJar = classpath.find { it.name == "android.jar" }
-    it.classpath = files(classpath.minus(androidJar).plus(androidJar))
-}
 tasks.withType(Test).configureEach {
     // https://github.com/google/compile-testing/issues/222
     it.jvmArgs "--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED"
 }
 
-
 androidx {
     name = "Navigation TypeSafe Arguments Generator"
     type = LibraryType.OTHER_CODE_PROCESSOR
diff --git a/navigation/navigation-safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/kotlin/KotlinNavWriter.kt b/navigation/navigation-safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/kotlin/KotlinNavWriter.kt
index e2a61cc..b1021c3 100644
--- a/navigation/navigation-safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/kotlin/KotlinNavWriter.kt
+++ b/navigation/navigation-safe-args-generator/src/main/kotlin/androidx/navigation/safe/args/generator/kotlin/KotlinNavWriter.kt
@@ -128,11 +128,16 @@
         val className = ClassName("", action.id.javaIdentifier.toCamelCase())
 
         val actionIdPropSpec =
-            PropertySpec.builder("actionId", Int::class, KModifier.OVERRIDE)
+            PropertySpec.builder("actionId", Int::class, KModifier.PUBLIC, KModifier.OVERRIDE)
                 .initializer("%L", action.id.accessor()).build()
 
         val argumentsPropSpec =
-            PropertySpec.builder("arguments", BUNDLE_CLASSNAME, KModifier.OVERRIDE)
+            PropertySpec.builder(
+                "arguments",
+                BUNDLE_CLASSNAME,
+                KModifier.PUBLIC,
+                KModifier.OVERRIDE
+            )
                 .getter(
                     FunSpec.getterBuilder().apply {
                         if (action.args.any { it.type is ObjectType }) {
diff --git a/navigation/navigation-safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/SafeArgsPlugin.kt b/navigation/navigation-safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/SafeArgsPlugin.kt
index 26f46d6..998b9f8 100644
--- a/navigation/navigation-safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/SafeArgsPlugin.kt
+++ b/navigation/navigation-safe-args-gradle-plugin/src/main/kotlin/androidx/navigation/safeargs/gradle/SafeArgsPlugin.kt
@@ -122,8 +122,12 @@
                 // the class
                 task.rFilePackage.set(namespaces[variant.name] ?: task.applicationId)
                 task.navigationFiles.setFrom(navigationFiles(variant, project))
-                task.outputDir.set(File(project.buildDir, "$GENERATED_PATH/${variant.dirName}"))
-                task.incrementalFolder.set(File(project.buildDir, "$INCREMENTAL_PATH/${task.name}"))
+                task.outputDir.set(
+                    project.layout.buildDirectory.dir("$GENERATED_PATH/${variant.dirName}")
+                )
+                task.incrementalFolder.set(
+                    project.layout.buildDirectory.dir("$INCREMENTAL_PATH/${task.name}")
+                )
                 task.useAndroidX.set(
                     (project.findProperty("android.useAndroidX") == "true").also {
                         if (!it) {
diff --git a/paging/paging-common/api/current.ignore b/paging/paging-common/api/current.ignore
new file mode 100644
index 0000000..43720c7
--- /dev/null
+++ b/paging/paging-common/api/current.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+RemovedJvmDefaultWithCompatibility: androidx.paging.Logger:
+    Cannot remove @kotlin.jvm.JvmDefaultWithCompatibility annotation from class androidx.paging.Logger: Incompatible change
diff --git a/paging/paging-common/api/restricted_current.ignore b/paging/paging-common/api/restricted_current.ignore
new file mode 100644
index 0000000..43720c7
--- /dev/null
+++ b/paging/paging-common/api/restricted_current.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+RemovedJvmDefaultWithCompatibility: androidx.paging.Logger:
+    Cannot remove @kotlin.jvm.JvmDefaultWithCompatibility annotation from class androidx.paging.Logger: Incompatible change
diff --git a/paging/paging-common/build.gradle b/paging/paging-common/build.gradle
index f68f058..3368c0b 100644
--- a/paging/paging-common/build.gradle
+++ b/paging/paging-common/build.gradle
@@ -73,8 +73,7 @@
         }
 
         commonJvmAndroidTest {
-            kotlin.srcDir("src/commonJvmAndroidTest/kotlin")
-
+            dependsOn(commonTest)
             dependencies {
                 implementation(libs.junit)
                 implementation(libs.mockitoCore4)
@@ -90,6 +89,10 @@
 
         androidAndroidTest {
             dependsOn(commonJvmAndroidTest)
+            dependencies {
+                implementation(libs.testRunner)
+                implementation(libs.mockitoAndroid)
+            }
         }
 
         if (enableNative) {
diff --git a/paging/paging-common/lint-baseline.xml b/paging/paging-common/lint-baseline.xml
deleted file mode 100644
index 034ebe2..0000000
--- a/paging/paging-common/lint-baseline.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.1.0-beta02" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-beta02)" variant="all" version="8.1.0-beta02">
-
-    <issue
-        id="BanThreadSleep"
-        message="Uses Thread.sleep()"
-        errorLine1="                Thread.sleep(1000)"
-        errorLine2="                       ~~~~~">
-        <location
-            file="src/test/kotlin/androidx/paging/PagedListTest.kt"/>
-    </issue>
-
-    <issue
-        id="BanThreadSleep"
-        message="Uses Thread.sleep()"
-        errorLine1="                    Thread.sleep(100)"
-        errorLine2="                           ~~~~~">
-        <location
-            file="src/test/kotlin/androidx/paging/SingleRunnerTest.kt"/>
-    </issue>
-
-    <issue
-        id="SupportAnnotationUsage"
-        message="Did you mean `@get:VisibleForTesting`? Without `get:` this annotates the constructor parameter itself instead of the associated getter."
-        errorLine1="        @VisibleForTesting"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/kotlin/androidx/paging/PageFetcher.kt"/>
-    </issue>
-
-    <issue
-        id="SupportAnnotationUsage"
-        message="Did you mean `@get:RestrictTo`? Without `get:` this annotates the constructor parameter itself instead of the associated getter."
-        errorLine1="    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/kotlin/androidx/paging/PagedList.kt"/>
-    </issue>
-
-</issues>
diff --git a/paging/paging-compose/build.gradle b/paging/paging-compose/build.gradle
index b08c7ea..98319a3 100644
--- a/paging/paging-compose/build.gradle
+++ b/paging/paging-compose/build.gradle
@@ -21,24 +21,56 @@
     id("AndroidXPlugin")
     id("com.android.library")
     id("AndroidXComposePlugin")
-    id("org.jetbrains.kotlin.android")
-    id("kotlin-android")
+}
+
+androidXMultiplatform {
+    android()
+
+    sourceSets {
+        commonMain {
+            dependencies {
+                implementation(libs.kotlinStdlib)
+                api("androidx.compose.foundation:foundation:1.5.0-beta03")
+                api(project(":paging:paging-common"))
+                api("androidx.compose.runtime:runtime:1.5.0-beta03")
+            }
+        }
+
+        jvmMain {
+            dependsOn(commonMain)
+        }
+
+        androidMain {
+            dependsOn(jvmMain)
+        }
+
+        commonTest {
+            dependencies {
+                implementation projectOrArtifact(":compose:ui:ui-tooling")
+                implementation(project(":compose:test-utils"))
+                implementation(projectOrArtifact(":internal-testutils-paging"))
+            }
+        }
+
+        jvmTest {
+            dependsOn(commonTest)
+            dependencies {
+                implementation(projectOrArtifact(":compose:ui:ui-test-junit4"))
+            }
+        }
+
+        androidAndroidTest {
+            dependsOn(jvmTest)
+            dependencies {
+                implementation(libs.testRunner)
+                implementation(libs.junit)
+                implementation(libs.truth)
+            }
+        }
+    }
 }
 
 dependencies {
-    implementation(libs.kotlinStdlib)
-    api("androidx.compose.foundation:foundation:1.2.1")
-    api(project(":paging:paging-common"))
-    api("androidx.compose.runtime:runtime:1.2.1")
-
-    androidTestImplementation(projectOrArtifact(":compose:ui:ui-test-junit4"))
-    androidTestImplementation projectOrArtifact(":compose:ui:ui-tooling")
-    androidTestImplementation(project(":compose:test-utils"))
-    androidTestImplementation(projectOrArtifact(":internal-testutils-paging"))
-    androidTestImplementation(libs.testRunner)
-    androidTestImplementation(libs.junit)
-    androidTestImplementation(libs.truth)
-
     samples(projectOrArtifact(":paging:paging-compose:paging-compose-samples"))
 }
 
diff --git a/paging/paging-compose/lint-baseline.xml b/paging/paging-compose/lint-baseline.xml
deleted file mode 100644
index 2612a94..0000000
--- a/paging/paging-compose/lint-baseline.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.1.0-beta02" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-beta02)" variant="all" version="8.1.0-beta02">
-
-    <issue
-        id="PrimitiveInLambda"
-        message="Use a functional interface instead of lambda syntax for lambdas with primitive values in return type Function1&lt;Integer, Object> of &apos;itemKey&apos;."
-        errorLine1="): (index: Int) -> Any {"
-        errorLine2="   ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/paging/compose/LazyFoundationExtensions.kt"/>
-    </issue>
-
-    <issue
-        id="PrimitiveInLambda"
-        message="Use a functional interface instead of lambda syntax for lambdas with primitive values in return type Function1&lt;Integer, Object> of &apos;itemContentType&apos;."
-        errorLine1="): (index: Int) -> Any? {"
-        errorLine2="   ~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/paging/compose/LazyFoundationExtensions.kt"/>
-    </issue>
-
-</issues>
diff --git a/paging/paging-compose/src/androidTest/java/androidx/paging/compose/LazyPagingItemsPreviewTest.kt b/paging/paging-compose/src/androidAndroidTest/kotlin/androidx/paging/compose/LazyPagingItemsPreviewTest.kt
similarity index 100%
rename from paging/paging-compose/src/androidTest/java/androidx/paging/compose/LazyPagingItemsPreviewTest.kt
rename to paging/paging-compose/src/androidAndroidTest/kotlin/androidx/paging/compose/LazyPagingItemsPreviewTest.kt
diff --git a/paging/paging-compose/src/androidTest/java/androidx/paging/compose/LazyPagingItemsTest.kt b/paging/paging-compose/src/androidAndroidTest/kotlin/androidx/paging/compose/LazyPagingItemsTest.kt
similarity index 100%
rename from paging/paging-compose/src/androidTest/java/androidx/paging/compose/LazyPagingItemsTest.kt
rename to paging/paging-compose/src/androidAndroidTest/kotlin/androidx/paging/compose/LazyPagingItemsTest.kt
diff --git a/paging/paging-compose/src/main/java/androidx/paging/compose/PagingPlaceholders.kt b/paging/paging-compose/src/androidMain/kotlin/androidx/paging/compose/PagingPlaceholderKey.android.kt
similarity index 88%
rename from paging/paging-compose/src/main/java/androidx/paging/compose/PagingPlaceholders.kt
rename to paging/paging-compose/src/androidMain/kotlin/androidx/paging/compose/PagingPlaceholderKey.android.kt
index 417f2e3..8b018cd 100644
--- a/paging/paging-compose/src/main/java/androidx/paging/compose/PagingPlaceholders.kt
+++ b/paging/paging-compose/src/androidMain/kotlin/androidx/paging/compose/PagingPlaceholderKey.android.kt
@@ -20,8 +20,10 @@
 import android.os.Parcel
 import android.os.Parcelable
 
+internal actual fun getPagingPlaceholderKey(index: Int): Any = PagingPlaceholderKey(index)
+
 @SuppressLint("BanParcelableUsage")
-internal data class PagingPlaceholderKey(private val index: Int) : Parcelable {
+private data class PagingPlaceholderKey(private val index: Int) : Parcelable {
     override fun writeToParcel(parcel: Parcel, flags: Int) {
         parcel.writeInt(index)
     }
@@ -42,5 +44,3 @@
             }
     }
 }
-
-internal object PagingPlaceholderContentType
diff --git a/paging/paging-compose/src/main/java/androidx/paging/compose/LazyFoundationExtensions.kt b/paging/paging-compose/src/commonMain/kotlin/androidx/paging/compose/LazyFoundationExtensions.kt
similarity index 94%
rename from paging/paging-compose/src/main/java/androidx/paging/compose/LazyFoundationExtensions.kt
rename to paging/paging-compose/src/commonMain/kotlin/androidx/paging/compose/LazyFoundationExtensions.kt
index bb24cb0..61b1c9e 100644
--- a/paging/paging-compose/src/main/java/androidx/paging/compose/LazyFoundationExtensions.kt
+++ b/paging/paging-compose/src/commonMain/kotlin/androidx/paging/compose/LazyFoundationExtensions.kt
@@ -38,15 +38,16 @@
  * based on the key, which means if you add/remove items before the current visible item the
  * item with the given key will be kept as the first visible one.
  */
+@Suppress("PrimitiveInLambda")
 public fun <T : Any> LazyPagingItems<T>.itemKey(
     key: ((item: @JvmSuppressWildcards T) -> Any)? = null
 ): (index: Int) -> Any {
     return { index ->
         if (key == null) {
-            PagingPlaceholderKey(index)
+            getPagingPlaceholderKey(index)
         } else {
             val item = peek(index)
-            if (item == null) PagingPlaceholderKey(index) else key(item)
+            if (item == null) getPagingPlaceholderKey(index) else key(item)
         }
     }
 }
@@ -68,6 +69,7 @@
  * the same type could be reused more efficiently. Note that null is a valid type and items of
  * such type will be considered compatible.
  */
+@Suppress("PrimitiveInLambda")
 public fun <T : Any> LazyPagingItems<T>.itemContentType(
     contentType: ((item: @JvmSuppressWildcards T) -> Any?)? = null
 ): (index: Int) -> Any? {
diff --git a/paging/paging-compose/src/main/java/androidx/paging/compose/LazyPagingItems.kt b/paging/paging-compose/src/commonMain/kotlin/androidx/paging/compose/LazyPagingItems.kt
similarity index 100%
rename from paging/paging-compose/src/main/java/androidx/paging/compose/LazyPagingItems.kt
rename to paging/paging-compose/src/commonMain/kotlin/androidx/paging/compose/LazyPagingItems.kt
diff --git a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence1.java b/paging/paging-compose/src/commonMain/kotlin/androidx/paging/compose/PagingPlaceholders.kt
similarity index 74%
copy from lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence1.java
copy to paging/paging-compose/src/commonMain/kotlin/androidx/paging/compose/PagingPlaceholders.kt
index ae9c85f..823d611 100644
--- a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence1.java
+++ b/paging/paging-compose/src/commonMain/kotlin/androidx/paging/compose/PagingPlaceholders.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,10 +14,8 @@
  * limitations under the License.
  */
 
-package androidx.lifecycle.observers;
+package androidx.paging.compose
 
-public class DerivedSequence1 extends Base {
+internal expect fun getPagingPlaceholderKey(index: Int): Any
 
-    public void something() {
-    }
-}
+internal object PagingPlaceholderContentType
diff --git a/paging/samples/lint-baseline.xml b/paging/samples/lint-baseline.xml
deleted file mode 100644
index 0a6dfe4..0000000
--- a/paging/samples/lint-baseline.xml
+++ /dev/null
@@ -1,85 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.1.0-beta05" type="baseline" client="gradle" dependencies="true" name="AGP (8.1.0-beta05)" variant="all" version="8.1.0-beta05">
-
-    <issue
-        id="ObsoleteSampledAnnotation"
-        message="androidx.paging.samples.cachedInSample is annotated with @Sampled, but is not linked to from a @sample tag."
-        errorLine1="fun cachedInSample() {"
-        errorLine2="    ~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/paging/samples/CachedInSample.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSampledAnnotation"
-        message="androidx.paging.samples.insertSeparatorsSample is annotated with @Sampled, but is not linked to from a @sample tag."
-        errorLine1="fun insertSeparatorsSample() {"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/paging/samples/InsertSeparatorsSample.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSampledAnnotation"
-        message="androidx.paging.samples.insertSeparatorsUiModelSample is annotated with @Sampled, but is not linked to from a @sample tag."
-        errorLine1="fun insertSeparatorsUiModelSample() {"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/paging/samples/InsertSeparatorsUiModelSample.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSampledAnnotation"
-        message="androidx.paging.samples.itemKeyedPagingSourceSample is annotated with @Sampled, but is not linked to from a @sample tag."
-        errorLine1="fun itemKeyedPagingSourceSample() {"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/paging/samples/PagingSourceSample.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSampledAnnotation"
-        message="androidx.paging.samples.pageKeyedPagingSourceSample is annotated with @Sampled, but is not linked to from a @sample tag."
-        errorLine1="fun pageKeyedPagingSourceSample() {"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/paging/samples/PagingSourceSample.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSampledAnnotation"
-        message="androidx.paging.samples.pageKeyedPage is annotated with @Sampled, but is not linked to from a @sample tag."
-        errorLine1="fun pageKeyedPage() {"
-        errorLine2="    ~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/paging/samples/PagingSourceSample.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSampledAnnotation"
-        message="androidx.paging.samples.pageIndexedPage is annotated with @Sampled, but is not linked to from a @sample tag."
-        errorLine1="fun pageIndexedPage() {"
-        errorLine2="    ~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/paging/samples/PagingSourceSample.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSampledAnnotation"
-        message="androidx.paging.samples.remoteMediatorItemKeyedSample is annotated with @Sampled, but is not linked to from a @sample tag."
-        errorLine1="fun remoteMediatorItemKeyedSample() {"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/paging/samples/RemoteMediatorSample.kt"/>
-    </issue>
-
-    <issue
-        id="ObsoleteSampledAnnotation"
-        message="androidx.paging.samples.remoteMediatorPageKeyedSample is annotated with @Sampled, but is not linked to from a @sample tag."
-        errorLine1="fun remoteMediatorPageKeyedSample() {"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/paging/samples/RemoteMediatorSample.kt"/>
-    </issue>
-
-</issues>
diff --git a/playground-common/androidx-shared.properties b/playground-common/androidx-shared.properties
index aea8e01..e4ec300 100644
--- a/playground-common/androidx-shared.properties
+++ b/playground-common/androidx-shared.properties
@@ -32,8 +32,7 @@
 # org.gradle.vfs.watch=true
 org.gradle.dependency.verification.console=verbose
 org.gradle.unsafe.configuration-cache=true
-org.gradle.unsafe.configuration-cache-problems=warn
-org.gradle.unsafe.configuration-cache.max-problems=4000
+org.gradle.unsafe.configuration-cache-problems=fail
 
 android.lint.printStackTrace=true
 android.uniquePackageNames=false
diff --git a/playground-common/playground.properties b/playground-common/playground.properties
index db11414..866f0cb9 100644
--- a/playground-common/playground.properties
+++ b/playground-common/playground.properties
@@ -25,6 +25,6 @@
 kotlin.code.style=official
 # Disable docs
 androidx.enableDocumentation=false
-androidx.playground.snapshotBuildId=10339410
-androidx.playground.metalavaBuildId=10401832
-androidx.studio.type=playground
\ No newline at end of file
+androidx.playground.snapshotBuildId=10533165
+androidx.playground.metalavaBuildId=10499661
+androidx.studio.type=playground
diff --git a/privacysandbox/ads/ads-adservices-java/api/current.txt b/privacysandbox/ads/ads-adservices-java/api/current.txt
index 26eea8b..18004f9 100644
--- a/privacysandbox/ads/ads-adservices-java/api/current.txt
+++ b/privacysandbox/ads/ads-adservices-java/api/current.txt
@@ -64,6 +64,7 @@
     method public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Integer> getMeasurementApiStatusAsync();
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerSourceAsync(android.net.Uri attributionSource, android.view.InputEvent? inputEvent);
+    method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerSourceAsync(androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest request);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerTriggerAsync(android.net.Uri trigger);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebSourceAsync(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebTriggerAsync(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request);
diff --git a/privacysandbox/ads/ads-adservices-java/api/restricted_current.txt b/privacysandbox/ads/ads-adservices-java/api/restricted_current.txt
index 26eea8b..18004f9 100644
--- a/privacysandbox/ads/ads-adservices-java/api/restricted_current.txt
+++ b/privacysandbox/ads/ads-adservices-java/api/restricted_current.txt
@@ -64,6 +64,7 @@
     method public static final androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures? from(android.content.Context context);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Integer> getMeasurementApiStatusAsync();
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerSourceAsync(android.net.Uri attributionSource, android.view.InputEvent? inputEvent);
+    method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerSourceAsync(androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest request);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerTriggerAsync(android.net.Uri trigger);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebSourceAsync(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract com.google.common.util.concurrent.ListenableFuture<kotlin.Unit> registerWebTriggerAsync(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request);
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/measurement/MeasurementManagerTest.java b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/measurement/MeasurementManagerTest.java
index 6a657ad4..7bc4ea9 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/measurement/MeasurementManagerTest.java
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/measurement/MeasurementManagerTest.java
@@ -26,6 +26,7 @@
 import androidx.privacysandbox.ads.adservices.java.endtoend.TestUtil;
 import androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures;
 import androidx.privacysandbox.ads.adservices.measurement.DeletionRequest;
+import androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest;
 import androidx.privacysandbox.ads.adservices.measurement.WebSourceParams;
 import androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest;
 import androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams;
@@ -107,6 +108,19 @@
     }
 
     @Test
+    public void testRegisterAppSources_NoServerSetup_NoErrors() throws Exception {
+        // Skip the test if SDK extension 5 is not present.
+        Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 5);
+
+        SourceRegistrationRequest request =
+                new SourceRegistrationRequest.Builder(
+                        Collections.singletonList(SOURCE_REGISTRATION_URI))
+                        .build();
+        assertThat(mMeasurementManager.registerSourceAsync(request).get())
+                .isNotNull();
+    }
+
+    @Test
     public void testRegisterTrigger_NoServerSetup_NoErrors() throws Exception {
         // Skip the test if SDK extension 5 is not present.
         Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 5);
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFuturesTest.kt b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFuturesTest.kt
index 6e1752b..0106e76 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFuturesTest.kt
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFuturesTest.kt
@@ -24,8 +24,10 @@
 import android.os.ext.SdkExtensions
 import android.view.InputEvent
 import androidx.annotation.RequiresExtension
+import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
 import androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures.Companion.from
 import androidx.privacysandbox.ads.adservices.measurement.DeletionRequest
+import androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest
 import androidx.privacysandbox.ads.adservices.measurement.WebSourceParams
 import androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest
 import androidx.privacysandbox.ads.adservices.measurement.WebTriggerParams
@@ -36,13 +38,23 @@
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
 import java.time.Instant
+import java.util.concurrent.ExecutionException
+import java.util.concurrent.Executor
 import kotlin.test.assertNotEquals
+import kotlin.test.assertTrue
+import kotlin.test.fail
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withContext
 import org.junit.Assume
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.atMost
 import org.mockito.Mockito.doAnswer
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.spy
@@ -278,6 +290,112 @@
         assertThat(result.get() == state)
     }
 
+    @ExperimentalFeatures.RegisterSourceOptIn
+    @Test
+    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
+    fun testRegisterSourceAsync_allSuccess() {
+        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+
+        Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
+        val inputEvent = mock(InputEvent::class.java)
+        val measurementManager = mockMeasurementManager(mContext)
+        val managerCompat = from(mContext)
+        val successCallback = { args: InvocationOnMock ->
+            assertNotEquals(Looper.myLooper(), Looper.getMainLooper())
+            val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(3)
+            receiver.onResult(Object())
+            null
+        }
+        doAnswer(successCallback).`when`(measurementManager)
+            .registerSource(any(), any(), any(), any())
+
+        // Actually invoke the compat code.
+        val request = SourceRegistrationRequest.Builder(listOf(uri1, uri2))
+            .setInputEvent(inputEvent)
+            .build()
+        managerCompat!!.registerSourceAsync(request).get()
+
+        // Verify that the compat code was invoked correctly.
+        verify(measurementManager).registerSource(
+            eq(uri1),
+            eq(inputEvent),
+            any(Executor::class.java),
+            any())
+        verify(measurementManager).registerSource(
+            eq(uri2),
+            eq(inputEvent),
+            any(Executor::class.java),
+            any())
+    }
+
+    @ExperimentalFeatures.RegisterSourceOptIn
+    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
+    @Test
+    fun testRegisterSource_15thOf20Fails_atLeast15thExecutes() {
+        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+
+        Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
+        val measurementManager = mockMeasurementManager(mContext)
+        val mockInputEvent = mock(InputEvent::class.java)
+        val managerCompat = from(mContext)
+        val successCallback = { args: InvocationOnMock ->
+            val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(3)
+            receiver.onResult(Object())
+            null
+        }
+
+        val errorMessage = "some error occurred"
+        val errorCallback = { args: InvocationOnMock ->
+            val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(3)
+            receiver.onError(IllegalArgumentException(errorMessage))
+            null
+        }
+
+        val uris = (1..20).map { i ->
+            val uri = Uri.parse("www.uri$i.com")
+            if (i == 15) {
+                doAnswer(errorCallback).`when`(measurementManager)
+                    .registerSource(eq(uri), any(), any(), any())
+            } else {
+                doAnswer(successCallback).`when`(measurementManager)
+                    .registerSource(eq(uri), any(), any(), any())
+            }
+            uri
+        }.toList()
+
+        val request = SourceRegistrationRequest(uris, mockInputEvent)
+
+        // Actually invoke the compat code.
+        runBlocking {
+            try {
+                withContext(Dispatchers.Main) {
+                    managerCompat!!.registerSourceAsync(request).get()
+                }
+                fail("Expected failure.")
+            } catch (e: ExecutionException) {
+                assertTrue(e.cause!! is IllegalArgumentException)
+                assertThat(e.cause!!.message).isEqualTo(errorMessage)
+            }
+        }
+
+        // Verify that the compat code was invoked correctly.
+        // registerSource gets called 1-20 times. We cannot predict the exact number because
+        // uri15 would crash asynchronously. Other uris may succeed and those threads on default
+        // dispatcher won't crash.
+        verify(measurementManager, atLeastOnce()).registerSource(
+            any(),
+            eq(mockInputEvent),
+            any(),
+            any()
+        )
+        verify(measurementManager, atMost(20)).registerSource(
+            any(),
+            eq(mockInputEvent),
+            any(),
+            any()
+        )
+    }
+
     @SdkSuppress(minSdkVersion = 30)
     @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
     companion object {
diff --git a/privacysandbox/ads/ads-adservices-java/src/main/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFutures.kt b/privacysandbox/ads/ads-adservices-java/src/main/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFutures.kt
index b04eb21..e6c5487 100644
--- a/privacysandbox/ads/ads-adservices-java/src/main/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFutures.kt
+++ b/privacysandbox/ads/ads-adservices-java/src/main/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFutures.kt
@@ -22,10 +22,12 @@
 import android.view.InputEvent
 import androidx.annotation.DoNotInline
 import androidx.annotation.RequiresPermission
+import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
 import androidx.privacysandbox.ads.adservices.java.internal.asListenableFuture
 import androidx.privacysandbox.ads.adservices.measurement.DeletionRequest
 import androidx.privacysandbox.ads.adservices.measurement.MeasurementManager
 import androidx.privacysandbox.ads.adservices.measurement.MeasurementManager.Companion.obtain
+import androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest
 import androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest
 import androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest
 import com.google.common.util.concurrent.ListenableFuture
@@ -75,6 +77,19 @@
     abstract fun registerTriggerAsync(trigger: Uri): ListenableFuture<Unit>
 
     /**
+     * Register attribution sources(click or view). This API will not process any redirects, all
+     * registration URLs should be supplied with the request.
+     *
+     * @param request source registration request
+     */
+    @ExperimentalFeatures.RegisterSourceOptIn
+    @SuppressWarnings("MissingNullability")
+    @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION)
+    abstract fun registerSourceAsync(
+        request: SourceRegistrationRequest
+    ): ListenableFuture<Unit>
+
+    /**
      * Register an attribution source(click or view) from web context. This API will not process any
      * redirects, all registration URLs should be supplied with the request. At least one of
      * appDestination or webDestination parameters are required to be provided.
@@ -135,6 +150,17 @@
         }
 
         @DoNotInline
+        @ExperimentalFeatures.RegisterSourceOptIn
+        @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION)
+        override fun registerSourceAsync(
+            request: SourceRegistrationRequest
+        ): ListenableFuture<Unit> {
+            return CoroutineScope(Dispatchers.Default).async {
+                mMeasurementManager.registerSource(request)
+            }.asListenableFuture()
+        }
+
+        @DoNotInline
         @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION)
         override fun registerTriggerAsync(trigger: Uri): ListenableFuture<Unit> {
             return CoroutineScope(Dispatchers.Default).async {
diff --git a/privacysandbox/ads/ads-adservices/api/current.txt b/privacysandbox/ads/ads-adservices/api/current.txt
index 839dec6..6a4905b 100644
--- a/privacysandbox/ads/ads-adservices/api/current.txt
+++ b/privacysandbox/ads/ads-adservices/api/current.txt
@@ -126,6 +126,12 @@
     property public final String identifier;
   }
 
+  public sealed interface ExperimentalFeatures {
+  }
+
+  @SuppressCompatibility @kotlin.RequiresOptIn(message="This API is experimental.", level=kotlin.RequiresOptIn.Level.WARNING) public static @interface ExperimentalFeatures.RegisterSourceOptIn {
+  }
+
 }
 
 package androidx.privacysandbox.ads.adservices.customaudience {
@@ -249,6 +255,7 @@
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? getMeasurementApiStatus(kotlin.coroutines.Continuation<? super java.lang.Integer>);
     method public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerSource(android.net.Uri attributionSource, android.view.InputEvent? inputEvent, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public abstract suspend Object? registerSource(androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerTrigger(android.net.Uri trigger, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebSource(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebTrigger(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
@@ -261,6 +268,20 @@
     method public androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
   }
 
+  @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public final class SourceRegistrationRequest {
+    ctor public SourceRegistrationRequest(java.util.List<? extends android.net.Uri> registrationUris, optional android.view.InputEvent? inputEvent);
+    method public android.view.InputEvent? getInputEvent();
+    method public java.util.List<android.net.Uri> getRegistrationUris();
+    property public final android.view.InputEvent? inputEvent;
+    property public final java.util.List<android.net.Uri> registrationUris;
+  }
+
+  public static final class SourceRegistrationRequest.Builder {
+    ctor public SourceRegistrationRequest.Builder(java.util.List<? extends android.net.Uri> registrationUris);
+    method public androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest build();
+    method public androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest.Builder setInputEvent(android.view.InputEvent inputEvent);
+  }
+
   @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebSourceParams {
     ctor public WebSourceParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
     method public boolean getDebugKeyAllowed();
diff --git a/privacysandbox/ads/ads-adservices/api/restricted_current.txt b/privacysandbox/ads/ads-adservices/api/restricted_current.txt
index 839dec6..6a4905b 100644
--- a/privacysandbox/ads/ads-adservices/api/restricted_current.txt
+++ b/privacysandbox/ads/ads-adservices/api/restricted_current.txt
@@ -126,6 +126,12 @@
     property public final String identifier;
   }
 
+  public sealed interface ExperimentalFeatures {
+  }
+
+  @SuppressCompatibility @kotlin.RequiresOptIn(message="This API is experimental.", level=kotlin.RequiresOptIn.Level.WARNING) public static @interface ExperimentalFeatures.RegisterSourceOptIn {
+  }
+
 }
 
 package androidx.privacysandbox.ads.adservices.customaudience {
@@ -249,6 +255,7 @@
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? getMeasurementApiStatus(kotlin.coroutines.Continuation<? super java.lang.Integer>);
     method public static final androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerSource(android.net.Uri attributionSource, android.view.InputEvent? inputEvent, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method @SuppressCompatibility @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public abstract suspend Object? registerSource(androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerTrigger(android.net.Uri trigger, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebSource(androidx.privacysandbox.ads.adservices.measurement.WebSourceRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @RequiresPermission(android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION) public abstract suspend Object? registerWebTrigger(androidx.privacysandbox.ads.adservices.measurement.WebTriggerRegistrationRequest request, kotlin.coroutines.Continuation<? super kotlin.Unit>);
@@ -261,6 +268,20 @@
     method public androidx.privacysandbox.ads.adservices.measurement.MeasurementManager? obtain(android.content.Context context);
   }
 
+  @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.RegisterSourceOptIn public final class SourceRegistrationRequest {
+    ctor public SourceRegistrationRequest(java.util.List<? extends android.net.Uri> registrationUris, optional android.view.InputEvent? inputEvent);
+    method public android.view.InputEvent? getInputEvent();
+    method public java.util.List<android.net.Uri> getRegistrationUris();
+    property public final android.view.InputEvent? inputEvent;
+    property public final java.util.List<android.net.Uri> registrationUris;
+  }
+
+  public static final class SourceRegistrationRequest.Builder {
+    ctor public SourceRegistrationRequest.Builder(java.util.List<? extends android.net.Uri> registrationUris);
+    method public androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest build();
+    method public androidx.privacysandbox.ads.adservices.measurement.SourceRegistrationRequest.Builder setInputEvent(android.view.InputEvent inputEvent);
+  }
+
   @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class WebSourceParams {
     ctor public WebSourceParams(android.net.Uri registrationUri, boolean debugKeyAllowed);
     method public boolean getDebugKeyAllowed();
diff --git a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerTest.kt b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerTest.kt
index 191e2d8..1e0a826 100644
--- a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerTest.kt
+++ b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerTest.kt
@@ -23,13 +23,16 @@
 import android.os.ext.SdkExtensions
 import android.view.InputEvent
 import androidx.annotation.RequiresExtension
+import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
 import androidx.privacysandbox.ads.adservices.measurement.MeasurementManager.Companion.obtain
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SdkSuppress
 import androidx.test.filters.SmallTest
+import androidx.testutils.fail
 import com.google.common.truth.Truth.assertThat
 import java.time.Instant
+import kotlin.IllegalArgumentException
 import kotlinx.coroutines.runBlocking
 import org.junit.Assume
 import org.junit.Before
@@ -37,9 +40,12 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mockito.doAnswer
 import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
 import org.mockito.Mockito.spy
+import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.invocation.InvocationOnMock
@@ -211,6 +217,105 @@
         assertThat(!actualRequest.sourceParams[0].isDebugKeyAllowed)
     }
 
+    @ExperimentalFeatures.RegisterSourceOptIn
+    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
+    @Test
+    fun testRegisterSource_allSuccess() {
+        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+
+        Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
+        val measurementManager = mockMeasurementManager(mContext)
+        val mockInputEvent = mock(InputEvent::class.java)
+        val managerCompat = obtain(mContext)
+        val successCallback = { args: InvocationOnMock ->
+            val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(3)
+            receiver.onResult(Object())
+            null
+        }
+        doAnswer(successCallback).`when`(measurementManager)
+            .registerSource(any(), any(), any(), any())
+
+        val request = SourceRegistrationRequest(listOf(uri1, uri2), mockInputEvent)
+
+        // Actually invoke the compat code.
+        runBlocking {
+            managerCompat!!.registerSource(request)
+        }
+
+        // Verify that the compat code was invoked correctly.
+        verify(measurementManager, times(2)).registerSource(
+            any(),
+            eq(mockInputEvent),
+            any(),
+            any()
+        )
+    }
+
+    @ExperimentalFeatures.RegisterSourceOptIn
+    @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
+    @Test
+    fun testRegisterSource_15thOf20Fails_remaining5DoNotExecute() {
+        val sdkExtVersion = SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES)
+
+        Assume.assumeTrue("minSdkVersion = API 33 ext 5", sdkExtVersion >= 5)
+        val measurementManager = mockMeasurementManager(mContext)
+        val mockInputEvent = mock(InputEvent::class.java)
+        val managerCompat = obtain(mContext)
+        val successCallback = { args: InvocationOnMock ->
+            val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(3)
+            receiver.onResult(Object())
+            null
+        }
+
+        val errorMessage = "some error occurred"
+        val errorCallback = { args: InvocationOnMock ->
+            val receiver = args.getArgument<OutcomeReceiver<Any, Exception>>(3)
+            receiver.onError(IllegalArgumentException(errorMessage))
+            null
+        }
+        val uris = (0..20).map { i ->
+            val uri = Uri.parse("www.uri$i.com")
+            if (i == 15) {
+                doAnswer(errorCallback).`when`(measurementManager)
+                    .registerSource(eq(uri), any(), any(), any())
+            } else {
+                doAnswer(successCallback).`when`(measurementManager)
+                    .registerSource(eq(uri), any(), any(), any())
+            }
+            uri
+        }.toList()
+
+        val request = SourceRegistrationRequest(uris, mockInputEvent)
+
+        // Actually invoke the compat code.
+        runBlocking {
+            try {
+                managerCompat!!.registerSource(request)
+                fail("Expected failure.")
+            } catch (e: IllegalArgumentException) {
+                assertThat(e.message).isEqualTo(errorMessage)
+            }
+        }
+
+        // Verify that the compat code was invoked correctly.
+        (0..15).forEach { i ->
+            verify(measurementManager).registerSource(
+                eq(Uri.parse("www.uri$i.com")),
+                eq(mockInputEvent),
+                any(),
+                any()
+            )
+        }
+        (16..20).forEach { i ->
+            verify(measurementManager, never()).registerSource(
+                eq(Uri.parse("www.uri$i.com")),
+                eq(mockInputEvent),
+                any(),
+                any()
+            )
+        }
+    }
+
     @Test
     @RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 5)
     fun testRegisterWebTrigger() {
diff --git a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/SourceRegistrationRequestTest.kt b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/SourceRegistrationRequestTest.kt
new file mode 100644
index 0000000..cff885a
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/SourceRegistrationRequestTest.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.ads.adservices.measurement
+
+import android.net.Uri
+import android.view.InputEvent
+import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+
[email protected]
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = 33)
+class SourceRegistrationRequestTest {
+    @Test
+    fun testToString() {
+        val result = "AppSourcesRegistrationRequest { RegistrationUris=" +
+            "[[www.abc.com, www.xyz.com]], InputEvent=null }"
+
+        val uri1 = Uri.parse("www.abc.com")
+        val uri2 = Uri.parse("www.xyz.com")
+        val params = listOf(uri1, uri2)
+        val request = SourceRegistrationRequest.Builder(params).build()
+        Truth.assertThat(request.toString()).isEqualTo(result)
+    }
+
+    @Test
+    fun testEquals() {
+        val uri1 = Uri.parse("www.abc.com")
+        val uri2 = Uri.parse("www.xyz.com")
+        val params = listOf(uri1, uri2)
+        val inputEvent = mock(InputEvent::class.java)
+        val request1 = SourceRegistrationRequest.Builder(params)
+            .setInputEvent(inputEvent)
+            .build()
+        val request2 = SourceRegistrationRequest(params, inputEvent)
+        val request3 = SourceRegistrationRequest.Builder(params).build()
+
+        Truth.assertThat(request1 == request2).isTrue()
+        Truth.assertThat(request1 != request3).isTrue()
+    }
+}
diff --git a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsRequestTest.kt b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsRequestTest.kt
index e76c23e..70cd0c4 100644
--- a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsRequestTest.kt
+++ b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsRequestTest.kt
@@ -18,6 +18,7 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import androidx.testutils.assertThrows
 import com.google.common.truth.Truth
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -41,4 +42,30 @@
         // Verify equality.
         Truth.assertThat(request == request2).isTrue()
     }
+
+    @Test
+    fun testToString_emptySdkName() {
+        val result = "GetTopicsRequest: adsSdkName=, shouldRecordObservation=true"
+        val request = GetTopicsRequest("", true)
+        Truth.assertThat(request.toString()).isEqualTo(result)
+
+        // Verify Builder.
+        val request2 = GetTopicsRequest.Builder()
+            .setShouldRecordObservation(true)
+            .build()
+        Truth.assertThat(request.toString()).isEqualTo(result)
+
+        // Verify equality.
+        Truth.assertThat(request == request2).isTrue()
+    }
+
+    @Test
+    fun testBuilder_setEmptyAdsSdkName_throwsError() {
+        assertThrows(IllegalStateException::class.java) {
+            GetTopicsRequest.Builder()
+                .setAdsSdkName("")
+                .setShouldRecordObservation(true)
+                .build()
+        }
+    }
 }
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/ExperimentalFeatures.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/ExperimentalFeatures.kt
new file mode 100644
index 0000000..e1935b3
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/common/ExperimentalFeatures.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.ads.adservices.common
+
+/**
+ * Contains AdServices experimental feature opt-in anntations.
+ */
+sealed interface ExperimentalFeatures {
+    /**
+     * Clients should use it when they want to use [MeasurementManager#registerSource] API.
+     */
+    @RequiresOptIn("This API is experimental.", RequiresOptIn.Level.WARNING)
+    annotation class RegisterSourceOptIn
+}
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManager.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManager.kt
index e316ab0..de62bd2 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManager.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManager.kt
@@ -27,7 +27,10 @@
 import androidx.annotation.RequiresExtension
 import androidx.annotation.RequiresPermission
 import androidx.core.os.asOutcomeReceiver
+import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
 import androidx.privacysandbox.ads.adservices.internal.AdServicesInfo
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
 import kotlinx.coroutines.suspendCancellableCoroutine
 
 /**
@@ -82,6 +85,16 @@
     abstract suspend fun registerWebTrigger(request: WebTriggerRegistrationRequest)
 
     /**
+     * Register an attribution source(click or view) context. This API will not process any
+     * redirects, all registration URLs should be supplied with the request.
+     *
+     * @param request source registration request
+     */
+    @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
+    @ExperimentalFeatures.RegisterSourceOptIn
+    abstract suspend fun registerSource(request: SourceRegistrationRequest)
+
+    /**
      * Get Measurement API status.
      *
      * The call returns an integer value (see [MEASUREMENT_API_STATE_DISABLED] and
@@ -160,6 +173,26 @@
             }
         }
 
+        @DoNotInline
+        @ExperimentalFeatures.RegisterSourceOptIn
+        @RequiresPermission(ACCESS_ADSERVICES_ATTRIBUTION)
+        override suspend fun registerSource(
+            request: SourceRegistrationRequest
+        ): Unit = coroutineScope {
+            request.registrationUris.forEach { uri ->
+                launch {
+                    suspendCancellableCoroutine<Any> { continuation ->
+                        mMeasurementManager.registerSource(
+                            uri,
+                            request.inputEvent,
+                            Runnable::run,
+                            continuation.asOutcomeReceiver()
+                        )
+                    }
+                }
+            }
+        }
+
         private fun convertWebSourceRequest(
             request: WebSourceRegistrationRequest
         ): android.adservices.measurement.WebSourceRegistrationRequest {
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/SourceRegistrationRequest.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/SourceRegistrationRequest.kt
new file mode 100644
index 0000000..7bda8bf
--- /dev/null
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/SourceRegistrationRequest.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.ads.adservices.measurement
+
+import android.net.Uri
+import android.view.InputEvent
+import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
+
+/**
+ * Class to hold input to measurement source registration calls from web context.
+ *
+ * @param registrationUris [List] of Registration [Uri]s to fetch sources.
+ * @param inputEvent User Interaction [InputEvent] used by the AttributionReporting API to
+ * distinguish clicks from views.
+ */
[email protected]
+class SourceRegistrationRequest constructor(
+    val registrationUris: List<Uri>,
+    val inputEvent: InputEvent? = null
+    ) {
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other !is SourceRegistrationRequest) return false
+        return this.registrationUris == other.registrationUris &&
+            this.inputEvent == other.inputEvent
+    }
+
+    override fun hashCode(): Int {
+        var hash = registrationUris.hashCode()
+        if (inputEvent != null) {
+            hash = 31 * hash + inputEvent.hashCode()
+        }
+        return hash
+    }
+
+    override fun toString(): String {
+        val vals = "RegistrationUris=[$registrationUris], InputEvent=$inputEvent"
+        return "AppSourcesRegistrationRequest { $vals }"
+    }
+
+    /**
+     * Builder for [SourceRegistrationRequest].
+     *
+     * @param registrationUris source registration request [Uri]
+     */
+    class Builder(
+        private val registrationUris: List<Uri>
+    ) {
+        private var inputEvent: InputEvent? = null
+
+        /**
+         * Setter for input event.
+         *
+         * @param inputEvent User Interaction InputEvent used by the AttributionReporting API to
+         *     distinguish clicks from views.
+         * @return builder
+         */
+        fun setInputEvent(inputEvent: InputEvent): Builder = apply {
+            this.inputEvent = inputEvent
+        }
+
+        /** Pre-validates parameters and builds [SourceRegistrationRequest]. */
+        fun build(): SourceRegistrationRequest {
+            return SourceRegistrationRequest(
+                registrationUris,
+                inputEvent
+            )
+        }
+    }
+}
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsRequest.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsRequest.kt
index d8f4a81..6dede0c 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsRequest.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/topics/GetTopicsRequest.kt
@@ -64,7 +64,10 @@
          *
          * @param adsSdkName the Ads Sdk Name.
          */
-        fun setAdsSdkName(adsSdkName: String): Builder = apply { this.adsSdkName = adsSdkName }
+        fun setAdsSdkName(adsSdkName: String): Builder = apply {
+            check(adsSdkName.isNotEmpty()) { "adsSdkName must be set" }
+            this.adsSdkName = adsSdkName
+        }
 
         /**
          * Set the Record Observation.
@@ -80,7 +83,6 @@
 
         /** Builds a [GetTopicsRequest] instance. */
         fun build(): GetTopicsRequest {
-            check(adsSdkName.isNotEmpty()) { "adsSdkName must be set" }
             return GetTopicsRequest(adsSdkName, shouldRecordObservation)
         }
     }
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/build.gradle b/privacysandbox/sdkruntime/sdkruntime-client/build.gradle
index beeef14..704d543 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/build.gradle
+++ b/privacysandbox/sdkruntime/sdkruntime-client/build.gradle
@@ -30,7 +30,7 @@
     api project(path: ':privacysandbox:sdkruntime:sdkruntime-core')
 
     implementation("androidx.core:core:1.12.0-alpha05")
-    implementation("androidx.activity:activity:1.8.0-alpha05")
+    implementation("androidx.activity:activity:1.8.0-alpha06")
 
     testImplementation(libs.junit)
     testImplementation(libs.truth)
diff --git a/privacysandbox/sdkruntime/sdkruntime-core/build.gradle b/privacysandbox/sdkruntime/sdkruntime-core/build.gradle
index 2eec85b..a5ca74b 100644
--- a/privacysandbox/sdkruntime/sdkruntime-core/build.gradle
+++ b/privacysandbox/sdkruntime/sdkruntime-core/build.gradle
@@ -27,7 +27,7 @@
     api("androidx.annotation:annotation:1.6.0")
 
     implementation("androidx.core:core:1.12.0-alpha05")
-    implementation("androidx.activity:activity:1.8.0-alpha05")
+    implementation("androidx.activity:activity:1.8.0-alpha06")
 
     // TODO(b/249982004): cleanup dependencies
     androidTestImplementation(libs.testCore)
diff --git a/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/AbstractSdkProviderGenerator.kt b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/AbstractSdkProviderGenerator.kt
index 540f146..21bb582 100644
--- a/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/AbstractSdkProviderGenerator.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/AbstractSdkProviderGenerator.kt
@@ -61,7 +61,7 @@
 
     private fun generateGetViewFunction(): FunSpec {
         return FunSpec.builder("getView").build {
-            addModifiers(KModifier.OVERRIDE)
+            addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE)
             addParameter("windowContext", contextClass)
             addParameter("params", bundleClass)
             addParameter("width", Int::class)
diff --git a/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/Api33SdkProviderGenerator.kt b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/Api33SdkProviderGenerator.kt
index 5639123..f4c9aef 100644
--- a/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/Api33SdkProviderGenerator.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/Api33SdkProviderGenerator.kt
@@ -40,7 +40,7 @@
         ClassName("android.app.sdksandbox", "SandboxedSdkProvider")
 
     override fun generateOnLoadSdkFunction() = FunSpec.builder("onLoadSdk").build {
-        addModifiers(KModifier.OVERRIDE)
+        addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE)
         addParameter("params", bundleClass)
         returns(sandboxedSdkClass)
 
diff --git a/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/CompatSdkProviderGenerator.kt b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/CompatSdkProviderGenerator.kt
index 5053460f..14bd699 100644
--- a/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/CompatSdkProviderGenerator.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/CompatSdkProviderGenerator.kt
@@ -40,7 +40,7 @@
         ClassName("androidx.privacysandbox.sdkruntime.core", "SandboxedSdkProviderCompat")
 
     override fun generateOnLoadSdkFunction() = FunSpec.builder("onLoadSdk").build {
-        addModifiers(KModifier.OVERRIDE)
+        addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE)
         addParameter("params", bundleClass)
         returns(sandboxedSdkCompatClass)
 
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MyCallbackClientProxy.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MyCallbackClientProxy.kt
index b471257..af49258 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MyCallbackClientProxy.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MyCallbackClientProxy.kt
@@ -7,19 +7,19 @@
     public val remote: IMyCallback,
     public val context: Context,
 ) : MyCallback {
-    public override fun onComplete(response: Response): Unit {
+    public override fun onComplete(response: Response) {
         remote.onComplete(ResponseConverter(context).toParcelable(response))
     }
 
-    public override fun onClick(x: Int, y: Int): Unit {
+    public override fun onClick(x: Int, y: Int) {
         remote.onClick(x, y)
     }
 
-    public override fun onCompleteInterface(myInterface: MyInterface): Unit {
+    public override fun onCompleteInterface(myInterface: MyInterface) {
         remote.onCompleteInterface(MyInterfaceStubDelegate(myInterface, context))
     }
 
-    public override fun onCompleteUiInterface(myUiInterface: MyUiInterface): Unit {
+    public override fun onCompleteUiInterface(myUiInterface: MyUiInterface) {
         remote.onCompleteUiInterface(IMyUiInterfaceCoreLibInfoAndBinderWrapperConverter.toParcelable(myUiInterface.toCoreLibInfo(context),
                 MyUiInterfaceStubDelegate(myUiInterface, context)))
     }
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MyInterfaceStubDelegate.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MyInterfaceStubDelegate.kt
index 0f0d2f7..223d580 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MyInterfaceStubDelegate.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MyInterfaceStubDelegate.kt
@@ -1,10 +1,8 @@
 package com.mysdk
 
 import android.content.Context
-import com.mysdk.PrivacySandboxThrowableParcelConverter
 import com.mysdk.PrivacySandboxThrowableParcelConverter.toThrowableParcel
 import kotlin.Int
-import kotlin.Unit
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
@@ -16,7 +14,7 @@
   private val coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.Main)
 
   public override fun doSomething(request: ParcelableRequest,
-      transactionCallback: IResponseTransactionCallback): Unit {
+      transactionCallback: IResponseTransactionCallback) {
     val job = coroutineScope.launch {
       try {
         val result = delegate.doSomething(RequestConverter(context).fromParcelable(request))
@@ -31,7 +29,7 @@
   }
 
   public override fun getMyInterface(input: IMyInterface,
-      transactionCallback: IMyInterfaceTransactionCallback): Unit {
+      transactionCallback: IMyInterfaceTransactionCallback) {
     val job = coroutineScope.launch {
       try {
         val result = delegate.getMyInterface((input as MyInterfaceStubDelegate).delegate)
@@ -46,7 +44,7 @@
   }
 
   public override fun getMySecondInterface(input: IMySecondInterface,
-      transactionCallback: IMySecondInterfaceTransactionCallback): Unit {
+      transactionCallback: IMySecondInterfaceTransactionCallback) {
     val job = coroutineScope.launch {
       try {
         val result = delegate.getMySecondInterface((input as
@@ -61,7 +59,7 @@
     transactionCallback.onCancellable(cancellationSignal)
   }
 
-  public override fun doMoreStuff(x: Int): Unit {
+  public override fun doMoreStuff(x: Int) {
     coroutineScope.launch {
       delegate.doMoreStuff(x)
     }
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MySdkStubDelegate.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MySdkStubDelegate.kt
index b1f2995..3f376f2 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MySdkStubDelegate.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MySdkStubDelegate.kt
@@ -3,11 +3,9 @@
 import android.content.Context
 import android.os.Bundle
 import androidx.privacysandbox.ui.provider.toCoreLibInfo
-import com.mysdk.PrivacySandboxThrowableParcelConverter
 import com.mysdk.PrivacySandboxThrowableParcelConverter.toThrowableParcel
 import kotlin.Int
 import kotlin.IntArray
-import kotlin.Unit
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
@@ -22,7 +20,7 @@
     x: Int,
     y: Int,
     transactionCallback: IStringTransactionCallback,
-  ): Unit {
+  ) {
     val job = coroutineScope.launch {
       try {
         val result = delegate.doStuff(x, y)
@@ -37,7 +35,7 @@
   }
 
   public override fun handleRequest(request: ParcelableRequest,
-      transactionCallback: IResponseTransactionCallback): Unit {
+      transactionCallback: IResponseTransactionCallback) {
     val job = coroutineScope.launch {
       try {
         val result = delegate.handleRequest(RequestConverter(context).fromParcelable(request))
@@ -52,7 +50,7 @@
   }
 
   public override fun logRequest(request: ParcelableRequest,
-      transactionCallback: IUnitTransactionCallback): Unit {
+      transactionCallback: IUnitTransactionCallback) {
     val job = coroutineScope.launch {
       try {
         delegate.logRequest(RequestConverter(context).fromParcelable(request))
@@ -66,20 +64,20 @@
     transactionCallback.onCancellable(cancellationSignal)
   }
 
-  public override fun setListener(listener: IMyCallback): Unit {
+  public override fun setListener(listener: IMyCallback) {
     coroutineScope.launch {
       delegate.setListener(MyCallbackClientProxy(listener, context))
     }
   }
 
-  public override fun doMoreStuff(): Unit {
+  public override fun doMoreStuff() {
     coroutineScope.launch {
       delegate.doMoreStuff()
     }
   }
 
   public override fun getMyInterface(input: IMyInterface,
-      transactionCallback: IMyInterfaceTransactionCallback): Unit {
+      transactionCallback: IMyInterfaceTransactionCallback) {
     val job = coroutineScope.launch {
       try {
         val result = delegate.getMyInterface((input as MyInterfaceStubDelegate).delegate)
@@ -93,7 +91,7 @@
     transactionCallback.onCancellable(cancellationSignal)
   }
 
-  public override fun mutateMySecondInterface(input: IMySecondInterface): Unit {
+  public override fun mutateMySecondInterface(input: IMySecondInterface) {
     coroutineScope.launch {
       delegate.mutateMySecondInterface((input as MySecondInterfaceStubDelegate).delegate)
     }
@@ -103,7 +101,7 @@
     x: IntArray,
     y: IntArray,
     transactionCallback: IListStringTransactionCallback,
-  ): Unit {
+  ) {
     val job = coroutineScope.launch {
       try {
         val result = delegate.handleNullablePrimitives(x.firstOrNull(), y.firstOrNull())
@@ -118,7 +116,7 @@
   }
 
   public override fun handleNullableValues(maybeRequest: ParcelableRequest?,
-      transactionCallback: IResponseTransactionCallback): Unit {
+      transactionCallback: IResponseTransactionCallback) {
     val job = coroutineScope.launch {
       try {
         val result = delegate.handleNullableValues(maybeRequest?.let { notNullValue ->
@@ -135,7 +133,7 @@
   }
 
   public override fun handleNullableInterfaces(maybeCallback: IMyCallback?,
-      transactionCallback: IMyInterfaceTransactionCallback): Unit {
+      transactionCallback: IMyInterfaceTransactionCallback) {
     val job = coroutineScope.launch {
       try {
         val result = delegate.handleNullableInterfaces(maybeCallback?.let { notNullValue ->
@@ -151,8 +149,7 @@
     transactionCallback.onCancellable(cancellationSignal)
   }
 
-  public override fun returnUiInterface(transactionCallback: IMyUiInterfaceTransactionCallback):
-      Unit {
+  public override fun returnUiInterface(transactionCallback: IMyUiInterfaceTransactionCallback) {
     val job = coroutineScope.launch {
       try {
         val result = delegate.returnUiInterface()
@@ -167,22 +164,20 @@
     transactionCallback.onCancellable(cancellationSignal)
   }
 
-  public override fun acceptUiInterfaceParam(input: IMyUiInterfaceCoreLibInfoAndBinderWrapper):
-      Unit {
+  public override fun acceptUiInterfaceParam(input: IMyUiInterfaceCoreLibInfoAndBinderWrapper) {
     coroutineScope.launch {
       delegate.acceptUiInterfaceParam((input.binder as MyUiInterfaceStubDelegate).delegate)
     }
   }
 
-  public override fun acceptSdkActivityLauncherParam(activityLauncher: Bundle): Unit {
+  public override fun acceptSdkActivityLauncherParam(activityLauncher: Bundle) {
     coroutineScope.launch {
       delegate.acceptSdkActivityLauncherParam(SdkActivityLauncherAndBinderWrapper(activityLauncher))
     }
   }
 
   public override
-      fun returnSdkActivityLauncher(transactionCallback: ISdkActivityLauncherTransactionCallback):
-      Unit {
+      fun returnSdkActivityLauncher(transactionCallback: ISdkActivityLauncherTransactionCallback) {
     val job = coroutineScope.launch {
       try {
         val result = delegate.returnSdkActivityLauncher()
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MySecondInterfaceStubDelegate.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MySecondInterfaceStubDelegate.kt
index 817f83a..af61158b 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MySecondInterfaceStubDelegate.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MySecondInterfaceStubDelegate.kt
@@ -1,7 +1,6 @@
 package com.mysdk
 
 import android.content.Context
-import com.mysdk.PrivacySandboxThrowableParcelConverter
 import com.mysdk.PrivacySandboxThrowableParcelConverter.toThrowableParcel
 import kotlin.Array
 import kotlin.BooleanArray
@@ -11,7 +10,6 @@
 import kotlin.IntArray
 import kotlin.LongArray
 import kotlin.String
-import kotlin.Unit
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
@@ -22,8 +20,7 @@
 ) : IMySecondInterface.Stub() {
   private val coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.Main)
 
-  public override fun doIntStuff(x: IntArray, transactionCallback: IListIntTransactionCallback):
-      Unit {
+  public override fun doIntStuff(x: IntArray, transactionCallback: IListIntTransactionCallback) {
     val job = coroutineScope.launch {
       try {
         val result = delegate.doIntStuff(x.toList())
@@ -37,8 +34,7 @@
     transactionCallback.onCancellable(cancellationSignal)
   }
 
-  public override fun doCharStuff(x: CharArray, transactionCallback: IListCharTransactionCallback):
-      Unit {
+  public override fun doCharStuff(x: CharArray, transactionCallback: IListCharTransactionCallback) {
     val job = coroutineScope.launch {
       try {
         val result = delegate.doCharStuff(x.toList())
@@ -53,7 +49,7 @@
   }
 
   public override fun doFloatStuff(x: FloatArray,
-      transactionCallback: IListFloatTransactionCallback): Unit {
+      transactionCallback: IListFloatTransactionCallback) {
     val job = coroutineScope.launch {
       try {
         val result = delegate.doFloatStuff(x.toList())
@@ -67,8 +63,7 @@
     transactionCallback.onCancellable(cancellationSignal)
   }
 
-  public override fun doLongStuff(x: LongArray, transactionCallback: IListLongTransactionCallback):
-      Unit {
+  public override fun doLongStuff(x: LongArray, transactionCallback: IListLongTransactionCallback) {
     val job = coroutineScope.launch {
       try {
         val result = delegate.doLongStuff(x.toList())
@@ -83,7 +78,7 @@
   }
 
   public override fun doDoubleStuff(x: DoubleArray,
-      transactionCallback: IListDoubleTransactionCallback): Unit {
+      transactionCallback: IListDoubleTransactionCallback) {
     val job = coroutineScope.launch {
       try {
         val result = delegate.doDoubleStuff(x.toList())
@@ -98,7 +93,7 @@
   }
 
   public override fun doBooleanStuff(x: BooleanArray,
-      transactionCallback: IListBooleanTransactionCallback): Unit {
+      transactionCallback: IListBooleanTransactionCallback) {
     val job = coroutineScope.launch {
       try {
         val result = delegate.doBooleanStuff(x.toList())
@@ -112,8 +107,8 @@
     transactionCallback.onCancellable(cancellationSignal)
   }
 
-  public override fun doShortStuff(x: IntArray, transactionCallback: IListShortTransactionCallback):
-      Unit {
+  public override fun doShortStuff(x: IntArray,
+      transactionCallback: IListShortTransactionCallback) {
     val job = coroutineScope.launch {
       try {
         val result = delegate.doShortStuff(x.map { it.toShort() }.toList())
@@ -128,7 +123,7 @@
   }
 
   public override fun doStringStuff(x: Array<String>,
-      transactionCallback: IListStringTransactionCallback): Unit {
+      transactionCallback: IListStringTransactionCallback) {
     val job = coroutineScope.launch {
       try {
         val result = delegate.doStringStuff(x.toList())
@@ -143,7 +138,7 @@
   }
 
   public override fun doValueStuff(x: Array<ParcelableRequest>,
-      transactionCallback: IListResponseTransactionCallback): Unit {
+      transactionCallback: IListResponseTransactionCallback) {
     val job = coroutineScope.launch {
       try {
         val result = delegate.doValueStuff(x.map { RequestConverter(context).fromParcelable(it)
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MyUiInterfaceStubDelegate.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MyUiInterfaceStubDelegate.kt
index b234751..747598f 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MyUiInterfaceStubDelegate.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/MyUiInterfaceStubDelegate.kt
@@ -2,7 +2,6 @@
 
 import android.content.Context
 import kotlin.Int
-import kotlin.Unit
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
@@ -13,7 +12,7 @@
 ) : IMyUiInterface.Stub() {
   private val coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.Main)
 
-  public override fun doSomethingForUi(x: Int, y: Int): Unit {
+  public override fun doSomethingForUi(x: Int, y: Int) {
     coroutineScope.launch {
       delegate.doSomethingForUi(x, y)
     }
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/TransportCancellationCallback.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/TransportCancellationCallback.kt
index 287dc52..ba1815c 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/TransportCancellationCallback.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/fullfeaturedsdk/output/com/mysdk/TransportCancellationCallback.kt
@@ -8,7 +8,7 @@
 ) : ICancellationSignal.Stub() {
   private val hasCancelled: AtomicBoolean = AtomicBoolean(false)
 
-  public override fun cancel(): Unit {
+  public override fun cancel() {
     if (hasCancelled.compareAndSet(false, true)) {
       onCancel()
     }
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkwithpackages/output/com/myotherpackage/MyOtherPackageInterfaceStubDelegate.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkwithpackages/output/com/myotherpackage/MyOtherPackageInterfaceStubDelegate.kt
index 594b8e3..ad1f00a 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkwithpackages/output/com/myotherpackage/MyOtherPackageInterfaceStubDelegate.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkwithpackages/output/com/myotherpackage/MyOtherPackageInterfaceStubDelegate.kt
@@ -5,7 +5,6 @@
 import com.mysdk.PrivacySandboxThrowableParcelConverter.toThrowableParcel
 import com.mysdk.TransportCancellationCallback
 import kotlin.Int
-import kotlin.Unit
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
@@ -16,14 +15,14 @@
 ) : IMyOtherPackageInterface.Stub() {
   private val coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.Main)
 
-  public override fun doStuff(x: Int): Unit {
+  public override fun doStuff(x: Int) {
     coroutineScope.launch {
       delegate.doStuff(x)
     }
   }
 
   public override fun useDataClass(x: ParcelableMyOtherPackageDataClass,
-      transactionCallback: IUnitTransactionCallback): Unit {
+      transactionCallback: IUnitTransactionCallback) {
     val job = coroutineScope.launch {
       try {
         delegate.useDataClass(MyOtherPackageDataClassConverter(context).fromParcelable(x))
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkwithpackages/output/com/mysdk/MyMainPackageInterfaceStubDelegate.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkwithpackages/output/com/mysdk/MyMainPackageInterfaceStubDelegate.kt
index 661bf8c..c5bf8e7 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkwithpackages/output/com/mysdk/MyMainPackageInterfaceStubDelegate.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkwithpackages/output/com/mysdk/MyMainPackageInterfaceStubDelegate.kt
@@ -3,10 +3,8 @@
 import android.content.Context
 import com.myotherpackage.MyOtherPackageDataClassConverter
 import com.myotherpackage.ParcelableMyOtherPackageDataClass
-import com.mysdk.PrivacySandboxThrowableParcelConverter
 import com.mysdk.PrivacySandboxThrowableParcelConverter.toThrowableParcel
 import kotlin.IntArray
-import kotlin.Unit
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
@@ -17,8 +15,7 @@
 ) : IMyMainPackageInterface.Stub() {
   private val coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.Main)
 
-  public override fun doIntStuff(x: IntArray, transactionCallback: IListIntTransactionCallback):
-      Unit {
+  public override fun doIntStuff(x: IntArray, transactionCallback: IListIntTransactionCallback) {
     val job = coroutineScope.launch {
       try {
         val result = delegate.doIntStuff(x.toList())
@@ -33,7 +30,7 @@
   }
 
   public override fun useDataClass(x: ParcelableMyOtherPackageDataClass,
-      transactionCallback: IMyOtherPackageDataClassTransactionCallback): Unit {
+      transactionCallback: IMyOtherPackageDataClassTransactionCallback) {
     val job = coroutineScope.launch {
       try {
         val result =
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkwithpackages/output/com/mysdk/MySdkStubDelegate.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkwithpackages/output/com/mysdk/MySdkStubDelegate.kt
index b9c2fb5..d95fdd0 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkwithpackages/output/com/mysdk/MySdkStubDelegate.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkwithpackages/output/com/mysdk/MySdkStubDelegate.kt
@@ -2,10 +2,8 @@
 
 import android.content.Context
 import com.myotherpackage.MyOtherPackageInterfaceStubDelegate
-import com.mysdk.PrivacySandboxThrowableParcelConverter
 import com.mysdk.PrivacySandboxThrowableParcelConverter.toThrowableParcel
 import kotlin.Int
-import kotlin.Unit
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
@@ -20,7 +18,7 @@
     x: Int,
     y: Int,
     transactionCallback: IStringTransactionCallback,
-  ): Unit {
+  ) {
     val job = coroutineScope.launch {
       try {
         val result = delegate.doStuff(x, y)
@@ -35,7 +33,7 @@
   }
 
   public override
-      fun getMyInterface(transactionCallback: IMyMainPackageInterfaceTransactionCallback): Unit {
+      fun getMyInterface(transactionCallback: IMyMainPackageInterfaceTransactionCallback) {
     val job = coroutineScope.launch {
       try {
         val result = delegate.getMyInterface()
@@ -50,8 +48,7 @@
   }
 
   public override
-      fun getMyOtherPackageInterface(transactionCallback: IMyOtherPackageInterfaceTransactionCallback):
-      Unit {
+      fun getMyOtherPackageInterface(transactionCallback: IMyOtherPackageInterfaceTransactionCallback) {
     val job = coroutineScope.launch {
       try {
         val result = delegate.getMyOtherPackageInterface()
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkwithpackages/output/com/mysdk/TransportCancellationCallback.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkwithpackages/output/com/mysdk/TransportCancellationCallback.kt
index 287dc52..ba1815c 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkwithpackages/output/com/mysdk/TransportCancellationCallback.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/sdkwithpackages/output/com/mysdk/TransportCancellationCallback.kt
@@ -8,7 +8,7 @@
 ) : ICancellationSignal.Stub() {
   private val hasCancelled: AtomicBoolean = AtomicBoolean(false)
 
-  public override fun cancel(): Unit {
+  public override fun cancel() {
     if (hasCancelled.compareAndSet(false, true)) {
       onCancel()
     }
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/withoutruntimelibrarysdk/output/com/mysdk/TransportCancellationCallback.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/withoutruntimelibrarysdk/output/com/mysdk/TransportCancellationCallback.kt
index 287dc52..ba1815c 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/test-data/withoutruntimelibrarysdk/output/com/mysdk/TransportCancellationCallback.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/withoutruntimelibrarysdk/output/com/mysdk/TransportCancellationCallback.kt
@@ -8,7 +8,7 @@
 ) : ICancellationSignal.Stub() {
   private val hasCancelled: AtomicBoolean = AtomicBoolean(false)
 
-  public override fun cancel(): Unit {
+  public override fun cancel() {
     if (hasCancelled.compareAndSet(false, true)) {
       onCancel()
     }
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/withoutruntimelibrarysdk/output/com/mysdk/WithoutRuntimeLibrarySdkStubDelegate.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/withoutruntimelibrarysdk/output/com/mysdk/WithoutRuntimeLibrarySdkStubDelegate.kt
index c46719e..a814b72 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/test-data/withoutruntimelibrarysdk/output/com/mysdk/WithoutRuntimeLibrarySdkStubDelegate.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/withoutruntimelibrarysdk/output/com/mysdk/WithoutRuntimeLibrarySdkStubDelegate.kt
@@ -3,7 +3,6 @@
 import android.content.Context
 import com.mysdk.PrivacySandboxThrowableParcelConverter.toThrowableParcel
 import kotlin.Int
-import kotlin.Unit
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
@@ -18,7 +17,7 @@
     x: Int,
     y: Int,
     transactionCallback: IStringTransactionCallback,
-  ): Unit {
+  ) {
     val job = coroutineScope.launch {
       try {
         val result = delegate.doStuff(x, y)
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/MyInterface.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/MyInterface.kt
index 0474314..0b68d85 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/MyInterface.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/MyInterface.kt
@@ -1,5 +1,5 @@
 package com.sdkwithcallbacks
 
 public interface MyInterface {
-    public fun doStuff(): Unit
+    public fun doStuff()
 }
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/MyInterfaceClientProxy.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/MyInterfaceClientProxy.kt
index f325ee4..c11a869 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/MyInterfaceClientProxy.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/MyInterfaceClientProxy.kt
@@ -3,7 +3,7 @@
 public class MyInterfaceClientProxy(
     public val remote: IMyInterface,
 ) : MyInterface {
-    public override fun doStuff(): Unit {
+    public override fun doStuff() {
         remote.doStuff()
     }
 }
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/MyUiInterface.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/MyUiInterface.kt
index 2975910..b337c45 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/MyUiInterface.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/MyUiInterface.kt
@@ -3,5 +3,5 @@
 import androidx.privacysandbox.ui.core.SandboxedUiAdapter
 
 public interface MyUiInterface : SandboxedUiAdapter {
-    public fun doUiStuff(): Unit
+    public fun doUiStuff()
 }
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/MyUiInterfaceClientProxy.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/MyUiInterfaceClientProxy.kt
index 00ccc68..4d8eeb9 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/MyUiInterfaceClientProxy.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/MyUiInterfaceClientProxy.kt
@@ -2,6 +2,7 @@
 
 import android.content.Context
 import android.os.Bundle
+import android.os.IBinder
 import androidx.privacysandbox.ui.client.SandboxedUiAdapterFactory
 import androidx.privacysandbox.ui.core.SandboxedUiAdapter
 import androidx.privacysandbox.ui.core.SandboxedUiAdapter.SessionClient
@@ -14,19 +15,20 @@
     public val sandboxedUiAdapter: SandboxedUiAdapter =
             SandboxedUiAdapterFactory.createFromCoreLibInfo(coreLibInfo)
 
-    public override fun doUiStuff(): Unit {
+    public override fun doUiStuff() {
         remote.doUiStuff()
     }
 
     public override fun openSession(
         context: Context,
+        windowInputToken: IBinder,
         initialWidth: Int,
         initialHeight: Int,
         isZOrderOnTop: Boolean,
         clientExecutor: Executor,
         client: SandboxedUiAdapter.SessionClient,
-    ): Unit {
-        sandboxedUiAdapter.openSession(context, initialWidth, initialHeight, isZOrderOnTop,
-                clientExecutor, client)
+    ) {
+        sandboxedUiAdapter.openSession(context, windowInputToken, initialWidth, initialHeight,
+                isZOrderOnTop, clientExecutor, client)
     }
 }
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/SdkCallback.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/SdkCallback.kt
index ec21831..040ec55 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/SdkCallback.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/SdkCallback.kt
@@ -3,13 +3,13 @@
 import androidx.privacysandbox.ui.core.SdkActivityLauncher
 
 public interface SdkCallback {
-    public fun onCompleteInterface(myInterface: MyInterface): Unit
+    public fun onCompleteInterface(myInterface: MyInterface)
 
-    public fun onEmptyEvent(): Unit
+    public fun onEmptyEvent()
 
-    public fun onPrimitivesReceived(x: Int, y: Int): Unit
+    public fun onPrimitivesReceived(x: Int, y: Int)
 
-    public fun onSdkActivityLauncherReceived(myLauncher: SdkActivityLauncher): Unit
+    public fun onSdkActivityLauncherReceived(myLauncher: SdkActivityLauncher)
 
-    public fun onValueReceived(response: Response): Unit
+    public fun onValueReceived(response: Response)
 }
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/SdkCallbackStubDelegate.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/SdkCallbackStubDelegate.kt
index 3bc4ed5..f98dafb 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/SdkCallbackStubDelegate.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/SdkCallbackStubDelegate.kt
@@ -4,7 +4,6 @@
 import com.sdkwithcallbacks.ResponseConverter.fromParcelable
 import com.sdkwithcallbacks.SdkActivityLauncherConverter.getLocalOrProxyLauncher
 import kotlin.Int
-import kotlin.Unit
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
@@ -14,31 +13,31 @@
 ) : ISdkCallback.Stub() {
   private val coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.Main)
 
-  public override fun onCompleteInterface(myInterface: IMyInterface): Unit {
+  public override fun onCompleteInterface(myInterface: IMyInterface) {
     coroutineScope.launch {
       delegate.onCompleteInterface(MyInterfaceClientProxy(myInterface))
     }
   }
 
-  public override fun onEmptyEvent(): Unit {
+  public override fun onEmptyEvent() {
     coroutineScope.launch {
       delegate.onEmptyEvent()
     }
   }
 
-  public override fun onPrimitivesReceived(x: Int, y: Int): Unit {
+  public override fun onPrimitivesReceived(x: Int, y: Int) {
     coroutineScope.launch {
       delegate.onPrimitivesReceived(x, y)
     }
   }
 
-  public override fun onSdkActivityLauncherReceived(myLauncher: Bundle): Unit {
+  public override fun onSdkActivityLauncherReceived(myLauncher: Bundle) {
     coroutineScope.launch {
       delegate.onSdkActivityLauncherReceived(getLocalOrProxyLauncher(myLauncher))
     }
   }
 
-  public override fun onValueReceived(response: ParcelableResponse): Unit {
+  public override fun onValueReceived(response: ParcelableResponse) {
     coroutineScope.launch {
       delegate.onValueReceived(fromParcelable(response))
     }
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/SdkService.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/SdkService.kt
index 510bd89..3a427d3 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/SdkService.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/SdkService.kt
@@ -1,5 +1,5 @@
 package com.sdkwithcallbacks
 
 public interface SdkService {
-    public fun registerCallback(callback: SdkCallback): Unit
+    public fun registerCallback(callback: SdkCallback)
 }
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/SdkServiceClientProxy.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/SdkServiceClientProxy.kt
index 83a8df2..d69a96b 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/SdkServiceClientProxy.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/SdkServiceClientProxy.kt
@@ -3,7 +3,7 @@
 public class SdkServiceClientProxy(
     public val remote: ISdkService,
 ) : SdkService {
-    public override fun registerCallback(callback: SdkCallback): Unit {
+    public override fun registerCallback(callback: SdkCallback) {
         remote.registerCallback(SdkCallbackStubDelegate(callback))
     }
 }
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MyInterface.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MyInterface.kt
index cc0fb50..61b2aa6 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MyInterface.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MyInterface.kt
@@ -5,9 +5,9 @@
 public interface MyInterface {
     public suspend fun add(x: Int, y: Int): Int
 
-    public fun doSomething(firstInterface: MyInterface, secondInterface: MySecondInterface): Unit
+    public fun doSomething(firstInterface: MyInterface, secondInterface: MySecondInterface)
 
-    public fun doSomethingWithNullableInterface(maybeInterface: MySecondInterface?): Unit
+    public fun doSomethingWithNullableInterface(maybeInterface: MySecondInterface?)
 
-    public fun doSomethingWithSdkActivityLauncher(launcher: SdkActivityLauncher): Unit
+    public fun doSomethingWithSdkActivityLauncher(launcher: SdkActivityLauncher)
 }
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MyInterfaceClientProxy.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MyInterfaceClientProxy.kt
index 33c8b84..ad696066 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MyInterfaceClientProxy.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MyInterfaceClientProxy.kt
@@ -32,19 +32,19 @@
     }
 
     public override fun doSomething(firstInterface: MyInterface,
-            secondInterface: MySecondInterface): Unit {
+            secondInterface: MySecondInterface) {
         remote.doSomething((firstInterface as MyInterfaceClientProxy).remote,
                 IMySecondInterfaceCoreLibInfoAndBinderWrapperConverter.toParcelable((secondInterface
                 as MySecondInterfaceClientProxy).coreLibInfo, secondInterface.remote))
     }
 
-    public override fun doSomethingWithNullableInterface(maybeInterface: MySecondInterface?): Unit {
+    public override fun doSomethingWithNullableInterface(maybeInterface: MySecondInterface?) {
         remote.doSomethingWithNullableInterface(maybeInterface?.let { notNullValue ->
                 IMySecondInterfaceCoreLibInfoAndBinderWrapperConverter.toParcelable((notNullValue as
                 MySecondInterfaceClientProxy).coreLibInfo, notNullValue.remote) })
     }
 
-    public override fun doSomethingWithSdkActivityLauncher(launcher: SdkActivityLauncher): Unit {
+    public override fun doSomethingWithSdkActivityLauncher(launcher: SdkActivityLauncher) {
         remote.doSomethingWithSdkActivityLauncher(toBinder(launcher))
     }
 }
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MySdkClientProxy.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MySdkClientProxy.kt
index 578aa60..fcb3541 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MySdkClientProxy.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MySdkClientProxy.kt
@@ -1,6 +1,5 @@
 package com.sdk
 
-import com.sdk.PrivacySandboxThrowableParcelConverter
 import com.sdk.PrivacySandboxThrowableParcelConverter.fromThrowableParcel
 import kotlin.coroutines.resumeWithException
 import kotlinx.coroutines.suspendCancellableCoroutine
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MySecondInterface.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MySecondInterface.kt
index 0505e08..36f87f5 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MySecondInterface.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MySecondInterface.kt
@@ -3,5 +3,5 @@
 import androidx.privacysandbox.ui.core.SandboxedUiAdapter
 
 public interface MySecondInterface : SandboxedUiAdapter {
-    public fun doStuff(): Unit
+    public fun doStuff()
 }
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MySecondInterfaceClientProxy.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MySecondInterfaceClientProxy.kt
index 2b6a244..d7f90e3 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MySecondInterfaceClientProxy.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MySecondInterfaceClientProxy.kt
@@ -2,6 +2,7 @@
 
 import android.content.Context
 import android.os.Bundle
+import android.os.IBinder
 import androidx.privacysandbox.ui.client.SandboxedUiAdapterFactory
 import androidx.privacysandbox.ui.core.SandboxedUiAdapter
 import androidx.privacysandbox.ui.core.SandboxedUiAdapter.SessionClient
@@ -14,19 +15,20 @@
     public val sandboxedUiAdapter: SandboxedUiAdapter =
             SandboxedUiAdapterFactory.createFromCoreLibInfo(coreLibInfo)
 
-    public override fun doStuff(): Unit {
+    public override fun doStuff() {
         remote.doStuff()
     }
 
     public override fun openSession(
         context: Context,
+        windowInputToken: IBinder,
         initialWidth: Int,
         initialHeight: Int,
         isZOrderOnTop: Boolean,
         clientExecutor: Executor,
         client: SandboxedUiAdapter.SessionClient,
-    ): Unit {
-        sandboxedUiAdapter.openSession(context, initialWidth, initialHeight, isZOrderOnTop,
-                clientExecutor, client)
+    ) {
+        sandboxedUiAdapter.openSession(context, windowInputToken, initialWidth, initialHeight,
+                isZOrderOnTop, clientExecutor, client)
     }
 }
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/primitives/output/com/mysdk/TestSandboxSdk.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/primitives/output/com/mysdk/TestSandboxSdk.kt
index 912155e..cb65c54 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/primitives/output/com/mysdk/TestSandboxSdk.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/primitives/output/com/mysdk/TestSandboxSdk.kt
@@ -7,19 +7,19 @@
         third: Long,
     ): Boolean
 
-    public fun echoBoolean(input: Boolean): Unit
+    public fun echoBoolean(input: Boolean)
 
-    public fun echoChar(input: Char): Unit
+    public fun echoChar(input: Char)
 
-    public fun echoDouble(input: Double): Unit
+    public fun echoDouble(input: Double)
 
-    public fun echoFloat(input: Float): Unit
+    public fun echoFloat(input: Float)
 
-    public fun echoInt(input: Int): Unit
+    public fun echoInt(input: Int)
 
-    public fun echoLong(input: Long): Unit
+    public fun echoLong(input: Long)
 
-    public fun echoString(input: String): Unit
+    public fun echoString(input: String)
 
     public suspend fun processBooleanList(x: List<Boolean>): List<Boolean>
 
@@ -39,13 +39,13 @@
 
     public suspend fun processStringList(x: List<String>): List<String>
 
-    public fun receiveAndReturnNothing(): Unit
+    public fun receiveAndReturnNothing()
 
-    public suspend fun receiveAndReturnNothingAsync(): Unit
+    public suspend fun receiveAndReturnNothingAsync()
 
     public fun receiveMultipleArguments(
         first: Int,
         second: String,
         third: Long,
-    ): Unit
+    )
 }
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/primitives/output/com/mysdk/TestSandboxSdkClientProxy.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/primitives/output/com/mysdk/TestSandboxSdkClientProxy.kt
index 329c0df..bc02194 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/primitives/output/com/mysdk/TestSandboxSdkClientProxy.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/primitives/output/com/mysdk/TestSandboxSdkClientProxy.kt
@@ -1,6 +1,5 @@
 package com.mysdk
 
-import com.mysdk.PrivacySandboxThrowableParcelConverter
 import com.mysdk.PrivacySandboxThrowableParcelConverter.fromThrowableParcel
 import kotlin.coroutines.resumeWithException
 import kotlinx.coroutines.suspendCancellableCoroutine
@@ -34,31 +33,31 @@
         }
     }
 
-    public override fun echoBoolean(input: Boolean): Unit {
+    public override fun echoBoolean(input: Boolean) {
         remote.echoBoolean(input)
     }
 
-    public override fun echoChar(input: Char): Unit {
+    public override fun echoChar(input: Char) {
         remote.echoChar(input)
     }
 
-    public override fun echoDouble(input: Double): Unit {
+    public override fun echoDouble(input: Double) {
         remote.echoDouble(input)
     }
 
-    public override fun echoFloat(input: Float): Unit {
+    public override fun echoFloat(input: Float) {
         remote.echoFloat(input)
     }
 
-    public override fun echoInt(input: Int): Unit {
+    public override fun echoInt(input: Int) {
         remote.echoInt(input)
     }
 
-    public override fun echoLong(input: Long): Unit {
+    public override fun echoLong(input: Long) {
         remote.echoLong(input)
     }
 
-    public override fun echoString(input: String): Unit {
+    public override fun echoString(input: String) {
         remote.echoString(input)
     }
 
@@ -269,7 +268,7 @@
         }
     }
 
-    public override fun receiveAndReturnNothing(): Unit {
+    public override fun receiveAndReturnNothing() {
         remote.receiveAndReturnNothing()
     }
 
@@ -299,7 +298,7 @@
         first: Int,
         second: String,
         third: Long,
-    ): Unit {
+    ) {
         remote.receiveMultipleArguments(first, second, third)
     }
 }
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/MyInterface.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/MyInterface.kt
index b7db6ef..a8ebad1 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/MyInterface.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/MyInterface.kt
@@ -1,5 +1,5 @@
 package com.sdkwithvalues
 
 public interface MyInterface {
-    public fun doStuff(): Unit
+    public fun doStuff()
 }
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/MyInterfaceClientProxy.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/MyInterfaceClientProxy.kt
index 2fd68e4..781d720 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/MyInterfaceClientProxy.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/MyInterfaceClientProxy.kt
@@ -3,7 +3,7 @@
 public class MyInterfaceClientProxy(
     public val remote: IMyInterface,
 ) : MyInterface {
-    public override fun doStuff(): Unit {
+    public override fun doStuff() {
         remote.doStuff()
     }
 }
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/MyUiInterface.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/MyUiInterface.kt
index 65cbc2b..4bf3412 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/MyUiInterface.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/MyUiInterface.kt
@@ -3,5 +3,5 @@
 import androidx.privacysandbox.ui.core.SandboxedUiAdapter
 
 public interface MyUiInterface : SandboxedUiAdapter {
-    public fun doUiStuff(): Unit
+    public fun doUiStuff()
 }
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/MyUiInterfaceClientProxy.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/MyUiInterfaceClientProxy.kt
index 7a2c6e0..86709a8 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/MyUiInterfaceClientProxy.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/MyUiInterfaceClientProxy.kt
@@ -2,6 +2,7 @@
 
 import android.content.Context
 import android.os.Bundle
+import android.os.IBinder
 import androidx.privacysandbox.ui.client.SandboxedUiAdapterFactory
 import androidx.privacysandbox.ui.core.SandboxedUiAdapter
 import androidx.privacysandbox.ui.core.SandboxedUiAdapter.SessionClient
@@ -14,19 +15,20 @@
     public val sandboxedUiAdapter: SandboxedUiAdapter =
             SandboxedUiAdapterFactory.createFromCoreLibInfo(coreLibInfo)
 
-    public override fun doUiStuff(): Unit {
+    public override fun doUiStuff() {
         remote.doUiStuff()
     }
 
     public override fun openSession(
         context: Context,
+        windowInputToken: IBinder,
         initialWidth: Int,
         initialHeight: Int,
         isZOrderOnTop: Boolean,
         clientExecutor: Executor,
         client: SandboxedUiAdapter.SessionClient,
-    ): Unit {
-        sandboxedUiAdapter.openSession(context, initialWidth, initialHeight, isZOrderOnTop,
-                clientExecutor, client)
+    ) {
+        sandboxedUiAdapter.openSession(context, windowInputToken, initialWidth, initialHeight,
+                isZOrderOnTop, clientExecutor, client)
     }
 }
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/SdkInterfaceClientProxy.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/SdkInterfaceClientProxy.kt
index def4b57..6505a91 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/SdkInterfaceClientProxy.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/SdkInterfaceClientProxy.kt
@@ -1,10 +1,7 @@
 package com.sdkwithvalues
 
-import com.sdkwithvalues.PrivacySandboxThrowableParcelConverter
 import com.sdkwithvalues.PrivacySandboxThrowableParcelConverter.fromThrowableParcel
-import com.sdkwithvalues.SdkRequestConverter
 import com.sdkwithvalues.SdkRequestConverter.toParcelable
-import com.sdkwithvalues.SdkResponseConverter
 import com.sdkwithvalues.SdkResponseConverter.fromParcelable
 import kotlin.coroutines.resumeWithException
 import kotlinx.coroutines.suspendCancellableCoroutine
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ClientProxyTypeGenerator.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ClientProxyTypeGenerator.kt
index ec4f236..04c8db3 100644
--- a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ClientProxyTypeGenerator.kt
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ClientProxyTypeGenerator.kt
@@ -107,6 +107,7 @@
 
     private fun toSuspendFunSpec(method: Method) =
         FunSpec.builder(method.name).build {
+            addModifiers(KModifier.PUBLIC)
             addModifiers(KModifier.OVERRIDE)
             addModifiers(KModifier.SUSPEND)
             addParameters(method.parameters.map { it.poetSpec() })
@@ -128,17 +129,18 @@
 
     private fun toNonSuspendFunSpec(method: Method) =
         FunSpec.builder(method.name).build {
-            addModifiers(KModifier.OVERRIDE)
+            addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE)
             addParameters(method.parameters.map { it.poetSpec() })
 
             addCode(generateRemoteCall(method))
         }
 
     private fun generateOpenSession() = FunSpec.builder("openSession").build {
-        addModifiers(KModifier.OVERRIDE)
+        addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE)
         addParameters(
             listOf(
                 ParameterSpec(contextPropertyName, contextClass),
+                ParameterSpec("windowInputToken", ClassName("android.os", "IBinder")),
                 ParameterSpec("initialWidth", Types.int.poetClassName()),
                 ParameterSpec("initialHeight", Types.int.poetClassName()),
                 ParameterSpec("isZOrderOnTop", Types.boolean.poetClassName()),
@@ -151,8 +153,8 @@
             )
         )
         addStatement(
-            "$sandboxedUiAdapterPropertyName.openSession(%N, initialWidth, initialHeight, " +
-                "isZOrderOnTop, clientExecutor, client)",
+            "$sandboxedUiAdapterPropertyName.openSession(%N, windowInputToken, initialWidth, " +
+                "initialHeight, isZOrderOnTop, clientExecutor, client)",
             contextPropertyName,
         )
     }
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/PrivacySandboxCancellationExceptionFileGenerator.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/PrivacySandboxCancellationExceptionFileGenerator.kt
index dadcb7d5..a53dbe9 100644
--- a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/PrivacySandboxCancellationExceptionFileGenerator.kt
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/PrivacySandboxCancellationExceptionFileGenerator.kt
@@ -36,11 +36,11 @@
                     PropertySpec.builder(
                         "message",
                         String::class.asTypeName().copy(nullable = true),
-                    ).addModifiers(KModifier.OVERRIDE).build(),
+                    ).addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE).build(),
                     PropertySpec.builder(
                         "cause",
                         Throwable::class.asTypeName().copy(nullable = true),
-                    ).addModifiers(KModifier.OVERRIDE).build()
+                    ).addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE).build()
                 )
             )
         }
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/PrivacySandboxExceptionFileGenerator.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/PrivacySandboxExceptionFileGenerator.kt
index fdb9242..c93a34f 100644
--- a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/PrivacySandboxExceptionFileGenerator.kt
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/PrivacySandboxExceptionFileGenerator.kt
@@ -36,11 +36,11 @@
                     PropertySpec.builder(
                         "message",
                         String::class.asTypeName().copy(nullable = true),
-                    ).addModifiers(KModifier.OVERRIDE).build(),
+                    ).addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE).build(),
                     PropertySpec.builder(
                         "cause",
                         Throwable::class.asTypeName().copy(nullable = true),
-                    ).addModifiers(KModifier.OVERRIDE).build()
+                    ).addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE).build()
                 )
             )
         }
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/StubDelegatesGenerator.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/StubDelegatesGenerator.kt
index 85f2dfa..7082174 100644
--- a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/StubDelegatesGenerator.kt
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/StubDelegatesGenerator.kt
@@ -102,7 +102,7 @@
 
     private fun toSuspendFunSpec(method: Method): FunSpec {
         return FunSpec.builder(method.name).build {
-            addModifiers(KModifier.OVERRIDE)
+            addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE)
             addParameters(getParameters(method))
             addCode {
                 addControlFlow(
@@ -145,7 +145,7 @@
     }
 
     private fun toNonSuspendFunSpec(method: Method) = FunSpec.builder(method.name).build {
-        addModifiers(KModifier.OVERRIDE)
+        addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE)
         addParameters(getParameters(method))
         addCode(CodeBlock.builder().build {
             addControlFlow("%L.%M", coroutineScopePropertyName, launchMethod) {
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/TransportCancellationGenerator.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/TransportCancellationGenerator.kt
index 8920889..cf8251b 100644
--- a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/TransportCancellationGenerator.kt
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/TransportCancellationGenerator.kt
@@ -54,7 +54,7 @@
                 ).initializer("%T(false)", atomicBooleanClass).build()
             )
             addFunction(FunSpec.builder("cancel").build {
-                addModifiers(KModifier.OVERRIDE)
+                addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE)
                 addCode {
                     addControlFlow("if (hasCancelled.compareAndSet(false, true))") {
                         addStatement("onCancel()")
diff --git a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/MainActivity.kt b/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/MainActivity.kt
index b719f23..abc5297 100644
--- a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/MainActivity.kt
+++ b/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/MainActivity.kt
@@ -81,7 +81,7 @@
 
         mSandboxedSdkView2 = SandboxedSdkView(this@MainActivity)
         mSandboxedSdkView2.addStateChangedListener(StateChangeListener(mSandboxedSdkView2))
-        mSandboxedSdkView2.layoutParams = ViewGroup.LayoutParams(200, 200)
+        mSandboxedSdkView2.layoutParams = ViewGroup.LayoutParams(400, 400)
         runOnUiThread {
             findViewById<LinearLayout>(R.id.ad_layout).addView(mSandboxedSdkView2)
         }
diff --git a/privacysandbox/ui/integration-tests/testapp/src/main/res/layout/activity_main.xml b/privacysandbox/ui/integration-tests/testapp/src/main/res/layout/activity_main.xml
index f071c2ae..dda74f2 100644
--- a/privacysandbox/ui/integration-tests/testapp/src/main/res/layout/activity_main.xml
+++ b/privacysandbox/ui/integration-tests/testapp/src/main/res/layout/activity_main.xml
@@ -22,55 +22,62 @@
     android:layout_height="match_parent"
     tools:context=".MainActivity">
 
-    <LinearLayout
-        android:id="@+id/ad_layout"
+    <ScrollView
+        android:id="@+id/scroll_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:orientation="vertical">
-
-        <TextView
-            android:id="@+id/headerTextView"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="16dp"
-            android:layout_marginStart="16dp"
-            android:layout_marginEnd="16dp"
-            android:text="@string/app_name"/>
-
-        <androidx.privacysandbox.ui.client.view.SandboxedSdkView
-            android:id="@+id/rendered_view"
-            android:background="#FF0000"
-            android:layout_width="match_parent"
-            android:layout_marginStart="16dp"
-            android:layout_marginEnd="16dp"
-            android:layout_marginBottom="16dp"
-            android:layout_marginTop="16dp"
-            android:layout_height="400dp" />
-
         <LinearLayout
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal">
-            <androidx.privacysandbox.ui.client.view.SandboxedSdkView
-                android:id="@+id/new_ad_view"
-                android:layout_width="150dp"
-                android:layout_height="150dp"
-                android:layout_marginBottom="16dp"
-                android:layout_marginEnd="16dp"
-                android:layout_marginStart="16dp"
-                android:layout_marginTop="16dp"
-                android:background="#FFFF00" />
+            android:id="@+id/ad_layout"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="vertical">
 
-            <Button
-                android:id="@+id/new_ad_button"
+            <TextView
+                android:id="@+id/headerTextView"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:text="new_ad"
-                android:textAllCaps="false" />
-            />
+                android:layout_marginTop="16dp"
+                android:layout_marginStart="16dp"
+                android:layout_marginEnd="16dp"
+                android:text="@string/app_name"/>
+
+            <androidx.privacysandbox.ui.client.view.SandboxedSdkView
+                android:id="@+id/rendered_view"
+                android:background="#FF0000"
+                android:layout_width="match_parent"
+                android:layout_marginStart="16dp"
+                android:layout_marginEnd="16dp"
+                android:layout_marginBottom="16dp"
+                android:layout_marginTop="16dp"
+                android:layout_height="400dp" />
+
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal">
+                <androidx.privacysandbox.ui.client.view.SandboxedSdkView
+                    android:id="@+id/new_ad_view"
+                    android:layout_width="150dp"
+                    android:layout_height="150dp"
+                    android:layout_marginBottom="16dp"
+                    android:layout_marginEnd="16dp"
+                    android:layout_marginStart="16dp"
+                    android:layout_marginTop="16dp"
+                    android:background="#FFFF00" />
+
+                <Button
+                    android:id="@+id/new_ad_button"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="new_ad"
+                    android:textAllCaps="false" />
+                />
+            </LinearLayout>
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/long_text" />
         </LinearLayout>
-
-
-
-    </LinearLayout>
+    </ScrollView>
 </androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/privacysandbox/ui/integration-tests/testapp/src/main/res/values/strings.xml b/privacysandbox/ui/integration-tests/testapp/src/main/res/values/strings.xml
index 960f177..8932ec4 100644
--- a/privacysandbox/ui/integration-tests/testapp/src/main/res/values/strings.xml
+++ b/privacysandbox/ui/integration-tests/testapp/src/main/res/values/strings.xml
@@ -16,4 +16,5 @@
 
 <resources>
     <string name="app_name">PrivacySandboxUiTestApp</string>
+    <string name="long_text">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean sit amet lacus dignissim, sollicitudin nisl sed, egestas leo. Sed congue vitae nulla vel mattis. Sed porttitor lobortis felis id sollicitudin. Maecenas a venenatis mi. Etiam sapien ipsum, bibendum at congue eget, venenatis laoreet erat. Integer dapibus varius lectus, eu gravida arcu pharetra in. Suspendisse volutpat sit amet ex non rutrum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed quis dui eros. Ut sed magna sit amet nulla iaculis ultrices. Pellentesque fermentum, nibh vel fermentum lacinia, urna nibh dictum risus, id feugiat sapien dolor nec erat. Maecenas augue nibh, sodales eu iaculis ut, volutpat non magna. Donec eget laoreet odio.Sed eget purus id mauris euismod lobortis. Vestibulum suscipit hendrerit rhoncus. Etiam et porttitor justo. Vivamus sodales velit in risus convallis tempor vitae non nunc. Integer lacinia consectetur ipsum, sit amet semper mi posuere eget. Etiam non quam nec sem malesuada viverra. Donec sollicitudin quam metus, at suscipit nisi mattis nec. Nam eu vehicula mauris. Cras nibh massa, interdum eget ante ut, molestie efficitur leo. In aliquet sodales mi vel bibendum. In iaculis neque in laoreet vestibulum. Nullam non interdum lectus. Etiam non ante elit. Vivamus luctus nisi ex, quis facilisis dui pellentesque porttitor. Etiam arcu nisl, porta eu hendrerit vel, porttitor vel turpis. Vestibulum in venenatis elit. Nunc in nisl congue, suscipit massa eu, luctus enim. Donec a fermentum magna, nec commodo purus. Quisque ac nisi et mi pretium porta ut eget nibh. Nulla consequat enim a congue porta. Donec odio magna, elementum in felis sit amet, posuere laoreet urna. Suspendisse ultricies in libero nec volutpat. Vivamus at magna lectus. Sed id metus et tellus suscipit aliquam in at lacus. Ut gravida ultrices augue, quis ultrices lacus ullamcorper ac. Ut fringilla ac quam sit amet pharetra. In non ante consectetur, dapibus ante eu, interdum risus. Nam lobortis blandit nisl ac dapibus. Maecenas vitae est ac odio sollicitudin varius eget quis orci. Mauris vitae ex eget neque tempor faucibus eget vel orci. Morbi eu feugiat lorem. Donec id sem et magna ullamcorper congue. Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed laoreet ultrices quam, quis eleifend libero malesuada id. Sed ac sollicitudin diam. Integer sit amet ex ac purus malesuada iaculis at in mauris. Vestibulum egestas velit et sapien volutpat, vel varius augue fringilla. Duis efficitur blandit arcu in suscipit. Maecenas neque purus, finibus vel rhoncus at, pretium ut ipsum.</string>
 </resources>
\ No newline at end of file
diff --git a/privacysandbox/ui/integration-tests/testsdkprovider/src/main/java/androidx/privacysandbox/ui/integration/testsdkprovider/SdkApi.kt b/privacysandbox/ui/integration-tests/testsdkprovider/src/main/java/androidx/privacysandbox/ui/integration/testsdkprovider/SdkApi.kt
index c855ee2..4ceebcb 100644
--- a/privacysandbox/ui/integration-tests/testsdkprovider/src/main/java/androidx/privacysandbox/ui/integration/testsdkprovider/SdkApi.kt
+++ b/privacysandbox/ui/integration-tests/testsdkprovider/src/main/java/androidx/privacysandbox/ui/integration/testsdkprovider/SdkApi.kt
@@ -24,6 +24,7 @@
 import android.graphics.Paint
 import android.net.Uri
 import android.os.Bundle
+import android.os.IBinder
 import android.provider.Settings
 import android.util.Log
 import android.view.View
@@ -54,11 +55,12 @@
         SandboxedUiAdapter {
         override fun openSession(
             context: Context,
+            windowInputToken: IBinder,
             initialWidth: Int,
             initialHeight: Int,
             isZOrderOnTop: Boolean,
             clientExecutor: Executor,
-            client: SandboxedUiAdapter.SessionClient
+            client: SandboxedUiAdapter.SessionClient,
         ) {
             Log.d(TAG, "Session requested")
             lateinit var adView: View
@@ -90,7 +92,8 @@
 
             override fun notifyResized(width: Int, height: Int) {
                 Log.i(TAG, "Resized $width $height")
-                view.layoutParams = ViewGroup.LayoutParams(width, height)
+                view.layoutParams.width = width
+                view.layoutParams.height = height
             }
 
             override fun notifyZOrderChanged(isZOrderOnTop: Boolean) {
diff --git a/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/SandboxedSdkViewTest.kt b/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/SandboxedSdkViewTest.kt
index 9670896..f545175 100644
--- a/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/SandboxedSdkViewTest.kt
+++ b/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/SandboxedSdkViewTest.kt
@@ -21,6 +21,8 @@
 import android.content.pm.ActivityInfo
 import android.content.res.Configuration
 import android.os.Build
+import android.os.IBinder
+import android.view.SurfaceView
 import android.view.View
 import android.view.ViewGroup
 import android.view.ViewTreeObserver
@@ -74,6 +76,7 @@
     class FailingTestSandboxedUiAdapter : SandboxedUiAdapter {
         override fun openSession(
             context: Context,
+            windowInputToken: IBinder,
             initialWidth: Int,
             initialHeight: Int,
             isZOrderOnTop: Boolean,
@@ -96,6 +99,7 @@
         var internalClient: SandboxedUiAdapter.SessionClient? = null
         var testSession: TestSession? = null
         var isZOrderOnTop = true
+        var inputToken: IBinder? = null
 
         // When set to true, the onSessionOpened callback will only be invoked when specified
         // by the test. This is to test race conditions when the session is being loaded.
@@ -103,6 +107,7 @@
 
         override fun openSession(
             context: Context,
+            windowInputToken: IBinder,
             initialWidth: Int,
             initialHeight: Int,
             isZOrderOnTop: Boolean,
@@ -117,6 +122,7 @@
             }
             isSessionOpened = true
             this.isZOrderOnTop = isZOrderOnTop
+            this.inputToken = windowInputToken
             openSessionLatch?.countDown()
         }
 
@@ -248,7 +254,6 @@
     @Test
     fun childViewRemovedOnErrorTest() {
         assertTrue(view.childCount == 0)
-
         addViewToLayout()
 
         openSessionLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)
@@ -404,6 +409,49 @@
         )
     }
 
+    /**
+     * Ensures that the input token passed when opening a session is non-null and is the same host
+     * token as another [SurfaceView] in the same activity.
+     */
+    @Test
+    fun inputTokenIsCorrect() {
+        lateinit var layout: LinearLayout
+        val surfaceView = SurfaceView(context)
+        val surfaceViewLatch = CountDownLatch(1)
+
+        // Attach SurfaceView
+        activity.runOnUiThread {
+            layout = activity.findViewById(
+                R.id.mainlayout
+            )
+            layout.addView(surfaceView)
+        }
+        var token: IBinder? = null
+        surfaceView.addOnAttachStateChangeListener(
+            object : View.OnAttachStateChangeListener {
+                override fun onViewAttachedToWindow(p0: View) {
+                    token = surfaceView.hostToken
+                    surfaceViewLatch.countDown()
+                }
+
+                override fun onViewDetachedFromWindow(p0: View) {
+                }
+            }
+        )
+
+        // Verify SurfaceView has a non-null token when attached.
+        assertThat(surfaceViewLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)).isTrue()
+        assertThat(surfaceView.hostToken).isNotNull()
+        activity.runOnUiThread {
+            layout.removeView(surfaceView)
+        }
+
+        // Verify that the UI adapter receives the same host token object when opening a session.
+        addViewToLayout()
+        assertThat(openSessionLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)).isTrue()
+        assertThat(testSandboxedUiAdapter.inputToken).isEqualTo(token)
+    }
+
     private fun addViewToLayout() {
         activity.runOnUiThread {
             activity.findViewById<LinearLayout>(
diff --git a/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/SandboxedUiAdapterFactory.kt b/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/SandboxedUiAdapterFactory.kt
index f4d3d5c..de5b048 100644
--- a/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/SandboxedUiAdapterFactory.kt
+++ b/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/SandboxedUiAdapterFactory.kt
@@ -19,9 +19,9 @@
 import android.content.Context
 import android.content.res.Configuration
 import android.hardware.display.DisplayManager
-import android.os.Binder
 import android.os.Build
 import android.os.Bundle
+import android.os.IBinder
 import android.view.Display
 import android.view.SurfaceControlViewHost
 import android.view.SurfaceView
@@ -62,6 +62,7 @@
 
         override fun openSession(
             context: Context,
+            windowInputToken: IBinder,
             initialWidth: Int,
             initialHeight: Int,
             isZOrderOnTop: Boolean,
@@ -73,7 +74,7 @@
             val displayId = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY).displayId
 
             adapterInterface.openRemoteSession(
-                Binder(), // Host Token
+                windowInputToken,
                 displayId,
                 initialWidth,
                 initialHeight,
diff --git a/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/view/SandboxedSdkView.kt b/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/view/SandboxedSdkView.kt
index fdd5e4a..0b6b31c 100644
--- a/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/view/SandboxedSdkView.kt
+++ b/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/view/SandboxedSdkView.kt
@@ -19,7 +19,9 @@
 import android.content.Context
 import android.content.res.Configuration
 import android.os.Build
+import android.os.IBinder
 import android.util.AttributeSet
+import android.view.SurfaceView
 import android.view.View
 import android.view.ViewGroup
 import androidx.annotation.RequiresApi
@@ -85,10 +87,13 @@
 
 // TODO(b/268014171): Remove API requirements once S- support is added
 @RequiresApi(Build.VERSION_CODES.TIRAMISU)
-class SandboxedSdkView @JvmOverloads constructor(
-    context: Context,
-    attrs: AttributeSet? = null
-) : ViewGroup(context, attrs) {
+class SandboxedSdkView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
+    ViewGroup(context, attrs) {
+
+    // TODO(b/284147223): Remove this logic in V+
+    private val surfaceView = SurfaceView(context).apply {
+        visibility = GONE
+    }
 
     private var adapter: SandboxedUiAdapter? = null
     private var client: Client? = null
@@ -97,6 +102,7 @@
     private var requestedWidth = -1
     private var requestedHeight = -1
     private var isTransitionGroupSet = false
+    private var windowInputToken: IBinder? = null
     internal val stateListenerManager: StateListenerManager = StateListenerManager()
 
     /**
@@ -136,11 +142,13 @@
 
     private fun checkClientOpenSession() {
         val adapter = adapter
-        if (client == null && adapter != null && isAttachedToWindow && width > 0 && height > 0) {
+        if (client == null && adapter != null && windowInputToken != null &&
+            width > 0 && height > 0) {
             stateListenerManager.currentUiSessionState = SandboxedSdkUiSessionState.Loading
             client = Client(this)
             adapter.openSession(
                 context,
+                windowInputToken!!,
                 width,
                 height,
                 isZOrderOnTop,
@@ -150,6 +158,33 @@
         }
     }
 
+    /**
+     * Attaches a temporary [SurfaceView] to the view hierarchy. This [SurfaceView] will be removed
+     * once it has been attached to the window and its host token is non-null.
+     *
+     * TODO(b/284147223): Remove this logic in V+
+     */
+    private fun attachTemporarySurfaceView() {
+        val onSurfaceViewAttachedListener =
+            object : OnAttachStateChangeListener {
+                override fun onViewAttachedToWindow(view: View) {
+                    view.removeOnAttachStateChangeListener(this)
+                    removeSurfaceViewAndOpenSession()
+                }
+
+                override fun onViewDetachedFromWindow(view: View) {
+                }
+            }
+        surfaceView.addOnAttachStateChangeListener(onSurfaceViewAttachedListener)
+        super.addView(surfaceView, 0, generateDefaultLayoutParams())
+    }
+
+    internal fun removeSurfaceViewAndOpenSession() {
+        windowInputToken = surfaceView.hostToken
+        super.removeView(surfaceView)
+        checkClientOpenSession()
+    }
+
     internal fun requestSize(width: Int, height: Int) {
         if (width == this.width && height == this.height) return
         requestedWidth = width
@@ -157,7 +192,7 @@
         requestLayout()
     }
 
-    internal fun removeContentView() {
+    private fun removeContentView() {
         if (childCount == 1) {
             super.removeViewAt(0)
         }
@@ -243,12 +278,13 @@
 
     override fun onAttachedToWindow() {
         super.onAttachedToWindow()
-        checkClientOpenSession()
+        attachTemporarySurfaceView()
     }
 
     override fun onDetachedFromWindow() {
         client?.close()
         client = null
+        windowInputToken = null
         super.onDetachedFromWindow()
     }
 
diff --git a/privacysandbox/ui/ui-core/api/current.txt b/privacysandbox/ui/ui-core/api/current.txt
index db32e1e..7aef7ba 100644
--- a/privacysandbox/ui/ui-core/api/current.txt
+++ b/privacysandbox/ui/ui-core/api/current.txt
@@ -2,7 +2,7 @@
 package androidx.privacysandbox.ui.core {
 
   public interface SandboxedUiAdapter {
-    method public void openSession(android.content.Context context, int initialWidth, int initialHeight, boolean isZOrderOnTop, java.util.concurrent.Executor clientExecutor, androidx.privacysandbox.ui.core.SandboxedUiAdapter.SessionClient client);
+    method public void openSession(android.content.Context context, android.os.IBinder windowInputToken, int initialWidth, int initialHeight, boolean isZOrderOnTop, java.util.concurrent.Executor clientExecutor, androidx.privacysandbox.ui.core.SandboxedUiAdapter.SessionClient client);
   }
 
   public static interface SandboxedUiAdapter.Session {
diff --git a/privacysandbox/ui/ui-core/api/restricted_current.txt b/privacysandbox/ui/ui-core/api/restricted_current.txt
index db32e1e..7aef7ba 100644
--- a/privacysandbox/ui/ui-core/api/restricted_current.txt
+++ b/privacysandbox/ui/ui-core/api/restricted_current.txt
@@ -2,7 +2,7 @@
 package androidx.privacysandbox.ui.core {
 
   public interface SandboxedUiAdapter {
-    method public void openSession(android.content.Context context, int initialWidth, int initialHeight, boolean isZOrderOnTop, java.util.concurrent.Executor clientExecutor, androidx.privacysandbox.ui.core.SandboxedUiAdapter.SessionClient client);
+    method public void openSession(android.content.Context context, android.os.IBinder windowInputToken, int initialWidth, int initialHeight, boolean isZOrderOnTop, java.util.concurrent.Executor clientExecutor, androidx.privacysandbox.ui.core.SandboxedUiAdapter.SessionClient client);
   }
 
   public static interface SandboxedUiAdapter.Session {
diff --git a/privacysandbox/ui/ui-core/src/main/java/androidx/privacysandbox/ui/core/SandboxedUiAdapter.kt b/privacysandbox/ui/ui-core/src/main/java/androidx/privacysandbox/ui/core/SandboxedUiAdapter.kt
index 2ebe9a8..1a724ed 100644
--- a/privacysandbox/ui/ui-core/src/main/java/androidx/privacysandbox/ui/core/SandboxedUiAdapter.kt
+++ b/privacysandbox/ui/ui-core/src/main/java/androidx/privacysandbox/ui/core/SandboxedUiAdapter.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.content.res.Configuration
+import android.os.IBinder
 import android.view.View
 import java.util.concurrent.Executor
 
@@ -36,6 +37,7 @@
      */
     fun openSession(
         context: Context,
+        windowInputToken: IBinder,
         initialWidth: Int,
         initialHeight: Int,
         isZOrderOnTop: Boolean,
diff --git a/privacysandbox/ui/ui-provider/build.gradle b/privacysandbox/ui/ui-provider/build.gradle
index 68339e0..6467c1a7 100644
--- a/privacysandbox/ui/ui-provider/build.gradle
+++ b/privacysandbox/ui/ui-provider/build.gradle
@@ -39,6 +39,9 @@
     androidTestImplementation(libs.testRunner)
     androidTestImplementation(libs.testRules)
     androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.espressoCore)
+    androidTestImplementation(libs.testUiautomator)
+    androidTestImplementation(project(":internal-testutils-runtime"))
 }
 
 android {
diff --git a/privacysandbox/ui/ui-provider/src/androidTest/AndroidManifest.xml b/privacysandbox/ui/ui-provider/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..7e82fae
--- /dev/null
+++ b/privacysandbox/ui/ui-provider/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+    <!-- This override is okay because the associated tests only run on U+ -->
+    <uses-sdk tools:overrideLibrary="android_libs.ub_uiautomator, androidx.test.uiautomator" />
+    <application>
+        <activity
+            android:name="androidx.privacysandbox.ui.provider.test.MainActivity" />
+    </application>
+</manifest>
diff --git a/privacysandbox/ui/ui-provider/src/androidTest/kotlin/androidx/privacysandbox/ui/provider/test/BinderAdapterDelegateTest.kt b/privacysandbox/ui/ui-provider/src/androidTest/kotlin/androidx/privacysandbox/ui/provider/test/BinderAdapterDelegateTest.kt
new file mode 100644
index 0000000..f2bb448
--- /dev/null
+++ b/privacysandbox/ui/ui-provider/src/androidTest/kotlin/androidx/privacysandbox/ui/provider/test/BinderAdapterDelegateTest.kt
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.ui.provider.test
+
+import android.content.Context
+import android.os.Binder
+import android.os.Build
+import android.os.IBinder
+import android.view.Display
+import android.view.MotionEvent
+import android.view.SurfaceControlViewHost
+import android.view.SurfaceView
+import android.view.View
+import androidx.annotation.RequiresApi
+import androidx.privacysandbox.ui.provider.TouchFocusTransferringView
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.action.ViewActions.click
+import androidx.test.espresso.action.ViewActions.slowSwipeLeft
+import androidx.test.espresso.action.ViewActions.swipeLeft
+import androidx.test.espresso.action.ViewActions.swipeUp
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.UiScrollable
+import androidx.test.uiautomator.UiSelector
+import androidx.testutils.withActivity
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+@RunWith(AndroidJUnit4::class)
+@LargeTest
+class BinderAdapterDelegateTest {
+    companion object {
+        const val TIMEOUT_MILLIS: Long = 2000
+        const val WIDTH = 500
+        const val HEIGHT = 500
+        const val SURFACE_VIEW_RES = "androidx.privacysandbox.ui.provider.test:id/surface_view"
+    }
+
+    @get:Rule
+    val activityScenarioRule = ActivityScenarioRule(MainActivity::class.java)
+
+    private val transferTouchFocusLatch = CountDownLatch(1)
+
+    @Before
+    fun setUp() {
+        Assume.assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+        val context = InstrumentationRegistry.getInstrumentation().targetContext
+        val activity = activityScenarioRule.withActivity { this }
+        UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+        activity.runOnUiThread {
+            val surfaceView = activity.findViewById<SurfaceView>(R.id.surface_view)
+            val surfaceControlViewHost =
+                GestureTransferringSurfaceControlViewHost(
+                    activity, activity.display!!, Binder(), transferTouchFocusLatch)
+            val touchFocusTransferringView =
+                TouchFocusTransferringView(context, surfaceControlViewHost)
+            touchFocusTransferringView.addView(TestView(context))
+            surfaceControlViewHost.setView(touchFocusTransferringView, WIDTH, HEIGHT)
+            surfaceView.setChildSurfacePackage(surfaceControlViewHost.surfacePackage!!)
+            surfaceView.setZOrderOnTop(true)
+        }
+    }
+
+    @Test
+    fun touchFocusTransferredForSwipeUp() {
+        onView(withId(R.id.surface_view)).perform(swipeUp())
+        assertThat(transferTouchFocusLatch.await(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue()
+    }
+
+    @Test
+    fun touchFocusTransferredForSwipeLeft() {
+        onView(withId(R.id.surface_view)).perform(swipeLeft())
+        assertThat(transferTouchFocusLatch.await(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue()
+    }
+
+    @Test
+    fun touchFocusTransferredForSlowSwipeLeft() {
+        onView(withId(R.id.surface_view)).perform(slowSwipeLeft())
+        assertThat(transferTouchFocusLatch.await(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue()
+    }
+
+    @Test
+    fun touchFocusNotTransferredForClicks() {
+        onView(withId(R.id.surface_view)).perform(click())
+        assertThat(transferTouchFocusLatch.await(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isFalse()
+    }
+
+    @Test
+    fun touchFocusTransferredForFlingForward() {
+        val scrollView = UiScrollable(UiSelector().resourceId(SURFACE_VIEW_RES))
+        scrollView.flingForward()
+        assertThat(transferTouchFocusLatch.await(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue()
+    }
+
+    @Test
+    fun touchFocusTransferredForFlingBackward() {
+        val scrollView = UiScrollable(UiSelector().resourceId(SURFACE_VIEW_RES))
+        scrollView.flingBackward()
+        assertThat(transferTouchFocusLatch.await(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue()
+    }
+
+    /**
+     * SCVH that takes note of when touch focus is transferred.
+     *
+     * TODO(b/290629538): Add full integration test.
+     */
+    private class GestureTransferringSurfaceControlViewHost(
+        context: Context,
+        display: Display,
+        hostToken: IBinder,
+        countDownLatch: CountDownLatch
+    ) : SurfaceControlViewHost(context, display, hostToken) {
+
+        val latch = countDownLatch
+
+        override fun transferTouchGestureToHost(): Boolean {
+            latch.countDown()
+            return true
+        }
+    }
+
+    private class TestView(context: Context) : View(context) {
+        override fun onTouchEvent(event: MotionEvent?): Boolean {
+            return true
+        }
+    }
+}
diff --git a/privacysandbox/ui/ui-provider/src/androidTest/kotlin/androidx/privacysandbox/ui/provider/test/MainActivity.kt b/privacysandbox/ui/ui-provider/src/androidTest/kotlin/androidx/privacysandbox/ui/provider/test/MainActivity.kt
new file mode 100644
index 0000000..3f07823
--- /dev/null
+++ b/privacysandbox/ui/ui-provider/src/androidTest/kotlin/androidx/privacysandbox/ui/provider/test/MainActivity.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.ui.provider.test
+
+import android.app.Activity
+import android.os.Bundle
+
+// TODO(b/290824219): Consolidate test activities.
+class MainActivity : Activity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_main)
+    }
+}
diff --git a/privacysandbox/ui/ui-provider/src/androidTest/res/layout/activity_main.xml b/privacysandbox/ui/ui-provider/src/androidTest/res/layout/activity_main.xml
new file mode 100644
index 0000000..74929d1
--- /dev/null
+++ b/privacysandbox/ui/ui-provider/src/androidTest/res/layout/activity_main.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:id="@+id/main_layout">
+    <SurfaceView
+        android:layout_width="500px"
+        android:layout_height="500px"
+        android:id="@+id/surface_view"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/privacysandbox/ui/ui-provider/src/main/java/androidx/privacysandbox/ui/provider/BinderAdapterDelegate.kt b/privacysandbox/ui/ui-provider/src/main/java/androidx/privacysandbox/ui/provider/BinderAdapterDelegate.kt
index 9163257..5bd2008 100644
--- a/privacysandbox/ui/ui-provider/src/main/java/androidx/privacysandbox/ui/provider/BinderAdapterDelegate.kt
+++ b/privacysandbox/ui/ui-provider/src/main/java/androidx/privacysandbox/ui/provider/BinderAdapterDelegate.kt
@@ -58,6 +58,7 @@
 
     fun openSession(
         context: Context,
+        windowInputToken: IBinder,
         initialWidth: Int,
         initialHeight: Int,
         isZOrderOnTop: Boolean,
@@ -65,13 +66,13 @@
         client: SandboxedUiAdapter.SessionClient
     ) {
         adapter.openSession(
-            context, initialWidth, initialHeight, isZOrderOnTop, clientExecutor,
+            context, windowInputToken, initialWidth, initialHeight, isZOrderOnTop, clientExecutor,
             client
         )
     }
 
     override fun openRemoteSession(
-        hostToken: IBinder,
+        windowInputToken: IBinder,
         displayId: Int,
         initialWidth: Int,
         initialHeight: Int,
@@ -87,13 +88,13 @@
                     sandboxContext.createDisplayContext(mDisplayManager.getDisplay(displayId))
                 val surfaceControlViewHost = SurfaceControlViewHost(
                     windowContext,
-                    mDisplayManager.getDisplay(displayId), hostToken
+                    mDisplayManager.getDisplay(displayId), windowInputToken
                 )
                 val sessionClient = SessionClientProxy(
                     surfaceControlViewHost, initialWidth, initialHeight, remoteSessionClient
                 )
                 openSession(
-                    windowContext, initialWidth, initialHeight, isZOrderOnTop,
+                    windowContext, windowInputToken, initialWidth, initialHeight, isZOrderOnTop,
                     Runnable::run, sessionClient
                 )
             } catch (exception: Throwable) {
@@ -111,7 +112,15 @@
 
         override fun onSessionOpened(session: SandboxedUiAdapter.Session) {
             val view = session.view
-            surfaceControlViewHost.setView(view, initialWidth, initialHeight)
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+                val touchTransferringView = TouchFocusTransferringView(
+                    sandboxContext, surfaceControlViewHost)
+                touchTransferringView.addView(view)
+                surfaceControlViewHost.setView(touchTransferringView, initialWidth, initialHeight)
+            } else {
+                surfaceControlViewHost.setView(view, initialWidth, initialHeight)
+            }
+
             val surfacePackage = surfaceControlViewHost.surfacePackage
             val remoteSessionController =
                 RemoteSessionController(surfaceControlViewHost, session)
diff --git a/privacysandbox/ui/ui-provider/src/main/java/androidx/privacysandbox/ui/provider/TouchFocusTransferringView.kt b/privacysandbox/ui/ui-provider/src/main/java/androidx/privacysandbox/ui/provider/TouchFocusTransferringView.kt
new file mode 100644
index 0000000..1ef330a
--- /dev/null
+++ b/privacysandbox/ui/ui-provider/src/main/java/androidx/privacysandbox/ui/provider/TouchFocusTransferringView.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.ui.provider
+
+import android.content.Context
+import android.os.Build
+import android.view.GestureDetector
+import android.view.MotionEvent
+import android.view.SurfaceControlViewHost
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.annotation.RequiresApi
+import androidx.core.view.GestureDetectorCompat
+
+/**
+ * A container [ViewGroup] that delegates touch events to the host or the UI provider.
+ *
+ * Touch events will first be passed to a scroll detector. If a scroll or fling
+ * is detected, the gesture will be transferred to the host. Otherwise, the touch event will pass
+ * through and be handled by the provider of UI.
+ *
+ * TODO(b/286829818): Pass scroll events to the UI provider if it can handle scrolls.
+ */
+@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+internal class TouchFocusTransferringView(
+    context: Context,
+    surfaceControlViewHost: SurfaceControlViewHost
+) : FrameLayout(context) {
+
+    private val scvh: SurfaceControlViewHost = surfaceControlViewHost
+    private val detector = ScrollDetector(context)
+
+    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
+        detector.onTouchEvent(ev)
+        if (!detector.isScrolling) {
+            return false
+        }
+        detector.reset()
+        return scvh.transferTouchGestureToHost()
+    }
+
+    override fun onTouchEvent(event: MotionEvent): Boolean {
+        // This will only be called if the dispatch of the touch event is intercepted.
+        return true
+    }
+
+    /**
+     * Handles intercepted touch events before they reach the UI provider.
+     *
+     * If a scroll or fling event is caught, this is indicated by the [isScrolling] var.
+     */
+    private class ScrollDetector(context: Context) : GestureDetector.SimpleOnGestureListener() {
+
+        var isScrolling = false
+          private set
+
+        private val gestureDetector: GestureDetectorCompat = GestureDetectorCompat(context, this)
+
+        override fun onScroll(e1: MotionEvent?, e2: MotionEvent, dX: Float, dY: Float): Boolean {
+            isScrolling = true
+            return false
+        }
+
+        override fun onFling(
+            e1: MotionEvent?,
+            e2: MotionEvent,
+            velocityX: Float,
+            velocityY: Float
+        ): Boolean {
+            isScrolling = true
+            return false
+        }
+
+        fun onTouchEvent(ev: MotionEvent) {
+            gestureDetector.onTouchEvent(ev)
+        }
+
+        fun reset() {
+            isScrolling = false
+        }
+    }
+}
diff --git a/privacysandbox/ui/ui-tests/src/androidTest/java/androidx/privacysandbox/ui/tests/endtoend/IntegrationTests.kt b/privacysandbox/ui/ui-tests/src/androidTest/java/androidx/privacysandbox/ui/tests/endtoend/IntegrationTests.kt
index a2ea2da..ae63942 100644
--- a/privacysandbox/ui/ui-tests/src/androidTest/java/androidx/privacysandbox/ui/tests/endtoend/IntegrationTests.kt
+++ b/privacysandbox/ui/ui-tests/src/androidTest/java/androidx/privacysandbox/ui/tests/endtoend/IntegrationTests.kt
@@ -19,7 +19,9 @@
 import android.content.Context
 import android.content.pm.ActivityInfo
 import android.content.res.Configuration
+import android.os.Binder
 import android.os.Build
+import android.os.IBinder
 import android.view.View
 import android.view.View.OnLayoutChangeListener
 import android.view.ViewGroup
@@ -168,6 +170,7 @@
 
         adapterFromCoreLibInfo.openSession(
             context,
+            Binder(),
             10 /* initialWidth */,
             10 /* initialHeight */,
             true,
@@ -242,6 +245,7 @@
 
         override fun openSession(
             context: Context,
+            windowInputToken: IBinder,
             initialWidth: Int,
             initialHeight: Int,
             isZOrderOnTop: Boolean,
diff --git a/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/DeviceProfileWriter.java b/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/DeviceProfileWriter.java
index b652534..69702a7 100644
--- a/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/DeviceProfileWriter.java
+++ b/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/DeviceProfileWriter.java
@@ -357,9 +357,13 @@
             FileChannel channel = os.getChannel();
             // Acquire a lock to avoid racing with the Android Runtime
             // when saving the contents of the profile.
+
+            // The documentation suggests that these locks are VM wide, however, the underlying
+            // implementation in libcore (https://cs.android.com/android/platform/superproject/+/main:libcore/ojluni/src/main/native/FileDispatcherImpl.c;l=217;drc=e9cc931d70205df4e7dcc601729707bc7367c081)
+            // ensures that this is OS wide.
             FileLock lock = channel.tryLock()
         ) {
-            Encoding.writeAll(bis, os, channel, lock);
+            Encoding.writeAll(bis, os, lock);
             result(ProfileInstaller.RESULT_INSTALL_SUCCESS, null);
             return true;
         } catch (FileNotFoundException e) {
diff --git a/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/Encoding.java b/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/Encoding.java
index bc88bab..24b1814 100644
--- a/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/Encoding.java
+++ b/profileinstaller/profileinstaller/src/main/java/androidx/profileinstaller/Encoding.java
@@ -24,7 +24,6 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.nio.channels.FileChannel;
 import java.nio.channels.FileLock;
 import java.nio.charset.StandardCharsets;
 import java.util.zip.DataFormatException;
@@ -188,18 +187,12 @@
 
     static void writeAll(@NonNull InputStream is,
             @NonNull OutputStream os,
-            @NonNull FileChannel fileChannel,
             @Nullable FileLock lock) throws IOException {
 
         boolean isValid = lock != null && lock.isValid();
         if (!isValid) {
             throw new IOException("Unable to acquire a lock on the underlying file channel.");
         }
-        if (fileChannel.position() > 0) {
-            // Only write to the cur profile, when it is empty.
-            throw new IOException("Unable to write profile, given one already exists.");
-        }
-
         byte[] buf = new byte[512];
         int length;
         while ((length = is.read(buf)) > 0) {
diff --git a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/DifferentialMotionFlingHelperTest.java b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/DifferentialMotionFlingHelperTest.java
new file mode 100644
index 0000000..6115b50
--- /dev/null
+++ b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/DifferentialMotionFlingHelperTest.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.recyclerview.widget;
+
+import static org.junit.Assert.assertEquals;
+
+import android.view.InputDevice;
+import android.view.MotionEvent;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DifferentialMotionFlingHelperTest {
+    private int mMinVelocity = 0;
+    private int mMaxVelocity = Integer.MAX_VALUE;
+    /** A fake velocity value that's going to be returned from the velocity provider. */
+    private float mVelocity;
+
+    private final DifferentialMotionFlingHelper.DifferentialVelocityProvider mVelocityProvider =
+            (vt, event, axis) -> mVelocity;
+
+    private final DifferentialMotionFlingHelper.FlingVelocityThresholdCalculator
+            mVelocityThresholdCalculator =
+                    (ctx, buffer, event, axis) -> {
+                        buffer[0] = mMinVelocity;
+                        buffer[1] = mMaxVelocity;
+                    };
+
+    private final TestDifferentialMotionFlingTarget mFlingTarget =
+            new TestDifferentialMotionFlingTarget();
+
+    private DifferentialMotionFlingHelper mFlingHelper;
+
+    @Before
+    public void setUp() throws Exception {
+        mFlingHelper = new DifferentialMotionFlingHelper(
+                ApplicationProvider.getApplicationContext(),
+                mFlingTarget,
+                mVelocityThresholdCalculator,
+                mVelocityProvider);
+    }
+
+    @Test
+    public void flingVelocityOppositeToPrevious_stopsOngoingFling() {
+        deliverEventWithVelocity(createRotaryEncoderEvent(), MotionEvent.AXIS_SCROLL, 50);
+        deliverEventWithVelocity(createRotaryEncoderEvent(), MotionEvent.AXIS_SCROLL, -10);
+
+        // One stop on the initial event, and second stop due to opposite velocities.
+        assertEquals(2, mFlingTarget.mNumStops);
+    }
+
+    @Test
+    public void flingParamsChanged_stopsOngoingFling() {
+        deliverEventWithVelocity(createPointerEvent(), MotionEvent.AXIS_VSCROLL, 50);
+        deliverEventWithVelocity(createRotaryEncoderEvent(), MotionEvent.AXIS_SCROLL, 10);
+
+        // One stop on the initial event, and second stop due to changed axis/source.
+        assertEquals(2, mFlingTarget.mNumStops);
+    }
+
+    @Test
+    public void positiveFlingVelocityTooLow_doesNotGenerateFling() {
+        mMinVelocity = 50;
+        mMaxVelocity = 100;
+        deliverEventWithVelocity(createPointerEvent(), MotionEvent.AXIS_VSCROLL, 20);
+
+        assertEquals(0, mFlingTarget.mLastFlingVelocity, /* delta= */ 0);
+    }
+
+    @Test
+    public void negativeFlingVelocityTooLow_doesNotGenerateFling() {
+        mMinVelocity = 50;
+        mMaxVelocity = 100;
+        deliverEventWithVelocity(createPointerEvent(), MotionEvent.AXIS_VSCROLL, -20);
+
+        assertEquals(0, mFlingTarget.mLastFlingVelocity, /* delta= */ 0);
+    }
+
+    @Test
+    public void positiveFlingVelocityAboveMinimum_generateFlings() {
+        mMinVelocity = 50;
+        mMaxVelocity = 100;
+        deliverEventWithVelocity(createPointerEvent(), MotionEvent.AXIS_VSCROLL, 60);
+
+        assertEquals(60, mFlingTarget.mLastFlingVelocity, /* delta= */ 0);
+    }
+
+    @Test
+    public void negativeFlingVelocityAboveMinimum_generateFlings() {
+        mMinVelocity = 50;
+        mMaxVelocity = 100;
+        deliverEventWithVelocity(createPointerEvent(), MotionEvent.AXIS_VSCROLL, -60);
+
+        assertEquals(-60, mFlingTarget.mLastFlingVelocity, /* delta= */ 0);
+    }
+
+    @Test
+    public void positiveFlingVelocityAboveMaximum_velocityClamped() {
+        mMinVelocity = 50;
+        mMaxVelocity = 100;
+        deliverEventWithVelocity(createPointerEvent(), MotionEvent.AXIS_VSCROLL, 3000);
+
+        assertEquals(100, mFlingTarget.mLastFlingVelocity, /* delta= */ 0);
+    }
+
+    @Test
+    public void negativeFlingVelocityAboveMaximum_velocityClamped() {
+        mMinVelocity = 50;
+        mMaxVelocity = 100;
+        deliverEventWithVelocity(createPointerEvent(), MotionEvent.AXIS_VSCROLL, -3000);
+
+        assertEquals(-100, mFlingTarget.mLastFlingVelocity, /* delta= */ 0);
+    }
+
+    private MotionEvent createRotaryEncoderEvent() {
+        return TouchUtils.createMotionEvent(
+                /* inputDeviceId= */ 3,
+                InputDevice.SOURCE_ROTARY_ENCODER,
+                MotionEvent.AXIS_SCROLL,
+                /* axisValue= */ 10);
+    }
+
+    private MotionEvent createPointerEvent() {
+        return TouchUtils.createMotionEvent(
+                /* inputDeviceId= */ 4,
+                InputDevice.SOURCE_CLASS_POINTER,
+                MotionEvent.AXIS_VSCROLL,
+                /* axisValue= */ 10);
+    }
+
+    private void deliverEventWithVelocity(MotionEvent ev, int axis, float velocity) {
+        mVelocity = velocity;
+        mFlingHelper.onMotionEvent(ev, axis);
+        ev.recycle();
+    }
+}
diff --git a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/LinearLayoutManagerRemoveShownItemsTest.kt b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/LinearLayoutManagerRemoveShownItemsTest.kt
index 3d907a4..68cd698 100644
--- a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/LinearLayoutManagerRemoveShownItemsTest.kt
+++ b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/LinearLayoutManagerRemoveShownItemsTest.kt
@@ -90,10 +90,18 @@
         mLayoutManager.waitForLayout(2)
 
         // .. then the views after the removed view are moved into the gap
-        assertEquals(expectedResultingAdapterPosition, llm.findFirstVisibleItemPosition())
-        assertEquals(expectedResultingAdapterPosition, llm.findFirstCompletelyVisibleItemPosition())
-        assertEquals(expectedResultingAdapterPosition, llm.findLastCompletelyVisibleItemPosition())
-        assertEquals(expectedResultingAdapterPosition, llm.findLastVisibleItemPosition())
+        mActivityRule.runOnUiThread {
+            assertEquals(expectedResultingAdapterPosition, llm.findFirstVisibleItemPosition())
+            assertEquals(
+                expectedResultingAdapterPosition,
+                llm.findFirstCompletelyVisibleItemPosition()
+            )
+            assertEquals(
+                expectedResultingAdapterPosition,
+                llm.findLastCompletelyVisibleItemPosition()
+            )
+            assertEquals(expectedResultingAdapterPosition, llm.findLastVisibleItemPosition())
+        }
     }
 
     /**
@@ -136,13 +144,18 @@
         mLayoutManager.waitForLayout(2)
 
         // .. then the views after the removed view are moved into the gap
-        assertEquals(expectedResultingAdapterPosition, llm.findFirstVisibleItemPosition())
-        assertEquals(expectedResultingAdapterPosition, llm.findFirstCompletelyVisibleItemPosition())
-        assertEquals(
-            expectedResultingAdapterPosition + 1,
-            llm.findLastCompletelyVisibleItemPosition()
-        )
-        assertEquals(expectedResultingAdapterPosition + 1, llm.findLastVisibleItemPosition())
+        mActivityRule.runOnUiThread {
+            assertEquals(expectedResultingAdapterPosition, llm.findFirstVisibleItemPosition())
+            assertEquals(
+                expectedResultingAdapterPosition,
+                llm.findFirstCompletelyVisibleItemPosition()
+            )
+            assertEquals(
+                expectedResultingAdapterPosition + 1,
+                llm.findLastCompletelyVisibleItemPosition()
+            )
+            assertEquals(expectedResultingAdapterPosition + 1, llm.findLastVisibleItemPosition())
+        }
     }
 
     /**
@@ -183,13 +196,21 @@
         mLayoutManager.waitForLayout(2)
 
         // .. then the views after the removed view are moved into the gap
-        assertEquals(expectedResultingAdapterPosition, llm.findFirstVisibleItemPosition())
-        assertEquals(expectedResultingAdapterPosition, llm.findFirstCompletelyVisibleItemPosition())
-        assertEquals(expectedResultingAdapterPosition, llm.findLastCompletelyVisibleItemPosition())
-        assertEquals(expectedResultingAdapterPosition, llm.findLastVisibleItemPosition())
+        mActivityRule.runOnUiThread {
+            assertEquals(expectedResultingAdapterPosition, llm.findFirstVisibleItemPosition())
+            assertEquals(
+                expectedResultingAdapterPosition,
+                llm.findFirstCompletelyVisibleItemPosition()
+            )
+            assertEquals(
+                expectedResultingAdapterPosition,
+                llm.findLastCompletelyVisibleItemPosition()
+            )
+            assertEquals(expectedResultingAdapterPosition, llm.findLastVisibleItemPosition())
+        }
     }
 
-    private inner class MyLayoutManager internal constructor(context: Context, val itemSize: Int) :
+    private inner class MyLayoutManager(context: Context, val itemSize: Int) :
         WrappedLinearLayoutManager(context, config.mOrientation, config.mReverseLayout) {
 
         override fun calculateExtraLayoutSpace(
diff --git a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewKeyEventTest.kt b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewKeyEventTest.kt
index 45848e9..80b1fea 100644
--- a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewKeyEventTest.kt
+++ b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewKeyEventTest.kt
@@ -280,4 +280,14 @@
         MatcherAssert.assertThat(layoutManager.findFirstCompletelyVisibleItemPosition(),
             CoreMatchers.`is`(0))
     }
+
+    @Test
+    fun dispatchKeyEvent_withNoLayoutManager_doesNotCrash() {
+        val context = mActivityTestRule.activity
+        val recyclerView = RecyclerView(context)
+
+        recyclerView.layoutParams = ViewGroup.LayoutParams(viewWidth, viewHeight)
+        val event = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_PAGE_DOWN)
+        recyclerView.dispatchKeyEvent(event)
+    }
 }
diff --git a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewLayoutTest.java b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewLayoutTest.java
index b8aafcc..1bdee58 100644
--- a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewLayoutTest.java
+++ b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewLayoutTest.java
@@ -1897,12 +1897,13 @@
                 any(int[].class), eq(ViewCompat.TYPE_TOUCH));
         verify(nsp, atLeastOnce()).onStopNestedScroll(eq(mRecyclerView), eq(ViewCompat.TYPE_TOUCH));
 
-        // Verify that the non-touch events were dispatched by the fling settle
-        verify(nsp, times(1)).onStartNestedScroll(eq(mRecyclerView), eq(mRecyclerView),
+        // Verify that no non-touch events were dispatched by the fling settle, because this is a
+        // drag, not a fling
+        verify(nsp, never()).onStartNestedScroll(eq(mRecyclerView), eq(mRecyclerView),
                 eq(ViewCompat.SCROLL_AXIS_VERTICAL), eq(ViewCompat.TYPE_NON_TOUCH));
-        verify(nsp, times(1)).onNestedScrollAccepted(eq(mRecyclerView), eq(mRecyclerView),
+        verify(nsp, never()).onNestedScrollAccepted(eq(mRecyclerView), eq(mRecyclerView),
                 eq(ViewCompat.SCROLL_AXIS_VERTICAL), eq(ViewCompat.TYPE_NON_TOUCH));
-        verify(nsp, atLeastOnce()).onNestedPreScroll(eq(mRecyclerView), anyInt(), anyInt(),
+        verify(nsp, never()).onNestedPreScroll(eq(mRecyclerView), anyInt(), anyInt(),
                 any(int[].class), eq(ViewCompat.TYPE_NON_TOUCH));
     }
 
@@ -1923,12 +1924,13 @@
                 any(int[].class), eq(ViewCompat.TYPE_TOUCH));
         verify(nsp, atLeastOnce()).onStopNestedScroll(eq(mRecyclerView), eq(ViewCompat.TYPE_TOUCH));
 
-        // Verify that the non-touch events were dispatched by the fling settle
-        verify(nsp, times(1)).onStartNestedScroll(eq(mRecyclerView), eq(mRecyclerView),
+        // Verify that no non-touch events were dispatched by the fling settle, because this is a
+        // drag, not a fling
+        verify(nsp, never()).onStartNestedScroll(eq(mRecyclerView), eq(mRecyclerView),
                 eq(ViewCompat.SCROLL_AXIS_HORIZONTAL), eq(ViewCompat.TYPE_NON_TOUCH));
-        verify(nsp, times(1)).onNestedScrollAccepted(eq(mRecyclerView), eq(mRecyclerView),
+        verify(nsp, never()).onNestedScrollAccepted(eq(mRecyclerView), eq(mRecyclerView),
                 eq(ViewCompat.SCROLL_AXIS_HORIZONTAL), eq(ViewCompat.TYPE_NON_TOUCH));
-        verify(nsp, atLeastOnce()).onNestedPreScroll(eq(mRecyclerView), anyInt(), anyInt(),
+        verify(nsp, never()).onNestedPreScroll(eq(mRecyclerView), anyInt(), anyInt(),
                 any(int[].class), eq(ViewCompat.TYPE_NON_TOUCH));
     }
 
diff --git a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewOnGenericMotionEventTest.java b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewOnGenericMotionEventTest.java
index f489478..d0d6061 100644
--- a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewOnGenericMotionEventTest.java
+++ b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewOnGenericMotionEventTest.java
@@ -18,6 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
 
 import android.content.Context;
 import android.view.MotionEvent;
@@ -46,10 +47,13 @@
 public class RecyclerViewOnGenericMotionEventTest {
 
     TestRecyclerView mRecyclerView;
+    TestDifferentialMotionFlingHelper mFlingHelper;
 
     @Before
     public void setUp() throws Exception {
         mRecyclerView = new TestRecyclerView(getContext());
+        mFlingHelper = createDummyFlingHelper();
+        mRecyclerView.mDifferentialMotionFlingHelper = mFlingHelper;
     }
 
     private Context getContext() {
@@ -72,6 +76,8 @@
 
         assertTotalScroll(0, (int) (-2f * getScaledVerticalScrollFactor()),
                 /* assertSmoothScroll= */ false);
+        assertEquals(MotionEvent.AXIS_SCROLL, mFlingHelper.mLastAxis);
+        assertEquals(mRecyclerView.mLastGenericMotionEvent, mFlingHelper.mLastMotionEvent);
     }
 
     @Test
@@ -88,6 +94,8 @@
 
         assertTotalScroll((int) (2f * getScaledHorizontalScrollFactor()), 0,
                 /* assertSmoothScroll= */ false);
+        assertEquals(MotionEvent.AXIS_SCROLL, mFlingHelper.mLastAxis);
+        assertEquals(mRecyclerView.mLastGenericMotionEvent, mFlingHelper.mLastMotionEvent);
     }
 
     @Test
@@ -100,6 +108,7 @@
                 MotionEvent.AXIS_SCROLL, 2, InputDeviceCompat.SOURCE_ROTARY_ENCODER, mRecyclerView);
         assertTotalScroll(0, (int) (-2f * getScaledVerticalScrollFactor()),
                 /* assertSmoothScroll= */ true);
+        assertNull(mFlingHelper.mLastMotionEvent);
     }
 
     @Test
@@ -135,6 +144,7 @@
                 MotionEvent.AXIS_SCROLL, 2, InputDeviceCompat.SOURCE_ROTARY_ENCODER, mRecyclerView);
         assertTotalScroll((int) (2f * getScaledHorizontalScrollFactor()), 0,
                 /* assertSmoothScroll= */ true);
+        assertNull(mFlingHelper.mLastMotionEvent);
     }
 
     @Test
@@ -304,10 +314,18 @@
         int mTotalSmoothX = 0;
         int mTotalSmoothY = 0;
 
+        MotionEvent mLastGenericMotionEvent;
+
         TestRecyclerView(Context context) {
             super(context);
         }
 
+        @Override
+        public boolean onGenericMotionEvent(MotionEvent ev) {
+            mLastGenericMotionEvent = ev;
+            return super.onGenericMotionEvent(ev);
+        }
+
         boolean scrollByInternal(int x, int y, MotionEvent ev, int type) {
             mTotalX += x;
             mTotalY += y;
@@ -321,4 +339,26 @@
             super.smoothScrollBy(dx, dy, interpolator, duration, withNestedScrolling);
         }
     }
+
+    private TestDifferentialMotionFlingHelper createDummyFlingHelper() {
+        return new TestDifferentialMotionFlingHelper(
+                mRecyclerView.getContext(), new TestDifferentialMotionFlingTarget());
+    }
+
+    private static class TestDifferentialMotionFlingHelper extends DifferentialMotionFlingHelper {
+        MotionEvent mLastMotionEvent;
+        int mLastAxis;
+
+        TestDifferentialMotionFlingHelper(Context context,
+                DifferentialMotionFlingHelper.DifferentialMotionFlingTarget target) {
+            super(context, target);
+        }
+
+        @Override
+        public void onMotionEvent(MotionEvent event, int axis) {
+            mLastMotionEvent = event;
+            mLastAxis = axis;
+        }
+    }
+
 }
diff --git a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewSmoothScrollToPositionTest.kt b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewSmoothScrollToPositionTest.kt
index c3cab50..6a10b3d 100644
--- a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewSmoothScrollToPositionTest.kt
+++ b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewSmoothScrollToPositionTest.kt
@@ -159,7 +159,9 @@
             calledOnStop.await(30, TimeUnit.SECONDS)
         )
         Assert.assertNotNull(
-            "smoothScrollToPosition should succeed",
+            "smoothScrollToPosition should succeed " +
+                "(first visible item: " + layoutManager.findFirstVisibleItemPosition() +
+                ", last visible item: " + layoutManager.findLastVisibleItemPosition() + ")",
             recyclerView.findViewHolderForLayoutPosition(targetPosition)
         )
     }
diff --git a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/TestDifferentialMotionFlingTarget.java b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/TestDifferentialMotionFlingTarget.java
new file mode 100644
index 0000000..9a381a4
--- /dev/null
+++ b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/TestDifferentialMotionFlingTarget.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.recyclerview.widget;
+
+/**
+ * A test implementation for {@link DifferentialMotionFlingHelper.DifferentialMotionFlingTarget}.
+ */
+class TestDifferentialMotionFlingTarget
+        implements DifferentialMotionFlingHelper.DifferentialMotionFlingTarget {
+    float mLastFlingVelocity = 0;
+    int mNumStops = 0;
+
+    @Override
+    public boolean startDifferentialMotionFling(float velocity) {
+        mLastFlingVelocity = velocity;
+        return true;
+    }
+
+    @Override
+    public void stopDifferentialMotionFling() {
+        mNumStops++;
+    }
+
+    @Override
+    public float getScaledScrollFactor() {
+        return 1;
+    }
+}
diff --git a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/TouchUtils.java b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/TouchUtils.java
index 8ba182c..69df81d 100644
--- a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/TouchUtils.java
+++ b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/TouchUtils.java
@@ -165,15 +165,23 @@
     }
 
     static void scrollView(int axis, int axisValue, int inputDevice, View v) {
-        MotionEvent.PointerProperties[] pointerProperties = {new MotionEvent.PointerProperties()};
+        MotionEvent e = createMotionEvent(/* inputDeviceId= */ 0, inputDevice, axis, axisValue);
+        v.onGenericMotionEvent(e);
+        e.recycle();
+    }
+
+    /** Creates a {@link MotionEvent} with provided input and motion values. */
+    static MotionEvent createMotionEvent(
+            int inputDeviceId, int inputSource, int axis, int axisValue) {
+        MotionEvent.PointerProperties props = new MotionEvent.PointerProperties();
+        props.id = 0;
+        MotionEvent.PointerProperties[] pointerProperties = {props};
         MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
         coords.setAxisValue(axis, axisValue);
         MotionEvent.PointerCoords[] pointerCoords = {coords};
-        MotionEvent e = MotionEvent.obtain(
+        return MotionEvent.obtain(
                 0, System.currentTimeMillis(), MotionEvent.ACTION_SCROLL,
-                1, pointerProperties, pointerCoords, 0, 0, 1, 1, 0, 0, inputDevice, 0);
-        v.onGenericMotionEvent(e);
-        e.recycle();
+                1, pointerProperties, pointerCoords, 0, 0, 1, 1, inputDeviceId, 0, inputSource, 0);
     }
 
     static void dragViewToTop(Instrumentation inst, View v) {
@@ -313,6 +321,11 @@
             event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, x, y, 0);
             inst.sendPointerSync(event);
         }
+
+        // Dwell at the end, because this is a drag, not a fling
+        eventTime += 500;
+        event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, x, y, 0);
+        inst.sendPointerSync(event);
         eventTime++;
         event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
         inst.sendPointerSync(event);
diff --git a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/DifferentialMotionFlingHelper.java b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/DifferentialMotionFlingHelper.java
new file mode 100644
index 0000000..eb60f4f
--- /dev/null
+++ b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/DifferentialMotionFlingHelper.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.recyclerview.widget;
+
+import android.content.Context;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.ViewConfiguration;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.core.view.VelocityTrackerCompat;
+import androidx.core.view.ViewConfigurationCompat;
+
+/**
+ * Helper for controlling differential motion flings.
+ *
+ * <p><b>Differential motion</b> here refers to motions that report change in position instead of
+ * absolution position. For instance, differential data points of 2, -1, 5 represent: there was
+ * a movement by "2" units, then by "-1" units, then by "5" units. Examples of motions reported
+ * differentially include motions from {@link MotionEvent#AXIS_SCROLL}.
+ *
+ * <p>The client should call {@link #onMotionEvent} when a differential motion event happens on
+ * the target View (that is, the View on which we want to fling), and this class processes the event
+ * to orchestrate fling.
+ *
+ * <p>Note that this helper class currently works to control fling only in one direction at a time.
+ * As such, it works independently of horizontal/vertical orientations. It requests its client to
+ * start/stop fling, and it's up to the client to choose the fling direction based on its specific
+ * internal configurations and/or preferences.
+ */
+class DifferentialMotionFlingHelper {
+    private final Context mContext;
+    private final DifferentialMotionFlingTarget mTarget;
+
+    private final FlingVelocityThresholdCalculator mVelocityThresholdCalculator;
+    private final DifferentialVelocityProvider mVelocityProvider;
+
+    @Nullable private VelocityTracker mVelocityTracker;
+
+    private float mLastFlingVelocity;
+
+    private int mLastProcessedAxis = -1;
+    private int mLastProcessedSource = -1;
+    private int mLastProcessedDeviceId = -1;
+
+    // Initialize min and max to +infinity and 0, to effectively disable fling at start.
+    private final int[] mFlingVelocityThresholds = new int[] {Integer.MAX_VALUE, 0};
+
+    /** Interface to calculate the fling velocity thresholds. Helps fake during testing. */
+    @VisibleForTesting
+    interface FlingVelocityThresholdCalculator {
+        /**
+         * Calculates the fling velocity thresholds (in pixels/second) and puts them in a provided
+         * store.
+         *
+         * @param context the context associated with the View that may be flung.
+         * @param store an at-least size-2 int array. The method will overwrite positions 0 and 1
+         *             with the min and max fling velocities, respectively.
+         * @param event the event that may trigger fling.
+         * @param axis the axis being processed for the event.
+         */
+        void calculateFlingVelocityThresholds(
+                Context context, int[] store, MotionEvent event, int axis);
+    }
+
+    /**
+     * Interface to provide velocity. Helps fake during testing.
+     *
+     * <p>The client should call {@link #getCurrentVelocity(VelocityTracker, MotionEvent, int)} each
+     * time it wants to consider a {@link MotionEvent} towards the latest velocity, and the
+     * interface handles providing velocity that accounts for the latest and all past events.
+     */
+    @VisibleForTesting
+    interface DifferentialVelocityProvider {
+        /**
+         * Returns the latest velocity.
+         *
+         * @param vt the {@link VelocityTracker} to be used to compute velocity.
+         * @param event the latest event to be considered in the velocity computations.
+         * @param axis the axis being processed for the event.
+         * @return the calculated, latest velocity.
+         */
+        float getCurrentVelocity(VelocityTracker vt, MotionEvent event, int axis);
+    }
+
+    /**
+     * Represents an entity that may be flung by a differential motion or an entity that initiates
+     * fling on a target View.
+     */
+    interface DifferentialMotionFlingTarget {
+        /**
+         * Start flinging on the target View by a given velocity.
+         *
+         * @param velocity the fling velocity, in pixels/second.
+         * @return {@code true} if fling was successfully initiated, {@code false} otherwise.
+         */
+        boolean startDifferentialMotionFling(float velocity);
+
+        /** Stop any ongoing fling on the target View that is caused by a differential motion. */
+        void stopDifferentialMotionFling();
+
+        /**
+         * Returns the scaled scroll factor to be used for differential motions. This is the
+         * value that the raw {@link MotionEvent} values should be multiplied with to get pixels.
+         *
+         * <p>This usually is one of the values provided by {@link ViewConfigurationCompat}. It is
+         * up to the client to choose and provide any value as per its internal configuration.
+         *
+         * @see ViewConfigurationCompat#getScaledHorizontalScrollFactor(ViewConfiguration, Context)
+         * @see ViewConfigurationCompat#getScaledVerticalScrollFactor(ViewConfiguration, Context)
+         */
+        float getScaledScrollFactor();
+    }
+
+    /** Constructs an instance for a given {@link DifferentialMotionFlingTarget}. */
+    DifferentialMotionFlingHelper(
+            Context context,
+            DifferentialMotionFlingTarget target) {
+        this(context,
+                target,
+                DifferentialMotionFlingHelper::calculateFlingVelocityThresholds,
+                DifferentialMotionFlingHelper::getCurrentVelocity);
+    }
+
+    @VisibleForTesting
+    DifferentialMotionFlingHelper(
+            Context context,
+            DifferentialMotionFlingTarget target,
+            FlingVelocityThresholdCalculator velocityThresholdCalculator,
+            DifferentialVelocityProvider velocityProvider) {
+        mContext = context;
+        mTarget = target;
+        mVelocityThresholdCalculator = velocityThresholdCalculator;
+        mVelocityProvider = velocityProvider;
+    }
+
+    /**
+     * Called to report when a differential motion happens on the View that's the target for fling.
+     *
+     * @param event the {@link MotionEvent} being reported.
+     * @param axis the axis being processed by the target View.
+     */
+    void onMotionEvent(MotionEvent event, int axis) {
+        boolean flingParamsChanged = calculateFlingVelocityThresholds(event, axis);
+        float scaledVelocity =
+                getCurrentVelocity(event, axis) * mTarget.getScaledScrollFactor();
+
+        float velocityDirection = Math.signum(scaledVelocity);
+        // Stop ongoing fling if there has been state changes affecting fling, or if the current
+        // velocity (if non-zero) is opposite of the velocity that last caused fling.
+        if (flingParamsChanged
+                || (velocityDirection != Math.signum(mLastFlingVelocity)
+                    && velocityDirection != 0)) {
+            mTarget.stopDifferentialMotionFling();
+        }
+
+        if (Math.abs(scaledVelocity) < mFlingVelocityThresholds[0]) {
+            return;
+        }
+
+        // Clamp the scaled velocity between [-max, max].
+        // e.g. if max=100, and vel=200
+        // vel = max(-100, min(200, 100)) = max(-100, 100) = 100
+        // e.g. if max=100, and vel=-200
+        // vel = max(-100, min(-200, 100)) = max(-100, -200) = -100
+        scaledVelocity =
+                Math.max(
+                        -mFlingVelocityThresholds[1],
+                        Math.min(scaledVelocity, mFlingVelocityThresholds[1]));
+
+        boolean flung = mTarget.startDifferentialMotionFling(scaledVelocity);
+        mLastFlingVelocity = flung ? scaledVelocity : 0;
+    }
+
+    /**
+     * Calculates fling velocity thresholds based on the provided event and axis, and returns {@code
+     * true} if there has been a change of any params that may affect fling velocity thresholds.
+     */
+    private boolean calculateFlingVelocityThresholds(MotionEvent event, int axis) {
+        int source = event.getSource();
+        int deviceId = event.getDeviceId();
+        if (mLastProcessedSource != source
+                || mLastProcessedDeviceId != deviceId
+                || mLastProcessedAxis != axis) {
+            mVelocityThresholdCalculator.calculateFlingVelocityThresholds(
+                    mContext, mFlingVelocityThresholds, event, axis);
+            // Save data about this processing so that we don't have to re-process fling thresholds
+            // for similar parameters.
+            mLastProcessedSource = source;
+            mLastProcessedDeviceId = deviceId;
+            mLastProcessedAxis = axis;
+            return true;
+        }
+        return false;
+    }
+
+    private static void calculateFlingVelocityThresholds(
+            Context context, int[] buffer, MotionEvent event, int axis) {
+        ViewConfiguration vc = ViewConfiguration.get(context);
+        buffer[0] = ViewConfigurationCompat.getScaledMinimumFlingVelocity(
+                context, vc, event.getDeviceId(), axis, event.getSource());
+        buffer[1] = ViewConfigurationCompat.getScaledMaximumFlingVelocity(
+                context, vc, event.getDeviceId(), axis, event.getSource());
+    }
+
+    private float getCurrentVelocity(MotionEvent event, int axis) {
+        if (mVelocityTracker == null) {
+            mVelocityTracker = VelocityTracker.obtain();
+        }
+
+        return mVelocityProvider.getCurrentVelocity(mVelocityTracker, event, axis);
+    }
+
+    private static float getCurrentVelocity(VelocityTracker vt, MotionEvent event, int axis) {
+        vt.addMovement(event);
+        vt.computeCurrentVelocity(1000);
+        return VelocityTrackerCompat.getAxisVelocity(vt, axis);
+    }
+}
diff --git a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
index e0d8f30..207a943 100644
--- a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
+++ b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
@@ -615,8 +615,8 @@
     private final int mMaxFlingVelocity;
 
     // This value is used when handling rotary encoder generic motion events.
-    private float mScaledHorizontalScrollFactor = Float.MIN_VALUE;
-    private float mScaledVerticalScrollFactor = Float.MIN_VALUE;
+    float mScaledHorizontalScrollFactor = Float.MIN_VALUE;
+    float mScaledVerticalScrollFactor = Float.MIN_VALUE;
 
     private boolean mPreserveFocusAfterLayout = true;
 
@@ -749,6 +749,50 @@
                 }
             };
 
+    private final DifferentialMotionFlingHelper.DifferentialMotionFlingTarget
+            mDifferentialMotionFlingTarget =
+            new DifferentialMotionFlingHelper.DifferentialMotionFlingTarget() {
+                @Override
+                public boolean startDifferentialMotionFling(float velocity) {
+                    int vx = 0;
+                    int vy = 0;
+                    if (mLayout.canScrollVertically()) {
+                        vy = (int) velocity;
+                    } else if (mLayout.canScrollHorizontally()) {
+                        vx = (int) velocity;
+                    }
+
+                    if (vx == 0 && vy == 0) {
+                        return false;
+                    }
+
+                    stopScroll();
+                    // Fling with no threshold check, since the DifferentialMotionFlingHelper should
+                    //  have handled this already.
+                    return flingNoThresholdCheck(vx, vy);
+                }
+
+                @Override
+                public void stopDifferentialMotionFling() {
+                    stopScroll();
+                }
+
+                @Override
+                public float getScaledScrollFactor() {
+                    if (mLayout.canScrollVertically()) {
+                        return -mScaledVerticalScrollFactor;
+                    }
+                    if (mLayout.canScrollHorizontally()) {
+                        return -mScaledHorizontalScrollFactor;
+                    }
+                    return 0;
+                }
+
+            };
+
+    @VisibleForTesting
+    DifferentialMotionFlingHelper mDifferentialMotionFlingHelper =
+            new DifferentialMotionFlingHelper(getContext(), mDifferentialMotionFlingTarget);
     public RecyclerView(@NonNull Context context) {
         this(context, null);
     }
@@ -1964,7 +2008,13 @@
             return true;
         }
 
-        if (getLayoutManager().canScrollVertically()) {
+        LayoutManager layoutManager = getLayoutManager();
+        // If there is no layout manager, then there is nothing to handle key events for.
+        if (layoutManager == null) {
+            return false;
+        }
+
+        if (layoutManager.canScrollVertically()) {
             final int keyCode = event.getKeyCode();
             switch (keyCode) {
                 case KeyEvent.KEYCODE_PAGE_DOWN:
@@ -1979,7 +2029,7 @@
 
                 case KeyEvent.KEYCODE_MOVE_HOME:
                 case KeyEvent.KEYCODE_MOVE_END:
-                    final boolean isReversed = getLayoutManager().isLayoutReversed();
+                    final boolean isReversed = layoutManager.isLayoutReversed();
 
                     final int targetOffset;
                     if (keyCode == KeyEvent.KEYCODE_MOVE_HOME) {
@@ -1991,7 +2041,7 @@
                     smoothScrollToPosition(targetOffset);
                     return true;
             }
-        } else if (getLayoutManager().canScrollHorizontally()) {
+        } else if (layoutManager.canScrollHorizontally()) {
             final int keyCode = event.getKeyCode();
             switch (keyCode) {
                 case KeyEvent.KEYCODE_PAGE_DOWN:
@@ -2006,7 +2056,7 @@
 
                 case KeyEvent.KEYCODE_MOVE_HOME:
                 case KeyEvent.KEYCODE_MOVE_END:
-                    final boolean isReversed = getLayoutManager().isLayoutReversed();
+                    final boolean isReversed = layoutManager.isLayoutReversed();
 
                     final int targetOffset;
                     if (keyCode == KeyEvent.KEYCODE_MOVE_HOME) {
@@ -2819,6 +2869,16 @@
      * @see LayoutManager#canScrollHorizontally()
      */
     public boolean fling(int velocityX, int velocityY) {
+        return fling(velocityX, velocityY, mMinFlingVelocity, mMaxFlingVelocity);
+    }
+
+    /** Flings without checking fling velocity thresholds. */
+    boolean flingNoThresholdCheck(int velocityX, int velocityY) {
+        return fling(velocityX, velocityY, 0, Integer.MAX_VALUE);
+    }
+
+    private boolean fling(int velocityX, int velocityY, int minFlingVelocity,
+            int maxFlingVelocity) {
         if (mLayout == null) {
             Log.e(TAG, "Cannot fling without a LayoutManager set. "
                     + "Call setLayoutManager with a non-null argument.");
@@ -2831,10 +2891,10 @@
         final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
         final boolean canScrollVertical = mLayout.canScrollVertically();
 
-        if (!canScrollHorizontal || Math.abs(velocityX) < mMinFlingVelocity) {
+        if (!canScrollHorizontal || Math.abs(velocityX) < minFlingVelocity) {
             velocityX = 0;
         }
-        if (!canScrollVertical || Math.abs(velocityY) < mMinFlingVelocity) {
+        if (!canScrollVertical || Math.abs(velocityY) < minFlingVelocity) {
             velocityY = 0;
         }
         if (velocityX == 0 && velocityY == 0) {
@@ -2881,8 +2941,8 @@
             }
         }
         if (flingX != 0 || flingY != 0) {
-            flingX = Math.max(-mMaxFlingVelocity, Math.min(flingX, mMaxFlingVelocity));
-            flingY = Math.max(-mMaxFlingVelocity, Math.min(flingY, mMaxFlingVelocity));
+            flingX = Math.max(-maxFlingVelocity, Math.min(flingX, maxFlingVelocity));
+            flingY = Math.max(-maxFlingVelocity, Math.min(flingY, maxFlingVelocity));
             startNestedScrollForType(TYPE_NON_TOUCH);
             mViewFlinger.fling(flingX, flingY);
         }
@@ -2900,8 +2960,8 @@
 
             if (canScroll) {
                 startNestedScrollForType(TYPE_NON_TOUCH);
-                velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
-                velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
+                velocityX = Math.max(-maxFlingVelocity, Math.min(velocityX, maxFlingVelocity));
+                velocityY = Math.max(-maxFlingVelocity, Math.min(velocityY, maxFlingVelocity));
                 mViewFlinger.fling(velocityX, velocityY);
                 return true;
             }
@@ -4029,6 +4089,7 @@
             return false;
         }
 
+        int flingAxis = 0;
         boolean useSmoothScroll = false;
         if (event.getAction() == MotionEvent.ACTION_SCROLL) {
             final float vScroll, hScroll;
@@ -4062,6 +4123,8 @@
                 // Use smooth scrolling for low resolution rotary encoders to avoid the visible
                 // pixel jumps that would be caused by doing regular scrolling.
                 useSmoothScroll = mLowResRotaryEncoderFeature;
+                // Support fling for rotary encoders.
+                flingAxis = MotionEventCompat.AXIS_SCROLL;
             } else {
                 vScroll = 0f;
                 hScroll = 0f;
@@ -4079,6 +4142,10 @@
             } else {
                 nestedScrollByInternal(scaledHScroll, scaledVScroll, event, TYPE_NON_TOUCH);
             }
+
+            if (flingAxis != 0 && !useSmoothScroll) {
+                mDifferentialMotionFlingHelper.onMotionEvent(event, flingAxis);
+            }
         }
         return false;
     }
diff --git a/room/integration-tests/incremental-annotation-processing/src/test/kotlin/androidx/room/gradle/RoomIncrementalAnnotationProcessingTest.kt b/room/integration-tests/incremental-annotation-processing/src/test/kotlin/androidx/room/gradle/RoomIncrementalAnnotationProcessingTest.kt
index 7d9085e..6129158 100644
--- a/room/integration-tests/incremental-annotation-processing/src/test/kotlin/androidx/room/gradle/RoomIncrementalAnnotationProcessingTest.kt
+++ b/room/integration-tests/incremental-annotation-processing/src/test/kotlin/androidx/room/gradle/RoomIncrementalAnnotationProcessingTest.kt
@@ -127,8 +127,7 @@
             appendLine("}")
         }
         val agpDependency = projectSetup.props.agpDependency
-        val kotlinPluginDependency =
-            "org.jetbrains.kotlin:kotlin-gradle-plugin:${projectSetup.props.kotlinVersion}"
+        val kotlinPluginDependency = projectSetup.props.kgpDependency
         val kspPluginDependency =
             "com.google.devtools.ksp:symbol-processing-gradle-plugin:" +
                 projectSetup.props.kspVersion
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/MusicDao.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/MusicDao.kt
index a7ef64d..5753602 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/MusicDao.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/MusicDao.kt
@@ -20,6 +20,7 @@
 import androidx.collection.SparseArrayCompat
 import androidx.lifecycle.LiveData
 import androidx.room.Dao
+import androidx.room.Delete
 import androidx.room.Insert
 import androidx.room.MapInfo
 import androidx.room.Query
@@ -32,6 +33,9 @@
 import androidx.room.integration.kotlintestapp.vo.Artist
 import androidx.room.integration.kotlintestapp.vo.Image
 import androidx.room.integration.kotlintestapp.vo.ImageFormat
+import androidx.room.integration.kotlintestapp.vo.Playlist
+import androidx.room.integration.kotlintestapp.vo.PlaylistSongXRef
+import androidx.room.integration.kotlintestapp.vo.PlaylistWithSongs
 import androidx.room.integration.kotlintestapp.vo.ReleasedAlbum
 import androidx.room.integration.kotlintestapp.vo.Song
 import androidx.sqlite.db.SupportSQLiteQuery
@@ -41,6 +45,7 @@
 import io.reactivex.Flowable
 import java.nio.ByteBuffer
 import java.util.Date
+import kotlinx.coroutines.flow.Flow
 
 @JvmDefaultWithCompatibility
 @Dao
@@ -53,6 +58,16 @@
 
     @Insert
     fun addAlbums(vararg albums: Album)
+
+    @Insert
+    fun addPlaylists(vararg playlists: Playlist)
+
+    @Insert
+    fun addPlaylistSongRelations(vararg relations: PlaylistSongXRef)
+
+    @Delete
+    fun removePlaylistSongRelations(vararg relations: PlaylistSongXRef)
+
     @Insert
     fun addImages(vararg images: Image)
 
@@ -355,4 +370,8 @@
     @MapInfo(keyColumn = "mImageYear", valueColumn = "mTitle")
     @RewriteQueriesToDropUnusedColumns
     fun getNestedMapWithMapInfoKeyAndValue(): Map<Long, Map<Artist, Map<Album, List<String>>>>
+
+    @Transaction
+    @Query("SELECT * FROM Playlist WHERE mPlaylistId = :id")
+    fun getPlaylistsWithSongsFlow(id: Int): Flow<PlaylistWithSongs>
 }
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/FlowQueryTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/FlowQueryTest.kt
index 4a23ca5..02d20ce 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/FlowQueryTest.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/FlowQueryTest.kt
@@ -18,6 +18,10 @@
 
 import androidx.kruth.assertThat
 import androidx.room.integration.kotlintestapp.vo.Book
+import androidx.room.integration.kotlintestapp.vo.Playlist
+import androidx.room.integration.kotlintestapp.vo.PlaylistSongXRef
+import androidx.room.integration.kotlintestapp.vo.PlaylistWithSongs
+import androidx.room.integration.kotlintestapp.vo.Song
 import androidx.room.withTransaction
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
@@ -30,7 +34,6 @@
 import kotlinx.coroutines.cancelAndJoin
 import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.flow.buffer
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.produceIn
 import kotlinx.coroutines.flow.take
@@ -318,4 +321,73 @@
 
         job.cancelAndJoin()
     }
+
+    @Test
+    fun playlistSongs_async_update(): Unit = runBlocking {
+        val musicDao = database.musicDao()
+        val song1 = Song(
+            1,
+            "I Know Places",
+            "Taylor Swift",
+            "1989",
+            195,
+            2014
+        );
+        val song2 = Song(
+            2,
+            "Blank Space",
+            "Taylor Swift",
+            "1989",
+            241,
+            2014
+        )
+        musicDao.addSongs(song1, song2)
+        musicDao.addPlaylists(
+            Playlist(1),
+            Playlist(2)
+        )
+        musicDao.addPlaylistSongRelations(PlaylistSongXRef(1, 1))
+
+        val latches = Array(3) { CountDownLatch(1) }
+        val results = mutableListOf<PlaylistWithSongs>()
+        var collectCall = 0
+        val job = async(Dispatchers.IO) {
+            musicDao.getPlaylistsWithSongsFlow(1).collect {
+                if (collectCall >= latches.size) {
+                    fail("Should have only collected 3 results.")
+                }
+                results.add(it)
+                latches[collectCall].countDown()
+                collectCall++
+            }
+        }
+
+        latches[0].await()
+        assertThat(results.size).isEqualTo(1)
+        results[0].let { playlist ->
+            assertThat(playlist.songs.size).isEqualTo(1)
+            assertThat(playlist.songs[0]).isEqualTo(song1)
+        }
+
+        musicDao.addPlaylistSongRelations(PlaylistSongXRef(1, 2))
+
+        latches[1].await()
+        assertThat(results.size).isEqualTo(2)
+        results[1].let { playlist ->
+            assertThat(playlist.songs.size).isEqualTo(2)
+            assertThat(playlist.songs[0]).isEqualTo(song1)
+            assertThat(playlist.songs[1]).isEqualTo(song2)
+        }
+
+        musicDao.removePlaylistSongRelations(PlaylistSongXRef(1, 2))
+
+        latches[2].await()
+        assertThat(results.size).isEqualTo(3)
+        results[2].let { playlist ->
+            assertThat(playlist.songs.size).isEqualTo(1)
+            assertThat(playlist.songs[0]).isEqualTo(song1)
+        }
+
+        job.cancelAndJoin()
+    }
 }
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/QueryInterceptorTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/QueryInterceptorTest.kt
index 2808373..9fb9cc8d 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/QueryInterceptorTest.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/QueryInterceptorTest.kt
@@ -51,7 +51,7 @@
     var queryAndArgs = CopyOnWriteArrayList<Pair<String, ArrayList<Any?>>>()
 
     @Entity(tableName = "queryInterceptorTestDatabase")
-    data class QueryInterceptorEntity(@PrimaryKey val id: String, val description: String)
+    data class QueryInterceptorEntity(@PrimaryKey val id: String, val description: String?)
 
     @Dao
     interface QueryInterceptorDao {
@@ -197,6 +197,23 @@
     }
 
     @Test
+    fun testNullBindArgumentCompileStatement() {
+        val sql = "INSERT OR ABORT INTO `queryInterceptorTestDatabase` (`id`,`description`) " +
+            "VALUES (?,?)"
+        val statement = mDatabase.openHelper.writableDatabase.compileStatement(sql)
+        statement.bindString(1, "ID")
+        statement.bindNull(2)
+        statement.execute()
+
+        val filteredQueries = queryAndArgs.filter { (query, _) -> query == sql }
+
+        assertThat(filteredQueries).hasSize(1)
+        assertThat(filteredQueries[0].second).hasSize(2)
+        assertThat(filteredQueries[0].second[0]).isEqualTo("ID")
+        assertThat(filteredQueries[0].second[1]).isEqualTo(null)
+    }
+
+    @Test
     fun testCallbackCalledOnceAfterCloseAndReOpen() {
         val dbBuilder = Room.inMemoryDatabaseBuilder(
             ApplicationProvider.getApplicationContext(),
diff --git a/room/room-compiler-processing-testing/build.gradle b/room/room-compiler-processing-testing/build.gradle
index 506df13..5b2da1b 100644
--- a/room/room-compiler-processing-testing/build.gradle
+++ b/room/room-compiler-processing-testing/build.gradle
@@ -15,6 +15,7 @@
  */
 
 import androidx.build.LibraryType
+import androidx.build.KotlinTarget
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
@@ -29,7 +30,6 @@
     implementation(libs.kotlinStdlibJdk8) // KSP defines older version as dependency, force update.
     implementation(libs.ksp)
     implementation(libs.googleCompileTesting)
-    implementation(project(":kruth:kruth"))
     // specify these to match the kotlin compiler version in AndroidX rather than what KSP or KCT
     // uses
     implementation(libs.kotlinCompilerEmbeddable)
@@ -51,9 +51,6 @@
     property("kspVersion", libs.versions.ksp.get())
 }
 
-// Remove upon updating to version 8.0 of lint plugin, b/253219347
-tasks.named("compileKotlin").configure { it.dependsOn(writeTestPropsTask) }
-
 java {
     sourceSets {
         main {
@@ -75,6 +72,7 @@
 androidx {
     name = "Room XProcessor Testing"
     type = LibraryType.ANNOTATION_PROCESSOR_UTILS
+    kotlinTarget = KotlinTarget.KOTLIN_1_9
     inceptionYear = "2020"
     description = "Testing helpers for Room XProcessing APIs"
 }
diff --git a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/CompilationResultSubject.kt b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/CompilationResultSubject.kt
index e38371a..6642422 100644
--- a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/CompilationResultSubject.kt
+++ b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/CompilationResultSubject.kt
@@ -16,16 +16,18 @@
 
 package androidx.room.compiler.processing.util
 
-import androidx.kruth.FailureMetadata
-import androidx.kruth.StringSubject
-import androidx.kruth.Subject
-import androidx.kruth.assertAbout
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.ExperimentalProcessingApi
 import androidx.room.compiler.processing.SyntheticJavacProcessor
 import androidx.room.compiler.processing.SyntheticProcessor
 import androidx.room.compiler.processing.util.compiler.TestCompilationResult
 import androidx.room.compiler.processing.util.runner.CompilationTestRunner
+import com.google.common.truth.Fact.fact
+import com.google.common.truth.Fact.simpleFact
+import com.google.common.truth.FailureMetadata
+import com.google.common.truth.StringSubject
+import com.google.common.truth.Subject
+import com.google.common.truth.Subject.Factory
+import com.google.common.truth.Truth
 import com.google.testing.compile.Compilation
 import java.util.regex.Pattern
 import javax.tools.Diagnostic
@@ -124,9 +126,8 @@
 class CompilationResultSubject internal constructor(
     failureMetadata: FailureMetadata,
     val compilationResult: CompilationResult,
-) : Subject<CompilationResult>(
-    actual = compilationResult,
-    metadata = failureMetadata,
+) : Subject<CompilationResultSubject, CompilationResult>(
+    failureMetadata, compilationResult
 ) {
     /**
      * set to true if any assertion on the subject requires it to fail (e.g. looking for errors)
@@ -149,7 +150,9 @@
     fun hasRawOutputContaining(expected: String) = apply {
         val found = compilationResult.rawOutput().contains(expected)
         if (!found) {
-            failWithActual("Did not find $expected in the output.")
+            failWithActual(
+                simpleFact("Did not find $expected in the output.")
+            )
         }
     }
 
@@ -171,7 +174,9 @@
     private fun hasDiagnosticCount(kind: Diagnostic.Kind, expected: Int) = apply {
         val actual = compilationResult.diagnosticsOfKind(kind).size
         if (actual != expected) {
-            failWithActual("expected $expected $kind messages, found $actual")
+            failWithActual(
+                simpleFact("expected $expected $kind messages, found $actual")
+            )
         }
     }
     /**
@@ -326,7 +331,9 @@
     fun hasError() = apply {
         shouldSucceed = false
         if (compilationResult.diagnosticsOfKind(Diagnostic.Kind.ERROR).isEmpty()) {
-            failWithActual("expected at least one failure message")
+            failWithActual(
+                simpleFact("expected at least one failure message")
+            )
         }
     }
 
@@ -337,9 +344,12 @@
      */
     fun generatedSourceFileWithPath(relativePath: String): StringSubject {
         val match = findGeneratedSource(relativePath)
-            ?: failWithActual("Didn't generate file with path: $relativePath")
-
-        return assertThat(match.contents)
+        if (match == null) {
+            failWithActual(
+                simpleFact("Didn't generate file with path: $relativePath")
+            )
+        }
+        return Truth.assertThat(match!!.contents)
     }
 
     private fun findGeneratedSource(relativePath: String) = compilationResult.generatedSources
@@ -358,14 +368,20 @@
     fun generatedSource(source: Source) = apply {
         val match = compilationResult.generatedSources.firstOrNull {
             it.relativePath == source.relativePath
-        } ?: failWithActual("Didn't generate $source")
+        }
+        if (match == null) {
+            failWithActual(
+                simpleFact("Didn't generate $source")
+            )
+            return@apply
+        }
         val mismatch = source.findMismatch(match)
         if (mismatch != null) {
             failWithActual(
-                "Generated code does not match expected",
-                "mismatch: $mismatch",
-                "expected: ${source.contents}",
-                "actual: ${match.contents}",
+                simpleFact("Generated code does not match expected"),
+                fact("mismatch", mismatch),
+                fact("expected", source.contents),
+                fact("actual", match.contents),
             )
         }
     }
@@ -377,8 +393,10 @@
     internal fun assertCompilationResult() {
         if (compilationResult.successfulCompilation != shouldSucceed) {
             failWithActual(
-                "expected compilation result to be: $shouldSucceed but was " +
-                    "${compilationResult.successfulCompilation}"
+                simpleFact(
+                    "expected compilation result to be: $shouldSucceed but was " +
+                        "${compilationResult.successfulCompilation}"
+                )
             )
         }
     }
@@ -389,7 +407,9 @@
      */
     internal fun assertAllExpectedRoundsAreCompleted() {
         if (compilationResult.processor.expectsAnotherRound()) {
-            failWithActual("Test runner requested another round but that didn't happen")
+            failWithActual(
+                simpleFact("Test runner requested another round but that didn't happen")
+            )
         }
     }
 
@@ -427,7 +447,7 @@
             }
         }
         if (matches.isEmpty()) {
-            failWithActual(buildErrorMessage())
+            failWithActual(simpleFact(buildErrorMessage()))
         }
         return DiagnosticMessagesSubject.assertThat(matches)
     }
@@ -449,7 +469,7 @@
             }
         }
         if (matches.isEmpty()) {
-            failWithActual(buildErrorMessage())
+            failWithActual(simpleFact(buildErrorMessage()))
         }
         return DiagnosticMessagesSubject.assertThat(matches)
     }
@@ -470,10 +490,18 @@
     }
 
     companion object {
+        private val FACTORY =
+            Factory<CompilationResultSubject, CompilationResult> { metadata, actual ->
+                CompilationResultSubject(metadata, actual)
+            }
+
         fun assertThat(
             compilationResult: CompilationResult
-        ): CompilationResultSubject =
-            assertAbout(::CompilationResultSubject).that(compilationResult)
+        ): CompilationResultSubject {
+            return Truth.assertAbout(FACTORY).that(
+                compilationResult
+            )
+        }
     }
 }
 
diff --git a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/DiagnosticMessagesSubject.kt b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/DiagnosticMessagesSubject.kt
index de1f737..213eea0 100644
--- a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/DiagnosticMessagesSubject.kt
+++ b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/DiagnosticMessagesSubject.kt
@@ -16,9 +16,11 @@
 
 package androidx.room.compiler.processing.util
 
-import androidx.kruth.FailureMetadata
-import androidx.kruth.Subject
-import androidx.kruth.assertAbout
+import com.google.common.truth.Fact.simpleFact
+import com.google.common.truth.FailureMetadata
+import com.google.common.truth.Subject
+import com.google.common.truth.Subject.Factory
+import com.google.common.truth.Truth
 
 /**
  * Truth subject for diagnostic messages
@@ -26,8 +28,8 @@
 class DiagnosticMessagesSubject internal constructor(
     failureMetadata: FailureMetadata,
     private val diagnosticMessages: List<DiagnosticMessage>,
-) : Subject<List<DiagnosticMessage>>(
-    diagnosticMessages, failureMetadata
+) : Subject<DiagnosticMessagesSubject, List<DiagnosticMessage>>(
+    failureMetadata, diagnosticMessages
 ) {
 
     private val lineContents by lazy {
@@ -50,10 +52,15 @@
      * Note that if there are multiple messages, any match will be sufficient.
      */
     fun onLine(lineNumber: Int) = apply {
-        if (locations.none { it.line == lineNumber }) {
+        if (locations.none {
+            it.line == lineNumber
+        }
+        ) {
             failWithActual(
-                "expected line $lineNumber but it was " +
-                    locations.joinToString(",")
+                simpleFact(
+                    "expected line $lineNumber but it was " +
+                        locations.joinToString(",")
+                )
             )
         }
     }
@@ -64,7 +71,7 @@
     fun hasCount(expected: Int) = apply {
         if (diagnosticMessages.size != expected) {
             failWithActual(
-                "expected $expected messages, found ${diagnosticMessages.size}"
+                simpleFact("expected $expected messages, found ${diagnosticMessages.size}")
             )
         }
     }
@@ -75,7 +82,7 @@
     fun onLineContaining(content: String) = apply {
         if (lineContents.isEmpty()) {
             failWithActual(
-                "Cannot validate line content due to missing location information"
+                simpleFact("Cannot validate line content due to missing location information")
             )
         }
         if (lineContents.none {
@@ -83,8 +90,10 @@
         }
         ) {
             failWithActual(
-                "expected line content with $content but was " +
-                    lineContents.joinToString("\n")
+                simpleFact(
+                    "expected line content with $content but was " +
+                        lineContents.joinToString("\n")
+                )
             )
         }
     }
@@ -96,18 +105,28 @@
     fun onSource(source: Source) = apply {
         if (locations.none { it.source == source }) {
             failWithActual(
-                """
+                simpleFact(
+                    """
                     Expected diagnostic to be on $source but found it on
                     ${locations.joinToString(",")}
-                """.trimIndent()
+                    """.trimIndent()
+                )
             )
         }
     }
 
     companion object {
+        private val FACTORY =
+            Factory<DiagnosticMessagesSubject, List<DiagnosticMessage>> { metadata, actual ->
+                DiagnosticMessagesSubject(metadata, actual)
+            }
+
         fun assertThat(
             diagnosticMessages: List<DiagnosticMessage>
-        ): DiagnosticMessagesSubject =
-            assertAbout(::DiagnosticMessagesSubject).that(diagnosticMessages)
+        ): DiagnosticMessagesSubject {
+            return Truth.assertAbout(FACTORY).that(
+                diagnosticMessages
+            )
+        }
     }
 }
diff --git a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/ProcessorTestExt.kt b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/ProcessorTestExt.kt
index bcef38b..a0145e7 100644
--- a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/ProcessorTestExt.kt
+++ b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/ProcessorTestExt.kt
@@ -16,8 +16,6 @@
 
 package androidx.room.compiler.processing.util
 
-import androidx.kruth.assertThat
-import androidx.kruth.assertWithMessage
 import androidx.room.compiler.processing.ExperimentalProcessingApi
 import androidx.room.compiler.processing.XProcessingEnvConfig
 import androidx.room.compiler.processing.XProcessingEnvironmentTestConfigProvider
@@ -32,6 +30,8 @@
 import androidx.room.compiler.processing.util.runner.KspCompilationTestRunner
 import androidx.room.compiler.processing.util.runner.TestCompilationParameters
 import com.google.common.io.Files
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
 import com.google.devtools.ksp.processing.SymbolProcessorProvider
 import java.io.File
 import java.util.jar.JarEntry
diff --git a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/XTestInvocation.kt b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/XTestInvocation.kt
index 959e75e..4e230a0 100644
--- a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/XTestInvocation.kt
+++ b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/XTestInvocation.kt
@@ -16,10 +16,10 @@
 
 package androidx.room.compiler.processing.util
 
-import androidx.kruth.assertWithMessage
 import androidx.room.compiler.processing.ExperimentalProcessingApi
 import androidx.room.compiler.processing.XProcessingEnv
 import androidx.room.compiler.processing.XRoundEnv
+import com.google.common.truth.Truth
 import kotlin.reflect.KClass
 
 /**
@@ -100,7 +100,7 @@
     }
 
     private fun assertNotDisposed() {
-        assertWithMessage("Cannot use a test invocation after it is disposed.")
+        Truth.assertWithMessage("Cannot use a test invocation after it is disposed.")
             .that(disposed)
             .isFalse()
     }
diff --git a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/compiler/SourceSet.kt b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/compiler/SourceSet.kt
index c010286..6f0f335 100644
--- a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/compiler/SourceSet.kt
+++ b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/compiler/SourceSet.kt
@@ -19,6 +19,9 @@
 import androidx.room.compiler.processing.util.Source
 import java.io.File
 
+private val BY_ROUNDS_PATH_PATTERN =
+    "(byRounds${File.separator}[0-9]+${File.separator})?(.*)".toPattern()
+
 /**
  * Represents sources that are positioned in the [root] folder.
  * see: [fromExistingFiles]
@@ -59,7 +62,8 @@
     }
 
     /**
-     * Finds the source file matching the given relative path (from root)
+     * Finds the source file matching the given relative path (from root). This will ignore
+     * "byRounds/<round number>" directories added by KSP.
      */
     fun findSourceFile(
         path: String
@@ -68,7 +72,14 @@
         if (!file.path.startsWith(root.path)) {
             return null
         }
-        val relativePath = path.substringAfter(root.canonicalPath + File.separator)
+        val relativePath = path.substringAfter(root.canonicalPath + File.separator).let {
+            val matcher = BY_ROUNDS_PATH_PATTERN.matcher(it)
+            if (matcher.find()) {
+                matcher.group(2)
+            } else {
+                it
+            }
+        }
         return sources.firstOrNull {
             it.relativePath == relativePath
         }
diff --git a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticMessageCollectorTest.kt b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticMessageCollectorTest.kt
index 1c5c650..66b21fa5 100644
--- a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticMessageCollectorTest.kt
+++ b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticMessageCollectorTest.kt
@@ -16,10 +16,10 @@
 
 package androidx.room.compiler.processing.util
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.util.compiler.DiagnosticsMessageCollector
 import androidx.room.compiler.processing.util.compiler.steps.RawDiagnosticMessage
 import androidx.room.compiler.processing.util.compiler.steps.RawDiagnosticMessage.Location
+import com.google.common.truth.Truth.assertThat
 import javax.tools.Diagnostic
 import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
 import org.junit.Test
diff --git a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticsTest.kt b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticsTest.kt
index d211727..7829bd4 100644
--- a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticsTest.kt
+++ b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/DiagnosticsTest.kt
@@ -16,8 +16,8 @@
 
 package androidx.room.compiler.processing.util
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.ExperimentalProcessingApi
+import com.google.common.truth.Truth.assertThat
 import javax.tools.Diagnostic
 import org.junit.Test
 import org.junit.runner.RunWith
diff --git a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/GeneratedCodeMatchTest.kt b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/GeneratedCodeMatchTest.kt
index 7d71f16..ba43888 100644
--- a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/GeneratedCodeMatchTest.kt
+++ b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/GeneratedCodeMatchTest.kt
@@ -16,11 +16,10 @@
 
 package androidx.room.compiler.processing.util
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.ExperimentalProcessingApi
 import androidx.room.compiler.processing.XElement
 import androidx.room.compiler.processing.compat.XConverters.toXProcessing
-import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.JavaFile
 import com.squareup.javapoet.TypeName
 import com.squareup.javapoet.TypeSpec
@@ -339,8 +338,7 @@
             }
         }
 
-        // Kruth doesn't support exceptions yet
-        Truth.assertThat(result.exceptionOrNull())
+        assertThat(result.exceptionOrNull())
             .hasCauseThat()
             .hasMessageThat()
             .contains(
diff --git a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/LoadFromDefaultEnvironmentConfigurationProviderTest.kt b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/LoadFromDefaultEnvironmentConfigurationProviderTest.kt
index 773c6cd..40bdb48 100644
--- a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/LoadFromDefaultEnvironmentConfigurationProviderTest.kt
+++ b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/LoadFromDefaultEnvironmentConfigurationProviderTest.kt
@@ -16,7 +16,7 @@
 
 package androidx.room.compiler.processing.util
 
-import androidx.kruth.assertThat
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 
 class LoadFromDefaultEnvironmentConfigurationProviderTest {
diff --git a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/MultiRoundProcessingTest.kt b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/MultiRoundProcessingTest.kt
index f05bf87..7df4df1 100644
--- a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/MultiRoundProcessingTest.kt
+++ b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/MultiRoundProcessingTest.kt
@@ -16,7 +16,7 @@
 
 package androidx.room.compiler.processing.util
 
-import androidx.kruth.assertThat
+import com.google.common.truth.Truth.assertThat
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.JavaFile
 import com.squareup.javapoet.TypeSpec
diff --git a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/SourceSetTest.kt b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/SourceSetTest.kt
new file mode 100644
index 0000000..98fd644
--- /dev/null
+++ b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/SourceSetTest.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing.util
+
+import androidx.room.compiler.processing.util.compiler.SourceSet
+import com.google.common.truth.Truth.assertThat
+import org.jetbrains.kotlin.konan.file.File
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+
+class SourceSetTest {
+    @Rule
+    @JvmField
+    val tempFolder = TemporaryFolder()
+
+    private val BY_ROUNDS_DIR = "byRounds"
+    val SOURCE_DIR = "test"
+    val SOURCE_CLASS = "A"
+    val SOURCE_FILE = "$SOURCE_CLASS.java"
+
+    @Test
+    fun testFindSourceFile() {
+        tempFolder.newFolder(SOURCE_DIR)
+        val sourceFile = tempFolder.newFile(SOURCE_DIR + File.separator + SOURCE_FILE)
+        sourceFile.writeText("public class A {}")
+        val sourceSet = SourceSet.fromExistingFiles(tempFolder.root)
+        val path = tempFolder.root.path + File.separator + SOURCE_DIR + File.separator + SOURCE_FILE
+
+        val f = sourceSet.findSourceFile(path)
+        assertThat(f).isNotNull()
+        assertThat(f!!.relativePath).isEqualTo(SOURCE_DIR + File.separator + SOURCE_FILE)
+    }
+
+    @Test
+    fun testFindSourceFileShouldIgnoreRoundsDir() {
+        for (i in 0..100) {
+            val round = i.toString()
+
+            tempFolder.newFolder(BY_ROUNDS_DIR, round, SOURCE_DIR)
+            tempFolder.newFile(BY_ROUNDS_DIR + File.separator + round + File.separator +
+                    SOURCE_DIR + File.separator + SOURCE_FILE)
+            val sourceSet = SourceSet(tempFolder.root, listOf(
+                Source.java("$SOURCE_DIR.$SOURCE_CLASS", "public class A {}")))
+            val path = tempFolder.root.path + File.separator + BY_ROUNDS_DIR + File.separator +
+                    round + File.separator + SOURCE_DIR + File.separator + SOURCE_FILE
+
+            val f = sourceSet.findSourceFile(path)
+            assertThat(f).isNotNull()
+            assertThat(f!!.relativePath).isEqualTo(SOURCE_DIR + File.separator + SOURCE_FILE)
+        }
+    }
+}
diff --git a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestConfigTest.kt b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestConfigTest.kt
index cde233d..7f731db 100644
--- a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestConfigTest.kt
+++ b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestConfigTest.kt
@@ -16,8 +16,8 @@
 
 package androidx.room.compiler.processing.util
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.util.CompilationTestCapabilities.Config
+import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 
 class TestConfigTest {
diff --git a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestRunnerTest.kt b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestRunnerTest.kt
index 10cf032..32fc356 100644
--- a/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestRunnerTest.kt
+++ b/room/room-compiler-processing-testing/src/test/java/androidx/room/compiler/processing/util/TestRunnerTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.room.compiler.processing.util
 
-import androidx.kruth.assertThat
 import androidx.room.compiler.processing.ExperimentalProcessingApi
 import androidx.room.compiler.processing.SyntheticJavacProcessor
 import androidx.room.compiler.processing.SyntheticKspProcessor
@@ -29,7 +28,7 @@
 import androidx.room.compiler.processing.util.compiler.TestCompilationArguments
 import androidx.room.compiler.processing.util.compiler.compile
 import androidx.room.compiler.processing.util.compiler.steps.KaptCompilationStep
-import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
 import com.google.devtools.ksp.processing.SymbolProcessor
 import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
 import com.google.devtools.ksp.processing.SymbolProcessorProvider
@@ -144,8 +143,7 @@
             "c" to "d"
         )
         val handler: (XTestInvocation) -> Unit = {
-            // Kruth MapSubject doesn't support containsAtLeastEntriesIn yet
-            Truth.assertThat(it.processingEnv.options).containsAtLeastEntriesIn(testOptions)
+            assertThat(it.processingEnv.options).containsAtLeastEntriesIn(testOptions)
         }
         runJavaProcessorTest(
             sources = emptyList(),
@@ -425,7 +423,7 @@
             "org.jetbrains.kotlin.kapt3",
             listOf("-P", "plugin:org.jetbrains.kotlin.kapt3:correctErrorTypes=true")
         ).let { options ->
-            assertThat(options).containsExactly("correctErrorTypes" to "true")
+            assertThat(options).containsExactly("correctErrorTypes", "true")
         }
 
         // zero args
@@ -441,7 +439,7 @@
             "org.jetbrains.kotlin.kapt3",
             listOf("-P", "plugin:org.jetbrains.kotlin.kapt3:correctErrorTypes=true", "-verbose")
         ).let { options ->
-            assertThat(options).containsExactly("correctErrorTypes" to "true")
+            assertThat(options).containsExactly("correctErrorTypes", "true")
         }
 
         // illegal format (missing "=")
@@ -476,8 +474,8 @@
             )
         ).let { options ->
             assertThat(options).containsExactly(
-                "correctErrorTypes" to "true",
-                "sources" to "build/kapt/sources"
+                "correctErrorTypes", "true",
+                "sources", "build/kapt/sources"
             )
         }
     }
diff --git a/room/room-compiler-processing/build.gradle b/room/room-compiler-processing/build.gradle
index 0e71dd2..2b4f78a2 100644
--- a/room/room-compiler-processing/build.gradle
+++ b/room/room-compiler-processing/build.gradle
@@ -84,6 +84,7 @@
     testImplementation(libs.jsr250)
     testImplementation(libs.ksp)
     testImplementation(libs.kotlinMetadataJvm)
+    testImplementation(libs.testParameterInjector)
     testImplementation(project(":room:room-compiler-processing-testing"))
     testImplementation(project(":internal-testutils-common"))
     testImplementation(project(":kruth:kruth"))
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XTypeName.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XTypeName.kt
index f4a145b..c76af83 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XTypeName.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XTypeName.kt
@@ -38,10 +38,12 @@
 import com.squareup.kotlinpoet.javapoet.JClassName
 import com.squareup.kotlinpoet.javapoet.JParameterizedTypeName
 import com.squareup.kotlinpoet.javapoet.JTypeName
+import com.squareup.kotlinpoet.javapoet.JTypeVariableName
 import com.squareup.kotlinpoet.javapoet.JWildcardTypeName
 import com.squareup.kotlinpoet.javapoet.KClassName
 import com.squareup.kotlinpoet.javapoet.KParameterizedTypeName
 import com.squareup.kotlinpoet.javapoet.KTypeName
+import com.squareup.kotlinpoet.javapoet.KTypeVariableName
 import com.squareup.kotlinpoet.javapoet.KWildcardTypeName
 import kotlin.reflect.KClass
 
@@ -257,6 +259,16 @@
                 }
             )
         }
+
+        /**
+         * Creates a type variable named with bounds.
+         */
+        fun getTypeVariableName(name: String, bounds: List<XTypeName> = emptyList()): XTypeName {
+            return XTypeName(
+                java = JTypeVariableName.get(name, *bounds.map { it.java }.toTypedArray()),
+                kotlin = KTypeVariableName(name, bounds.map { it.kotlin })
+            )
+        }
     }
 }
 
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/JavaPoetExt.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/JavaPoetExt.kt
index fa7b4d7..08e61c1 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/JavaPoetExt.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/JavaPoetExt.kt
@@ -161,9 +161,13 @@
      * * thrown types are copied if the backing element is from java
      */
     @JvmStatic
+    @JvmOverloads
     fun overriding(
         elm: XMethodElement,
-        owner: XType
+        owner: XType =
+            checkNotNull(elm.enclosingElement.type) {
+                "Cannot override method without enclosing class"
+            }
     ): MethodSpec.Builder {
         val asMember = elm.asMemberOf(owner)
         return overriding(
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XConstructorElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XConstructorElement.kt
index 07d4bd2..1a58c12 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XConstructorElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XConstructorElement.kt
@@ -48,4 +48,9 @@
      * subclass ([other]) where type arguments are specified to actual types.
      */
     override fun asMemberOf(other: XType): XConstructorType
+
+    /**
+     * Denotes if this is a synthetic constructor generated via the usage of @JvmOverloads
+     */
+    fun isSyntheticConstructorForJvmOverloads(): Boolean
 }
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XTypeElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XTypeElement.kt
index 0023e78..d0d6a55 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XTypeElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XTypeElement.kt
@@ -189,7 +189,10 @@
     }
 
     /**
-     * Returns the list of constructors in this type element
+     * Returns the list of constructors in this type element.
+     *
+     * May return synthetic constructors in KSP due to @JvmOverloads. You may filter out synthetic
+     * constructors with XConstructorElement#isSyntheticConstructorFromJvmOverloads
      */
     fun getConstructors(): List<XConstructorElement>
 
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/compat/XConverters.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/compat/XConverters.kt
index 814848a..843368a 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/compat/XConverters.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/compat/XConverters.kt
@@ -43,6 +43,8 @@
 import androidx.room.compiler.processing.javac.JavacType
 import androidx.room.compiler.processing.javac.JavacTypeElement
 import androidx.room.compiler.processing.javac.JavacVariableElement
+import androidx.room.compiler.processing.ksp.KSClassDeclarationAsOriginatingElement
+import androidx.room.compiler.processing.ksp.KSFileAsOriginatingElement
 import androidx.room.compiler.processing.ksp.KspAnnotation
 import androidx.room.compiler.processing.ksp.KspAnnotationValue
 import androidx.room.compiler.processing.ksp.KspElement
@@ -123,6 +125,10 @@
             is TypeElement -> this.toXProcessing(env)
             is ExecutableElement -> this.toXProcessing(env)
             is VariableElement -> this.toXProcessing(env)
+            is KSFileAsOriginatingElement ->
+                (env as KspProcessingEnv).wrapKSFile(this.ksFile)
+            is KSClassDeclarationAsOriginatingElement ->
+                (env as KspProcessingEnv).wrapClassDeclaration(this.ksClassDeclaration)
             else -> error(
                 "Don't know how to convert element of type '${this::class}' to a XElement"
             )
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacConstructorElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacConstructorElement.kt
index e3dda74..0d041a0 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacConstructorElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacConstructorElement.kt
@@ -35,6 +35,9 @@
             "Constructor element is constructed with invalid type: $element"
         }
     }
+
+    override fun isSyntheticConstructorForJvmOverloads() = false
+
     override val name: String
         get() = "<init>"
 
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacTypeElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacTypeElement.kt
index b43c29d..5d3d6e4 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacTypeElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacTypeElement.kt
@@ -196,9 +196,9 @@
     }
 
     override fun getDeclaredMethods(): List<JavacMethodElement> {
-        return _declaredMethods.filter {
-            it.kotlinMetadata?.isSyntheticMethodForAnnotations() != true
-        }
+        // TODO(b/290800523): Remove the synthetic annotations method from the list
+        //  of declared methods so that KAPT matches KSP.
+        return _declaredMethods
     }
 
     fun getSyntheticMethodsForAnnotations(): List<JavacMethodElement> {
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/JvmDescriptorUtils.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/JvmDescriptorUtils.kt
index f7dae3e..402ab9e 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/JvmDescriptorUtils.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/JvmDescriptorUtils.kt
@@ -169,12 +169,26 @@
                     NestingKind.MEMBER, NestingKind.LOCAL ->
                         enclosingElement.internalName + "$" + simpleName
                     NestingKind.ANONYMOUS ->
-                        error("Unsupported nesting $nestingKind")
+                        elementError("Unsupported nesting $nestingKind", this)
                     else ->
-                        error("Unsupported, nestingKind == null")
+                        elementError("Unsupported, nestingKind == null", this)
                 }
             is ExecutableElement -> enclosingElement.internalName
             is QualifiedNameable -> qualifiedName.toString().replace('.', '/')
             else -> simpleName.toString()
         }
+
+    /**
+     * Throws an exception with the error [msg] and the [element] and its enclosing elements appended.
+     */
+    private fun elementError(msg: String, element: Element): Nothing {
+        fun buildName(element: Element): String {
+            val enclosingPart =
+                element.enclosingElement?.let { buildName(it) + "." } ?: ""
+            val simpleName = element.simpleName.ifEmpty { "<unnamed>" }
+            return enclosingPart + simpleName
+        }
+        val name = buildName(element)
+        error("$msg - On element $name")
+    }
 }
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSFunctionExt.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSFunctionExt.kt
index 21c5420..9e5fa7d 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSFunctionExt.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSFunctionExt.kt
@@ -22,6 +22,10 @@
 import com.google.devtools.ksp.symbol.KSPropertyDeclaration
 import com.google.devtools.ksp.symbol.KSTypeReference
 
+internal fun KSFunctionDeclaration.hasOverloads() = this.annotations.any {
+    it.annotationType.resolve().declaration.qualifiedName?.asString() == "kotlin.jvm.JvmOverloads"
+}
+
 /**
  * A custom ReturnType that return an [XType] while also resolving boxing if necessary (might happen
  * due to overrides).
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolver.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolver.kt
index 94cd21f..58eb2a6 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolver.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolver.kt
@@ -159,6 +159,8 @@
         // using "asMemberOf", then it has special rules about how to resolve the types.
         getJavaWildcardWithTypeVariables(
             declarationType = KSTypeWrapper(resolver, scope.declarationType())
+                .replaceTypeAliases()
+                .replaceSuspendFunctionTypes()
                 .getJavaWildcard(scope),
             scope = scope,
         )
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotation.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotation.kt
index 13ace16..2012535 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotation.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspAnnotation.kt
@@ -51,26 +51,27 @@
     }
 
     override val annotationValues: List<XAnnotationValue> by lazy {
-        // In KSP the annotation members may be represented by constructor parameters in kotlin
-        // source or by abstract methods in java source so we check both.
-        val declarationConstructors = typeElement.let {
-            // We access constructor using declaration since for compatibility with KAPT,
-            // XTypeElement.getConstructors() will return an empty list for annotation classes.
-            check(it is KspTypeElement)
-            it.declaration.getConstructors().map {
-                KspConstructorElement(
-                    env = env,
-                    declaration = it
-                )
+        // Whether the annotation value is being treated as property or abstract method depends on
+        // the actual usage of the annotation. If the annotation is being used on Java source, then
+        // the annotation value will have a corresponding method element, otherwise, it will become
+        // a kotlin property.
+        val typesByName =
+            buildMap {
+                typeElement.getDeclaredMethods()
+                    .filter {
+                        if ((typeElement as KspTypeElement).declaration
+                                .getConstructors()
+                                .single().parameters
+                                .isNotEmpty()) {
+                            it.isKotlinPropertyMethod()
+                        } else {
+                            it.isAbstract()
+                        }
+                    }.forEach {
+                        put(it.name, it.returnType)
+                        put(it.jvmName, it.returnType)
+                    }
             }
-        }
-        val typesByName = if (declarationConstructors.single().parameters.isNotEmpty()) {
-            declarationConstructors.single().parameters.associate { it.name to it.type }
-        } else {
-            typeElement.getDeclaredMethods()
-                .filter { it.isAbstract() }
-                .associate { it.name to it.returnType }
-        }
         // KSAnnotated.arguments isn't guaranteed to have the same ordering as declared in the
         // annotation declaration, so we order it manually using a map from name to index.
         val indexByName = typesByName.keys.mapIndexed { index, name -> name to index }.toMap()
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspConstructorElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspConstructorElement.kt
index 4a0275a..05c6aa8 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspConstructorElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspConstructorElement.kt
@@ -23,7 +23,7 @@
 import androidx.room.compiler.processing.XType
 import com.google.devtools.ksp.symbol.KSFunctionDeclaration
 
-internal class KspConstructorElement(
+internal open class KspConstructorElement(
     env: KspProcessingEnv,
     declaration: KSFunctionDeclaration
 ) : KspExecutableElement(env, declaration),
@@ -33,6 +33,9 @@
         filter = KspAnnotated.UseSiteFilter.NO_USE_SITE_OR_CONSTRUCTOR
     ),
     XConstructorElement {
+
+    override fun isSyntheticConstructorForJvmOverloads() = false
+
     override val name: String
         get() = "<init>"
 
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
index 63b4238..7228e79 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
@@ -33,7 +33,7 @@
 import androidx.room.compiler.processing.collectFieldsIncludingPrivateSupers
 import androidx.room.compiler.processing.filterMethodsByConfig
 import androidx.room.compiler.processing.ksp.KspAnnotated.UseSiteFilter.Companion.NO_USE_SITE
-import androidx.room.compiler.processing.ksp.synthetic.KspSyntheticNoArgConstructorElement
+import androidx.room.compiler.processing.ksp.synthetic.KspSyntheticConstructorElement
 import androidx.room.compiler.processing.ksp.synthetic.KspSyntheticPropertyMethodElement
 import androidx.room.compiler.processing.tryBox
 import androidx.room.compiler.processing.util.MemoizedSequence
@@ -46,7 +46,10 @@
 import com.google.devtools.ksp.symbol.KSDeclarationContainer
 import com.google.devtools.ksp.symbol.KSFunctionDeclaration
 import com.google.devtools.ksp.symbol.KSPropertyDeclaration
+import com.google.devtools.ksp.symbol.KSValueParameter
 import com.google.devtools.ksp.symbol.Modifier
+import com.google.devtools.ksp.symbol.Origin.JAVA_LIB
+import com.google.devtools.ksp.symbol.Origin.KOTLIN_LIB
 import com.squareup.javapoet.ClassName
 import com.squareup.kotlinpoet.javapoet.JClassName
 import com.squareup.kotlinpoet.javapoet.KClassName
@@ -313,25 +316,32 @@
         if (isAnnotationClass()) {
             return emptyList()
         }
+        val constructors = declaration.getConstructors().toList()
+
         return buildList {
             addAll(
-                declaration.getConstructors().map {
+                constructors.map {
                     KspConstructorElement(
                         env = env,
                         declaration = it
                     )
                 }
             )
-            // Too match KAPT if all params in the primary constructor have default values then
+            constructors
+                .filter { it.hasOverloads() }
+                .forEach { addAll(enumerateSyntheticConstructors(it)) }
+
+            // To match KAPT if all params in the primary constructor have default values then
             // synthesize a no-arg constructor if one is not already present.
-            val hasNoArgConstructor = declaration.getConstructors().any { it.parameters.isEmpty() }
+            val hasNoArgConstructor = constructors.any { it.parameters.isEmpty() }
             if (!hasNoArgConstructor) {
                 declaration.primaryConstructor?.let {
-                    if (it.parameters.all { it.hasDefault }) {
+                    if (!it.hasOverloads() && it.parameters.all { it.hasDefault }) {
                         add(
-                            KspSyntheticNoArgConstructorElement(
+                            KspSyntheticConstructorElement(
                                 env = env,
-                                declaration = it
+                                declaration = it,
+                                valueParameters = emptyList()
                             )
                         )
                     }
@@ -340,6 +350,32 @@
         }
     }
 
+    private fun enumerateSyntheticConstructors(
+        declaration: KSFunctionDeclaration
+    ): List<KspSyntheticConstructorElement> {
+        val parameters = declaration.parameters
+        val defaultParamsCount = parameters.count { it.hasDefault }
+        if (defaultParamsCount < 1) { return emptyList() }
+        val constructorEnumeration = mutableListOf<KspSyntheticConstructorElement>()
+        for (defaultParameterToUseCount in 0..defaultParamsCount - 1) {
+            val parameterEnumeration = mutableListOf<KSValueParameter>()
+            var defaultParameterUsedCount = 0
+            for (parameter in parameters) {
+                if (parameter.hasDefault) {
+                    if (defaultParameterUsedCount++ >= defaultParameterToUseCount) {
+                      continue
+                    }
+                }
+                parameterEnumeration.add(parameter)
+            }
+            constructorEnumeration.add(
+                KspSyntheticConstructorElement(env, declaration, parameterEnumeration))
+        }
+        val isPreCompiled =
+            declaration.origin == KOTLIN_LIB || declaration.origin == JAVA_LIB
+        return if (isPreCompiled) constructorEnumeration.reversed() else constructorEnumeration
+    }
+
     override fun getSuperInterfaceElements(): List<XTypeElement> {
         return declaration.superTypes
             .mapNotNull { it.resolve().declaration }
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticConstructorElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticConstructorElement.kt
new file mode 100644
index 0000000..11f749f
--- /dev/null
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticConstructorElement.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.compiler.processing.ksp.synthetic
+
+import androidx.room.compiler.processing.XExecutableParameterElement
+import androidx.room.compiler.processing.ksp.KspConstructorElement
+import androidx.room.compiler.processing.ksp.KspExecutableParameterElement
+import androidx.room.compiler.processing.ksp.KspProcessingEnv
+import com.google.devtools.ksp.symbol.KSFunctionDeclaration
+import com.google.devtools.ksp.symbol.KSValueParameter
+
+/**
+ * A synthetic element the represents and overload of constructor that exist
+ * in kapt, but not in ksp.
+ */
+internal class KspSyntheticConstructorElement(
+    env: KspProcessingEnv,
+    declaration: KSFunctionDeclaration,
+    valueParameters: List<KSValueParameter>
+) : KspConstructorElement(env, declaration) {
+
+    override fun isSyntheticConstructorForJvmOverloads() = true
+
+    override val parameters: List<XExecutableParameterElement> by lazy {
+        valueParameters.mapIndexed { index, param ->
+            KspExecutableParameterElement(
+                env = env,
+                enclosingElement = this,
+                parameter = param,
+                parameterIndex = index
+            )
+        }
+    }
+}
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticNoArgConstructorElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticNoArgConstructorElement.kt
deleted file mode 100644
index 037953e..0000000
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticNoArgConstructorElement.kt
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.room.compiler.processing.ksp.synthetic
-
-import androidx.room.compiler.processing.XAnnotated
-import androidx.room.compiler.processing.XConstructorElement
-import androidx.room.compiler.processing.XConstructorType
-import androidx.room.compiler.processing.XExecutableParameterElement
-import androidx.room.compiler.processing.XType
-import androidx.room.compiler.processing.ksp.KspAnnotated
-import androidx.room.compiler.processing.ksp.KspConstructorType
-import androidx.room.compiler.processing.ksp.KspExecutableElement
-import androidx.room.compiler.processing.ksp.KspProcessingEnv
-import androidx.room.compiler.processing.ksp.KspType
-import androidx.room.compiler.processing.ksp.KspTypeElement
-import androidx.room.compiler.processing.ksp.requireEnclosingMemberContainer
-import com.google.devtools.ksp.symbol.KSFunctionDeclaration
-
-/**
- * Represents the no-arg constructor of a type element for which all parameters in the primary
- * constructor have default values.
- */
-internal class KspSyntheticNoArgConstructorElement(
-    env: KspProcessingEnv,
-    declaration: KSFunctionDeclaration
-) : KspExecutableElement(env, declaration),
-    XAnnotated by KspAnnotated.create(
-        env = env,
-        delegate = declaration,
-        filter = KspAnnotated.UseSiteFilter.NO_USE_SITE_OR_CONSTRUCTOR
-    ),
-    XConstructorElement {
-
-    override val enclosingElement: KspTypeElement by lazy {
-        declaration.requireEnclosingMemberContainer(env) as? KspTypeElement
-            ?: error("Constructor parent must be a type element $this")
-    }
-
-    override val name: String
-        get() = "<init>"
-
-    override val parameters: List<XExecutableParameterElement>
-        get() = emptyList()
-
-    override val executableType: XConstructorType by lazy {
-        KspConstructorType(
-            env = env,
-            origin = this,
-            containing = this.enclosingElement.type
-        )
-    }
-
-    override fun asMemberOf(other: XType): XConstructorType {
-        check(other is KspType)
-        return KspConstructorType(
-            env = env,
-            origin = this,
-            containing = other
-        )
-    }
-}
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/MethodSpecHelperTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/MethodSpecHelperTest.kt
index c427a9b..eee3dd6 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/MethodSpecHelperTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/MethodSpecHelperTest.kt
@@ -43,7 +43,8 @@
     // if true, pre-compile sources then run the test to account for changes between .class files
     // and source files
     val preCompiledCode: Boolean,
-    val shouldMarkParamsFinal: Boolean
+    val shouldMarkParamsFinal: Boolean,
+    val ignoreOwner: Boolean
 ) {
     @Test
     fun javaOverrides() {
@@ -460,17 +461,15 @@
         ) { invocation ->
             val (target, methods) = invocation.getOverrideTestTargets(ignoreInheritedMethods)
             methods.forEachIndexed { index, method ->
-                val func = if (shouldMarkParamsFinal)
-                    MethodSpecHelper::overridingWithFinalParams
-                else
-                    MethodSpecHelper::overriding
+                val subject =
+                    if (ignoreOwner)
+                        MethodSpecHelper.overriding(method)
+                    else if (shouldMarkParamsFinal)
+                        MethodSpecHelper.overridingWithFinalParams(method, target.type)
+                    else
+                        MethodSpecHelper.overriding(method, target.type)
 
-                val subject = func(
-                    method,
-                    target.type
-                ).toSignature()
-
-                assertThat(subject).isEqualTo(golden[index])
+                assertThat(subject.toSignature()).isEqualTo(golden[index])
             }
         }
     }
@@ -573,6 +572,9 @@
         owner: DeclaredType,
         typeUtils: Types
     ): MethodSpec.Builder {
+        if (ignoreOwner) {
+          return MethodSpec.overriding(elm)
+        }
         val baseSpec = MethodSpec.overriding(elm, owner, typeUtils)
             .build()
 
@@ -600,7 +602,10 @@
 
     companion object {
         @JvmStatic
-        @Parameterized.Parameters(name = "preCompiledCode={0}, shouldMarkParamsFinal={1}")
-        fun params() = generateAllEnumerations(listOf(false, true), listOf(false, true))
+        @Parameterized.Parameters(
+            name = "preCompiledCode={0}, shouldMarkParamsFinal={1}, ignoreOwner={2}")
+        fun params() =
+            generateAllEnumerations(
+                listOf(false, true), listOf(false, true), listOf(false, true))
     }
 }
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt
index 4cf7845..2953253 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationTest.kt
@@ -92,6 +92,101 @@
     }
 
     @Test
+    fun typeParameterAnnotationsOnFunction() {
+        val kotlinSource = Source.kotlin(
+            "foo.bar.Subject.kt",
+            """
+            package foo.bar
+            import kotlin.collections.*
+
+            @Target(AnnotationTarget.TYPE)
+            annotation class SomeAnnotation(val value: String)
+
+            class Subject {
+                fun myFunction(): Map<@SomeAnnotation("someString") Int, Int> {
+                    return emptyMap()
+                }
+            }
+            """.trimIndent()
+        )
+        val javaSource = Source.java(
+            "foo.bar.Subject",
+            """
+            package foo.bar;
+            import java.lang.annotation.ElementType;
+            import java.lang.annotation.Target;
+            import java.util.Map;
+            import java.util.HashMap;
+
+            @Target(ElementType.TYPE_USE)
+            @interface SomeAnnotation {
+                String value();
+            }
+
+            class Subject {
+                Map<@SomeAnnotation("someString") Integer, Integer> myFunction() {
+                    return new HashMap<>();
+                }
+            }
+            """.trimIndent()
+        )
+
+        listOf(javaSource, kotlinSource).forEach { source ->
+            runTest(
+                sources = listOf(source)
+            ) { invocation ->
+                if (!invocation.isKsp) return@runTest
+                val subject = invocation.processingEnv.requireTypeElement(
+                    "foo.bar.Subject")
+                val method = subject.getMethodByJvmName("myFunction")
+                val firstArg = method.returnType.typeArguments.first()
+                val annotation = firstArg.getAllAnnotations().first()
+                assertThat(
+                    annotation.name
+                ).isEqualTo("SomeAnnotation")
+
+                assertThat(
+                    annotation.annotationValues.first().value
+                ).isEqualTo("someString")
+            }
+        }
+    }
+
+    @Test
+    fun testJvmNameAnnotationValue() {
+        val kotlinSrc = Source.kotlin(
+            "MyAnnotation.kt",
+            """
+            @Target(AnnotationTarget.CLASS)
+            annotation class MyAnnotation(
+                @get:JvmName("stringParameter")
+                val stringParam: String,
+                val intParam: Int,
+                @get:JvmName("longParameter")
+                val longParam: Long
+            )
+            """.trimIndent()
+        )
+        val javaSrc = Source.java(
+            "Foo",
+            """
+            @MyAnnotation(stringParameter = "1", intParam = 2, longParameter = 3)
+            public class Foo {}
+            """.trimIndent()
+        )
+        runTest(sources = listOf(javaSrc, kotlinSrc)) { invocation ->
+            val typeElement = invocation.processingEnv.requireTypeElement("Foo")
+            val annotation =
+                typeElement.getAllAnnotations().single { it.qualifiedName == "MyAnnotation" }
+            assertThat(
+                annotation.annotationValues.map { it.value }
+            ).containsExactly(
+                "1", 2, 3.toLong()
+            ).inOrder()
+        }
+    }
+
+    @Test
     fun readsAnnotationsDeclaredInSources() {
         val source = Source.kotlin(
             "MyClass.kt",
@@ -1263,9 +1358,17 @@
                     assertThat(subject.getDeclaredMethods().map { it.name })
                         .containsExactly("method")
                 } else {
-                    assertThat(subject.getDeclaredMethods().map { it.name })
-                        .containsExactly("getField", "method")
-                        .inOrder()
+                    if (invocation.isKsp || preCompiled) {
+                        assertThat(subject.getDeclaredMethods().map { it.name })
+                            .containsExactly("getField", "method")
+                            .inOrder()
+                    } else {
+                        // TODO(b/290800523): Remove the synthetic annotations method from the list
+                        //  of declared methods so that KAPT matches KSP.
+                        assertThat(subject.getDeclaredMethods().map { it.name })
+                            .containsExactly("getField", "getField\$annotations", "method")
+                            .inOrder()
+                    }
                 }
 
                 // Check the annotations on the elements
@@ -1400,6 +1503,86 @@
         }
     }
 
+    @Test
+    fun typeParameterAnnotations() {
+        val kotlinSource = Source.kotlin(
+            "foo.bar.Subject.kt",
+            """
+            package foo.bar
+
+            @Target(AnnotationTarget.TYPE_PARAMETER)
+            annotation class A(val value: Int)
+
+            class Subject<@A(42) T>
+            """.trimIndent()
+        )
+        val javaSource = Source.java(
+            "foo.bar.Subject",
+            """
+            package foo.bar;
+            import java.lang.annotation.ElementType;
+            import java.lang.annotation.Target;
+            import java.lang.annotation.Repeatable;
+
+            @Target(ElementType.TYPE_PARAMETER)
+            @interface A {
+                int value();
+            }
+
+            class Subject<@A(42) T> {}
+            """.trimIndent()
+        )
+
+        fun test(invocation: XTestInvocation) {
+            val subject = invocation.processingEnv.requireTypeElement(
+                "foo.bar.Subject")
+            assertThat(
+                subject.typeParameters.first().getAllAnnotations().first().name
+            ).isEqualTo("A")
+
+            assertThat(
+                subject.typeParameters.first().getAllAnnotations()
+                    .first().get("value") as Int
+            ).isEqualTo(42)
+        }
+
+        listOf(javaSource, kotlinSource).forEach { source ->
+            runTest(
+                sources = listOf(source)
+            ) { invocation ->
+                if (invocation.isKsp) { // doesn't work
+                    if (source === javaSource) {
+                        if (preCompiled) {
+                            // test(invocation)
+                        } else {
+                            // test(invocation)
+                        }
+                    } else {
+                        if (preCompiled) {
+                            // test(invocation)
+                        } else {
+                            // test(invocation)
+                        }
+                    }
+                } else {
+                    if (source === javaSource) {
+                        if (preCompiled) {
+                            test(invocation)
+                        } else {
+                            test(invocation)
+                        }
+                    } else {
+                        if (preCompiled) {
+                            test(invocation)
+                        } else {
+                            // test(invocation) // doesn't work
+                        }
+                    }
+                }
+            }
+        }
+    }
+
     // helper function to read what we need
     private fun XAnnotated.getSuppressValues(): List<String>? {
         return this.findAnnotation<TestSuppressWarnings>()
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationValueTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationValueTest.kt
index 656c3c5..866159c 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationValueTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XAnnotationValueTest.kt
@@ -1161,8 +1161,8 @@
                 """.trimIndent()
             ) as Source.KotlinSource
         ) { invocation ->
-            val myEnumJTypeName = JClassName.get("", "test.MyEnum")
-            val myEnumKTypeName = KClassName("", "test.MyEnum")
+            val myEnumJTypeName = JClassName.get("test", "MyEnum")
+            val myEnumKTypeName = KClassName("test", "MyEnum")
 
             fun checkSingleValue(annotationValue: XAnnotationValue, expectedValue: String) {
                 assertThat(annotationValue.valueType.asTypeName().java)
@@ -1517,8 +1517,8 @@
                 """.trimIndent()
             ) as Source.KotlinSource
         ) { invocation ->
-            val aJTypeName = JClassName.get("", "test.A")
-            val aKTypeName = KClassName("", "test.A")
+            val aJTypeName = JClassName.get("test", "A")
+            val aKTypeName = KClassName("test", "A")
 
             fun checkSingleValue(annotationValue: XAnnotationValue, expectedValue: String) {
                 assertThat(annotationValue.valueType.asTypeName().java)
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt
index c61d4e3..1763a8b 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XElementTest.kt
@@ -30,7 +30,6 @@
 import androidx.room.compiler.processing.util.XTestInvocation
 import androidx.room.compiler.processing.util.asJClassName
 import androidx.room.compiler.processing.util.compileFiles
-import androidx.room.compiler.processing.util.createXTypeVariableName
 import androidx.room.compiler.processing.util.getField
 import androidx.room.compiler.processing.util.getMethodByJvmName
 import androidx.room.compiler.processing.util.getParameter
@@ -244,24 +243,24 @@
 
             validateMethodElement(
                 element = it.processingEnv.requireTypeElement("foo.bar.Base"),
-                tTypeName = createXTypeVariableName("T"),
-                rTypeName = createXTypeVariableName("R")
+                tTypeName = XTypeName.getTypeVariableName("T", listOf(XTypeName.ANY_OBJECT)),
+                rTypeName = XTypeName.getTypeVariableName("R", listOf(XTypeName.ANY_OBJECT))
             )
             validateMethodElement(
                 element = it.processingEnv.requireTypeElement("foo.bar.Child"),
-                tTypeName = createXTypeVariableName("T"),
-                rTypeName = createXTypeVariableName("R")
+                tTypeName = XTypeName.getTypeVariableName("T", listOf(XTypeName.ANY_OBJECT)),
+                rTypeName = XTypeName.getTypeVariableName("R", listOf(XTypeName.ANY_OBJECT))
             )
 
             validateMethodTypeAsMemberOf(
                 element = it.processingEnv.requireTypeElement("foo.bar.Base"),
-                tTypeName = createXTypeVariableName("T"),
-                rTypeName = createXTypeVariableName("R")
+                tTypeName = XTypeName.getTypeVariableName("T", listOf(XTypeName.ANY_OBJECT)),
+                rTypeName = XTypeName.getTypeVariableName("R", listOf(XTypeName.ANY_OBJECT))
             )
             validateMethodTypeAsMemberOf(
                 element = it.processingEnv.requireTypeElement("foo.bar.Child"),
                 tTypeName = String::class.asClassName(),
-                rTypeName = createXTypeVariableName("R")
+                rTypeName = XTypeName.getTypeVariableName("R", listOf(XTypeName.ANY_OBJECT))
             )
         }
     }
@@ -825,7 +824,7 @@
                 }
             }
             """.trimIndent()
-        ))) { invocation, _ ->
+        ))) { invocation, precompiled ->
             val enclosingElement =
                 invocation.processingEnv.requireTypeElement("foo.bar.KotlinClass")
             val companionObj = enclosingElement.getEnclosedTypeElements().first {
@@ -897,14 +896,28 @@
                 methods.forEach {
                     assertThat(it.enclosingElement).isEqualTo(companionObj)
                 }
-                assertThat(methods.map { it.name }).containsExactly(
-                    "getCompanionObjectProperty",
-                    "getCompanionObjectPropertyJvmStatic",
-                    "getCompanionObjectPropertyLateinit",
-                    "setCompanionObjectPropertyLateinit",
-                    "companionObjectFunction",
-                    "companionObjectFunctionJvmStatic"
-                ).inOrder()
+                if (invocation.isKsp || precompiled) {
+                    assertThat(methods.map { it.name }).containsExactly(
+                        "getCompanionObjectProperty",
+                        "getCompanionObjectPropertyJvmStatic",
+                        "getCompanionObjectPropertyLateinit",
+                        "setCompanionObjectPropertyLateinit",
+                        "companionObjectFunction",
+                        "companionObjectFunctionJvmStatic"
+                    ).inOrder()
+                } else {
+                    // TODO(b/290800523): Remove the synthetic annotations method from the list
+                    //  of declared methods so that KAPT matches KSP.
+                    assertThat(methods.map { it.name }).containsExactly(
+                        "getCompanionObjectProperty",
+                        "getCompanionObjectPropertyJvmStatic",
+                        "getCompanionObjectPropertyJvmStatic\$annotations",
+                        "getCompanionObjectPropertyLateinit",
+                        "setCompanionObjectPropertyLateinit",
+                        "companionObjectFunction",
+                        "companionObjectFunctionJvmStatic"
+                    ).inOrder()
+                }
             }
         }
     }
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
index a5d61f1..db2aa22 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
@@ -25,12 +25,13 @@
 import androidx.room.compiler.processing.util.UNIT_JCLASS_NAME
 import androidx.room.compiler.processing.util.className
 import androidx.room.compiler.processing.util.compileFiles
-import androidx.room.compiler.processing.util.createXTypeVariableName
 import androidx.room.compiler.processing.util.getDeclaredMethodByJvmName
 import androidx.room.compiler.processing.util.getMethodByJvmName
 import androidx.room.compiler.processing.util.getParameter
 import androidx.room.compiler.processing.util.runProcessorTest
 import androidx.room.compiler.processing.util.typeName
+import com.google.testing.junit.testparameterinjector.TestParameter
+import com.google.testing.junit.testparameterinjector.TestParameterInjector
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.ParameterizedTypeName
 import com.squareup.javapoet.TypeName
@@ -47,9 +48,8 @@
 import java.io.IOException
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
-@RunWith(JUnit4::class)
+@RunWith(TestParameterInjector::class)
 class XExecutableElementTest {
     @Test
     fun basic() {
@@ -1047,7 +1047,7 @@
                 }
                 element.getDeclaredMethodByJvmName("ext5").let { method ->
                     assertThat(method.parameters[0].type.asTypeName())
-                        .isEqualTo(createXTypeVariableName("T"))
+                        .isEqualTo(XTypeName.getTypeVariableName("T"))
                 }
                 element.getDeclaredMethodByJvmName("ext6").let { method ->
                     assertThat(method.isSuspendFunction()).isTrue()
@@ -1068,7 +1068,7 @@
                     assertThat(method.isAbstract()).isTrue()
                     assertThat(method.isExtensionFunction()).isTrue()
                     assertThat(method.parameters[0].type.asTypeName())
-                        .isEqualTo(createXTypeVariableName("T"))
+                        .isEqualTo(XTypeName.getTypeVariableName("T"))
 
                     val fooImpl = it.processingEnv.requireTypeElement("$pkg.FooImpl")
                     assertThat(method.parameters[0].asMemberOf(fooImpl.type).asTypeName())
@@ -1348,4 +1348,85 @@
             }
         }
     }
+
+    @Test
+    fun parameterNames(
+        @TestParameter isJava: Boolean,
+        @TestParameter isPrecompiled: Boolean,
+        @TestParameter hasParametersFlag: Boolean,
+        @TestParameter hasDebugFlag: Boolean
+    ) {
+        val javaSource = Source.java(
+            "foo.bar.Baz",
+            """
+            package foo.bar;
+            public class Baz {
+                private Baz(String param1) {}
+            }
+            """.trimIndent())
+        val kotlinSource = Source.kotlin(
+            "foo.bar.Baz.kt",
+            """
+            package foo.bar
+            class Baz private constructor(param1: String)
+            """.trimIndent())
+
+        val sources: List<Source> =
+            if (isPrecompiled) {
+                emptyList()
+            } else {
+                if (isJava) {
+                    listOf(javaSource)
+                } else {
+                    listOf(kotlinSource)
+                }
+            }
+        val javacArgs = buildList {
+            if (hasParametersFlag) {
+                // This is used to generate `MethodParameters` in class files
+                add("-parameters")
+            }
+            if (hasDebugFlag) {
+                // This is used to generate `LocalVariableTable` in class files
+                add("-g:vars")
+            }
+        }
+        val classes: List<File> =
+            if (isPrecompiled) {
+                if (isJava) {
+                    compileFiles(listOf(javaSource), javacArguments = javacArgs)
+                } else {
+                    compileFiles(listOf(kotlinSource), javacArguments = javacArgs)
+                }
+            } else {
+                emptyList()
+            }
+        runProcessorTest(sources = sources, classpath = classes) {
+            val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
+            assertThat(element.getConstructors().single().parameters.single().name)
+                .isEqualTo(
+                    if (isJava) {
+                        if (isPrecompiled) {
+                            if (hasParametersFlag) {
+                                "param1"
+                            } else {
+                                if (it.isKsp) {
+                                    "p0"
+                                } else { // Javac/KAPT
+                                    if (hasDebugFlag) {
+                                        "param1"
+                                    } else {
+                                        "arg0"
+                                    }
+                                }
+                            }
+                        } else { // Java sources
+                            "param1"
+                        }
+                    } else { // Kotlin sources or classes
+                        "param1"
+                    }
+                )
+        }
+    }
 }
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
index 7f2906b..b69db6e 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
@@ -27,7 +27,6 @@
 import androidx.room.compiler.processing.util.asKClassName
 import androidx.room.compiler.processing.util.asMutableKClassName
 import androidx.room.compiler.processing.util.compileFiles
-import androidx.room.compiler.processing.util.createXTypeVariableName
 import androidx.room.compiler.processing.util.getAllFieldNames
 import androidx.room.compiler.processing.util.getDeclaredField
 import androidx.room.compiler.processing.util.getField
@@ -41,6 +40,7 @@
 import com.squareup.kotlinpoet.javapoet.JTypeVariableName
 import com.squareup.kotlinpoet.javapoet.KClassName
 import com.squareup.kotlinpoet.javapoet.KTypeVariableName
+import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
@@ -538,7 +538,7 @@
             }
 
             baseClass.getField("genericProp").let { field ->
-                assertThat(field.type.asTypeName()).isEqualTo(createXTypeVariableName("T"))
+                assertThat(field.type.asTypeName()).isEqualTo(XTypeName.getTypeVariableName("T"))
             }
 
             subClass.getField("genericProp").let { field ->
@@ -1488,6 +1488,83 @@
     }
 
     @Test
+    fun constructorsWithOverloads() {
+        val src = Source.kotlin(
+            "Subject.kt",
+            """
+            class DefaultArgs @JvmOverloads constructor(x:Int = 1, y: Double, z: Long = 1) {}
+            class NoDefaultArgs @JvmOverloads constructor(x:Int, y: Double, z: Long) {}
+            class AllDefaultArgs @JvmOverloads constructor(x:Int = 1, y: Double = 0.0, z: Long = 1) {}
+            """.trimIndent()
+        )
+        runTest(sources = listOf(src)) { invocation ->
+            val defaultArgsConstructors =
+                invocation.processingEnv.requireTypeElement("DefaultArgs")
+                    .getConstructorSignatures()
+            val noDefaultArgsConstructors =
+                invocation.processingEnv.requireTypeElement("NoDefaultArgs")
+                    .getConstructorSignatures()
+            val allDefaultArgsConstructors =
+                invocation.processingEnv.requireTypeElement("AllDefaultArgs")
+                    .getConstructorSignatures()
+
+            if (isPreCompiled) {
+                assertThat(defaultArgsConstructors)
+                    .containsExactly(
+                        "DefaultArgs(int,double,long)",
+                        "DefaultArgs(int,double)",
+                        "DefaultArgs(double)"
+                    ).inOrder()
+                assertThat(noDefaultArgsConstructors)
+                    .containsExactly(
+                        "NoDefaultArgs(int,double,long)"
+                    ).inOrder()
+                assertThat(allDefaultArgsConstructors)
+                    .containsExactly(
+                        "AllDefaultArgs(int,double,long)",
+                        "AllDefaultArgs(int,double)",
+                        "AllDefaultArgs(int)",
+                        "AllDefaultArgs()"
+                    ).inOrder()
+            } else {
+                assertThat(defaultArgsConstructors)
+                    .containsExactly(
+                        "DefaultArgs(int,double,long)",
+                        "DefaultArgs(double)",
+                        "DefaultArgs(int,double)"
+                    ).inOrder()
+                assertThat(noDefaultArgsConstructors)
+                    .containsExactly(
+                        "NoDefaultArgs(int,double,long)"
+                    ).inOrder()
+                assertThat(allDefaultArgsConstructors)
+                    .containsExactly(
+                        "AllDefaultArgs(int,double,long)",
+                        "AllDefaultArgs()",
+                        "AllDefaultArgs(int)",
+                        "AllDefaultArgs(int,double)"
+                    ).inOrder()
+            }
+
+            val subjects = listOf("DefaultArgs", "NoDefaultArgs", "AllDefaultArgs")
+            if (invocation.isKsp) {
+                val syntheticConstructorCounts = subjects.map {
+                    it to invocation.processingEnv.requireTypeElement(it)
+                        .getConstructors()
+                        .filter { it.isSyntheticConstructorForJvmOverloads() }
+                        .size
+                }
+                assertThat(syntheticConstructorCounts)
+                    .containsExactly(
+                        "DefaultArgs" to 2,
+                        "NoDefaultArgs" to 0,
+                        "AllDefaultArgs" to 3,
+                    )
+            }
+        }
+    }
+
+    @Test
     fun constructorsWithDefaultValues() {
         val src = Source.kotlin(
             "Subject.kt",
@@ -1554,6 +1631,7 @@
         }
     }
 
+    @Ignore("b/284452502")
     @Test
     fun jvmDefault() {
         val src = Source.kotlin(
@@ -1831,26 +1909,26 @@
                     method = method,
                     name = "method",
                     enclosingElement = abstractClass,
-                    returnType = createXTypeVariableName("T2"),
+                    returnType = XTypeName.getTypeVariableName("T2"),
                     parameterTypes = arrayOf(
-                        createXTypeVariableName("T1"),
-                        createXTypeVariableName("T2")
+                        XTypeName.getTypeVariableName("T1"),
+                        XTypeName.getTypeVariableName("T2")
                     )
                 )
                 checkMethodType(
                     methodType = method.executableType,
-                    returnType = createXTypeVariableName("T2"),
+                    returnType = XTypeName.getTypeVariableName("T2"),
                     parameterTypes = arrayOf(
-                        createXTypeVariableName("T1"),
-                        createXTypeVariableName("T2")
+                        XTypeName.getTypeVariableName("T1"),
+                        XTypeName.getTypeVariableName("T2")
                     )
                 )
                 checkMethodType(
                     methodType = method.asMemberOf(abstractClass.type),
-                    returnType = createXTypeVariableName("T2"),
+                    returnType = XTypeName.getTypeVariableName("T2"),
                     parameterTypes = arrayOf(
-                        createXTypeVariableName("T1"),
-                        createXTypeVariableName("T2")
+                        XTypeName.getTypeVariableName("T1"),
+                        XTypeName.getTypeVariableName("T2")
                     )
                 )
                 checkMethodType(
@@ -1903,26 +1981,26 @@
                 method = abstractClassMethod,
                 name = "method",
                 enclosingElement = abstractClass,
-                returnType = createXTypeVariableName("T2"),
+                returnType = XTypeName.getTypeVariableName("T2"),
                 parameterTypes = arrayOf(
-                    createXTypeVariableName("T1"),
-                    createXTypeVariableName("T2")
+                    XTypeName.getTypeVariableName("T1"),
+                    XTypeName.getTypeVariableName("T2")
                 )
             )
             checkMethodType(
                 methodType = abstractClassMethod.executableType,
-                returnType = createXTypeVariableName("T2"),
+                returnType = XTypeName.getTypeVariableName("T2"),
                 parameterTypes = arrayOf(
-                    createXTypeVariableName("T1"),
-                    createXTypeVariableName("T2")
+                    XTypeName.getTypeVariableName("T1"),
+                    XTypeName.getTypeVariableName("T2")
                 )
             )
             checkMethodType(
                 methodType = abstractClassMethod.asMemberOf(abstractClass.type),
-                returnType = createXTypeVariableName("T2"),
+                returnType = XTypeName.getTypeVariableName("T2"),
                 parameterTypes = arrayOf(
-                    createXTypeVariableName("T1"),
-                    createXTypeVariableName("T2"),
+                    XTypeName.getTypeVariableName("T1"),
+                    XTypeName.getTypeVariableName("T2"),
                 )
             )
             checkMethodType(
@@ -2010,7 +2088,7 @@
             checkConstructorParameters(
                 typeElement = abstractClass,
                 expectedParameters = arrayOf(
-                    createXTypeVariableName("T")
+                    XTypeName.getTypeVariableName("T")
                 )
             )
             checkConstructorParameters(
@@ -2243,6 +2321,17 @@
         it.jvmName
     }.toList()
 
+    private fun XTypeElement.getConstructorSignatures(): List<String> =
+        getConstructors().map { it.signature() }
+
+    private fun XConstructorElement.signature(): String {
+        val params = executableType.parameterTypes.joinToString(",") {
+            it.asTypeName().java.toString()
+        }
+        val enclosingName = enclosingElement.name
+        return "$enclosingName($params)"
+    }
+
     private fun XMethodElement.signature(owner: XType): String {
         val methodType = this.asMemberOf(owner)
         val params = methodType.parameterTypes.joinToString(",") {
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
index 840d390..1a68ad3 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
@@ -89,7 +89,12 @@
             if (it.isKsp) {
                 assertThat(type.asTypeName().kotlin).isEqualTo(
                     KClassName("foo.bar", "Parent")
-                        .parameterizedBy(KClassName("", "InputStreamType"))
+                        .parameterizedBy(
+                            KTypeVariableName(
+                                "InputStreamType",
+                                KClassName("java.io", "InputStream")
+                            )
+                        )
                 )
             }
 
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeNamesGoldenTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeNamesGoldenTest.kt
index f421d26..a97fc52 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeNamesGoldenTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeNamesGoldenTest.kt
@@ -185,6 +185,8 @@
                 typealias JSW = JvmSuppressWildcards
                 typealias JW = JvmWildcard
                 typealias MyLambdaTypeAlias = (@JvmWildcard MyType) -> @JvmWildcard MyType
+                typealias MyMapAlias<T> = Map<Int, @JvmSuppressWildcards MyGeneric<@JvmSuppressWildcards T>>
+                typealias MyMapAliasWithJSW<T> = Map<Int, @JSW MyGeneric<@JSW T>>
                 @JvmInline value class MyInlineType(val value: MyType)
                 enum class MyEnum {
                     VAL1,
@@ -592,6 +594,18 @@
                     @JSW fun suspendLambda6WithJSW(param: MySuspendLambdaAlias6): MySuspendLambdaAlias6 = TODO()
                     @JSW fun suspendLambda7WithJSW(param: MySuspendLambdaAlias7): MySuspendLambdaAlias7 = TODO()
                     @JSW fun suspendLambda8WithJSW(param: MySuspendLambdaAlias8): MySuspendLambdaAlias8 = TODO()
+                    fun mapTypeAlias1(param: MyMapAlias<R>): MyMapAlias<R> = TODO()
+                    fun mapTypeAlias2(param: MyMapAlias<MyInterface>): MyMapAlias<MyInterface> = TODO()
+                    fun mapTypeAlias3(param: MyGeneric<MyMapAlias<R>>): MyGeneric<MyMapAlias<R>> = TODO()
+                    fun mapTypeAlias4(param: MyGeneric<MyMapAlias<MyInterface>>): MyGeneric<MyMapAlias<MyInterface>> = TODO()
+                    fun mapTypeAlias5(param: MyGeneric<MyMapAlias<MyGeneric<R>>>): MyGeneric<MyMapAlias<MyGeneric<R>>> = TODO()
+                    fun mapTypeAlias6(param: MyGeneric<MyMapAlias<MyGeneric<MyInterface>>>): MyGeneric<MyMapAlias<MyGeneric<MyInterface>>> = TODO()
+                    fun mapTypeAlias7(param: MyMapAliasWithJSW<R>): MyMapAliasWithJSW<R> = TODO()
+                    fun mapTypeAlias8(param: MyMapAliasWithJSW<MyInterface>): MyMapAliasWithJSW<MyInterface> = TODO()
+                    fun mapTypeAlias9(param: MyGeneric<MyMapAliasWithJSW<R>>): MyGeneric<MyMapAliasWithJSW<R>> = TODO()
+                    fun mapTypeAlias10(param: MyGeneric<MyMapAliasWithJSW<MyInterface>>): MyGeneric<MyMapAliasWithJSW<MyInterface>> = TODO()
+                    fun mapTypeAlias11(param: MyGeneric<MyMapAliasWithJSW<MyGeneric<R>>>): MyGeneric<MyMapAliasWithJSW<MyGeneric<R>>> = TODO()
+                    fun mapTypeAlias12(param: MyGeneric<MyMapAliasWithJSW<MyGeneric<MyInterface>>>): MyGeneric<MyMapAliasWithJSW<MyGeneric<MyInterface>>> = TODO()
                 }
                 """.trimIndent()
             ), listOf(className)
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/util/PoetTestExt.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/util/PoetTestExt.kt
index 86942af..7de5edb 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/util/PoetTestExt.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/util/PoetTestExt.kt
@@ -16,7 +16,6 @@
 
 package androidx.room.compiler.processing.util
 
-import androidx.room.compiler.codegen.XTypeName
 import com.squareup.kotlinpoet.MUTABLE_COLLECTION
 import com.squareup.kotlinpoet.MUTABLE_ITERABLE
 import com.squareup.kotlinpoet.MUTABLE_LIST
@@ -73,14 +72,6 @@
     else -> this.asKClassName()
 }
 
-// Creates a simple XTypeName wrapping JTypeVariableName and KTypeVariableName without bounds.
-fun createXTypeVariableName(name: String): XTypeName {
-    return XTypeName(
-        java = JTypeVariableName.get(name),
-        kotlin = KTypeVariableName(name)
-    )
-}
-
 /**
  * Dumps the typename with its bounds in a given depth, making tests more readable.
  */
diff --git a/room/room-compiler/build.gradle b/room/room-compiler/build.gradle
index 6d06fe8..c73ef07 100644
--- a/room/room-compiler/build.gradle
+++ b/room/room-compiler/build.gradle
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-
 import androidx.build.BuildOnServerKt
 import androidx.build.LibraryType
 import androidx.build.SdkHelperKt
@@ -29,12 +28,6 @@
     id("com.github.johnrengelman.shadow")
 }
 
-def antlrOut = "$buildDir/generated/antlr/grammar-gen/"
-sourceSets {
-    main.java.srcDirs += "src/main/grammar-gen"
-    main.java.srcDirs += antlrOut
-}
-
 configurations {
     /**
      * shadowed is used for dependencies which we jarjar into the library jar instead of adding it
@@ -121,23 +114,27 @@
     testImplementation(project(":internal-testutils-common"))
 }
 
-def generateAntlrTask = task("generateAntlrGrammar", type: GenerateAntlrGrammar) {
-    sqliteFile = file("$projectDir/SQLite.g4")
-    antlrClasspath = configurations.compileClasspath
-    outputDirectory = file(antlrOut)
+def generateAntlrTask = tasks.register("generateAntlrGrammar", GenerateAntlrGrammar) { task ->
+    task.getSqliteFile().set(layout.projectDirectory.file("SQLite.g4"))
+    task.getAntlrClasspath().from(configurations.compileClasspath)
+    task.getOutputDirectory().set(layout.buildDirectory.dir("generated/antlr/grammar-gen/"))
+}
+
+sourceSets {
+    main.java.srcDirs += generateAntlrTask.map { it.outputDirectory }
 }
 
 @CacheableTask
 abstract class GenerateAntlrGrammar extends DefaultTask {
     @PathSensitive(PathSensitivity.NONE)
     @InputFile
-    File sqliteFile
+    abstract RegularFileProperty getSqliteFile()
 
     @Classpath
-    FileCollection antlrClasspath
+    abstract ConfigurableFileCollection getAntlrClasspath()
 
     @OutputDirectory
-    File outputDirectory
+    abstract DirectoryProperty getOutputDirectory()
 
     @Inject
     abstract ExecOperations getExecOperations()
@@ -152,10 +149,10 @@
     void generateAntlrGrammar() {
         execOperations.javaexec {
             mainClass.set("org.antlr.v4.Tool")
-            classpath = antlrClasspath
-            args "SQLite.g4",
+            classpath = getAntlrClasspath()
+            args getSqliteFile().asFile.get().absolutePath,
                  "-visitor",
-                 "-o", new File(outputDirectory, "androidx/room/parser").path,
+                 "-o", new File(getOutputDirectory().asFile.get(), "androidx/room/parser").path,
                  "-package", "androidx.room.parser"
         }
     }
@@ -165,23 +162,23 @@
  * Room compiler jarjars some dependencies. This task validates the published artifacts of room
  * compiler to ensure dependencies are properly jarjarred.
  */
-class CheckArtifactTask extends DefaultTask {
+abstract class CheckArtifactTask extends DefaultTask {
     @InputFiles
-    FileCollection artifactInputs = project.objects.fileCollection()
+    abstract ConfigurableFileCollection getArtifactInputs()
     @InputFile
-    File pomFile
+    abstract RegularFileProperty getPomFile()
     @OutputFile
-    File result = new File(project.buildDir, "checkArtifactOutput.txt")
+    abstract RegularFileProperty getResult()
     /**
      * Checks the publish task's artifacts to make sure the classes.jar does include jarjarred
      * antlr classes.
      */
-    def validatePublishTaskOutputs() {
-        if (artifactInputs.files.isEmpty()) {
+    void validatePublishTaskOutputs() {
+        if (getArtifactInputs().files.isEmpty()) {
             throw new GradleException("Couldn't find the classes.jar for the room-compiler " +
                     "artifact. Ensure that publish is setup properly.")
         }
-        artifactInputs.forEach {
+        getArtifactInputs().forEach {
             validateJarContents(it)
         }
     }
@@ -190,7 +187,7 @@
      * Traverses the given jar file, looks for the classes that should be jarjarred and validates
      * their location.
      */
-    def validateJarContents(File jarFile) {
+    static void validateJarContents(File jarFile) {
         Boolean found = false
         ZipFile zip = new ZipFile(jarFile)
         try {
@@ -223,11 +220,12 @@
      * Checks the generated pom file to ensure it does not depend on any jarjarred dependencies
      * but still depends on others.
      */
-    def validatePomTaskOutputs() {
-        if (!pomFile.canRead()) {
+    void validatePomTaskOutputs() {
+        File pom = getPomFile().asFile.get()
+        if (!pom.canRead()) {
             throw new GradleException("Cannot find the pom file for room-compiler")
         }
-        def pomContents = pomFile.newReader().text
+        def pomContents = pom.newReader().text
         if (pomContents.contains("antlr")) {
             throw new GradleException("Room-compiler pom file should not depend on antlr.\n" +
                     "Pom Contents:\n $pomContents")
@@ -239,24 +237,29 @@
     }
 
     @TaskAction
-    def validate() {
-        result.write("fail\n")
+    void validate() {
+        getResult().asFile.get().write("fail\n")
         validatePublishTaskOutputs()
         validatePomTaskOutputs()
         // have a no-op output to make gradle happy w/ input/output checking.
-        result.write("ok\n")
+        getResult().asFile.get().write("ok\n")
     }
 }
 
-def checkArtifactContentsTask = tasks.register("checkArtifactTask", CheckArtifactTask) {
-    def pomTask = (GenerateMavenPom) project.tasks.named("generatePomFileForMavenPublication").get()
-    it.pomFile = pomTask.destination
+def checkArtifactContentsTask = tasks.register("checkArtifact", CheckArtifactTask) { task ->
+    task.getResult().set(layout.buildDirectory.file("checkArtifactOutput.txt"))
+    def pomTask = (TaskProvider<GenerateMavenPom>) project.tasks.named("generatePomFileForMavenPublication")
+    task.getPomFile().set(
+            project.objects.fileProperty().fileProvider(
+                    pomTask.map {  it.destination }
+            )
+    )
 }
 
 afterEvaluate {
     def publishTaskProvider = project.tasks.named("publishMavenPublicationToMavenRepository")
-    checkArtifactContentsTask.configure {
-        it.artifactInputs.from {
+    checkArtifactContentsTask.configure { checkArtifactTask ->
+        checkArtifactTask.getArtifactInputs().from {
             publishTaskProvider.map {
                 ((PublishToMavenRepository) it).getPublication().artifacts.matching {
                     it.classifier == null
@@ -271,11 +274,6 @@
 // make sure we validate published artifacts on the build server.
 BuildOnServerKt.addToBuildOnServer(project, checkArtifactContentsTask)
 
-def tasksThatDependOnAntlr = ["compileKotlin", "sourceJar", "kotlinSourcesJar", "generateKmpDocs"]
-tasks.matching { tasksThatDependOnAntlr.contains(it.name) }.configureEach {
-    it.dependsOn(generateAntlrTask)
-}
-
 tasks.withType(KotlinCompile).configureEach {
     kotlinOptions {
         freeCompilerArgs += [
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/DeleteOrUpdateShortcutMethodProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/DeleteOrUpdateShortcutMethodProcessorTest.kt
index 32f453f..7f7e12a 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/DeleteOrUpdateShortcutMethodProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/DeleteOrUpdateShortcutMethodProcessorTest.kt
@@ -376,7 +376,7 @@
             assertThat(
                 param.type.asTypeName(),
                 `is`(
-                    XClassName.get("foo.bar", "MyClass.MyList").parametrizedBy(
+                    XClassName.get("foo.bar", "MyClass", "MyList").parametrizedBy(
                         CommonTypeNames.STRING, COMMON.USER_TYPE_NAME
                     )
                 )
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/InsertOrUpsertShortcutMethodProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/InsertOrUpsertShortcutMethodProcessorTest.kt
index fb756e4..45f42288 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/InsertOrUpsertShortcutMethodProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/InsertOrUpsertShortcutMethodProcessorTest.kt
@@ -400,7 +400,7 @@
             val param = insertionUpsertion.parameters.first()
             assertThat(param.type.asTypeName())
                 .isEqualTo(
-                    XClassName.get("foo.bar", "MyClass.MyList").parametrizedBy(
+                    XClassName.get("foo.bar", "MyClass", "MyList").parametrizedBy(
                         CommonTypeNames.STRING, USER_TYPE_NAME
                     )
                 )
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/PojoProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/PojoProcessorTest.kt
index ec56bef..a4c0fd9 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/PojoProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/PojoProcessorTest.kt
@@ -212,7 +212,7 @@
             assertThat(parent.field.name, `is`("myPoint"))
             assertThat(
                 parent.pojo.typeName,
-                `is`(XClassName.get("foo.bar.MyPojo", "Point"))
+                `is`(XClassName.get("foo.bar", "MyPojo", "Point"))
             )
         }
     }
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
index cbb26a4..13d0293 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
@@ -335,7 +335,7 @@
                 """
         ) { parsedQuery, invocation ->
             val expected = MUTABLE_LIST.parametrizedBy(
-                XClassName.get("", "T")
+                XTypeName.getTypeVariableName("T", listOf(XTypeName.ANY_OBJECT))
             )
             assertThat(parsedQuery.returnType.asTypeName(), `is`(expected))
             invocation.assertCompilationResult {
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/RawQueryMethodProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/RawQueryMethodProcessorTest.kt
index e943139..291267e 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/RawQueryMethodProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/RawQueryMethodProcessorTest.kt
@@ -179,7 +179,7 @@
 
     @Test
     fun pojo() {
-        val pojo = XClassName.get("foo.bar.MyClass", "MyPojo")
+        val pojo = XClassName.get("foo.bar", "MyClass", "MyPojo")
         singleQueryMethod(
             """
                 public class MyPojo {
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/writer/DefaultsInDaoTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/writer/DefaultsInDaoTest.kt
index 3ee50134..6d6fcc7 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/writer/DefaultsInDaoTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/writer/DefaultsInDaoTest.kt
@@ -17,7 +17,6 @@
 package androidx.room.writer
 
 import COMMON
-import androidx.kruth.StringSubject
 import androidx.room.compiler.codegen.CodeLanguage
 import androidx.room.compiler.processing.XTypeElement
 import androidx.room.compiler.processing.util.Source
@@ -25,6 +24,7 @@
 import androidx.room.ext.RoomTypeNames.ROOM_DB
 import androidx.room.processor.DaoProcessor
 import androidx.room.testing.context
+import com.google.common.truth.StringSubject
 import createVerifierFromEntitiesAndViews
 import org.jetbrains.kotlin.config.JvmDefaultMode
 import org.junit.Test
diff --git a/room/room-compiler/src/test/test-data/autoMigrationWriter/output/kotlin/AutoMigrationWithProvidedSpec.kt b/room/room-compiler/src/test/test-data/autoMigrationWriter/output/kotlin/AutoMigrationWithProvidedSpec.kt
index 647be54..90577fc 100644
--- a/room/room-compiler/src/test/test-data/autoMigrationWriter/output/kotlin/AutoMigrationWithProvidedSpec.kt
+++ b/room/room-compiler/src/test/test-data/autoMigrationWriter/output/kotlin/AutoMigrationWithProvidedSpec.kt
@@ -5,7 +5,6 @@
 import androidx.sqlite.db.SupportSQLiteDatabase
 import javax.`annotation`.processing.Generated
 import kotlin.Suppress
-import kotlin.Unit
 
 @Generated(value = ["androidx.room.RoomProcessor"])
 @Suppress(names = ["UNCHECKED_CAST", "DEPRECATION", "REDUNDANT_PROJECTION"])
@@ -16,7 +15,7 @@
         this.callback = callback
     }
 
-    public override fun migrate(db: SupportSQLiteDatabase): Unit {
+    public override fun migrate(db: SupportSQLiteDatabase) {
         db.execSQL("ALTER TABLE `Song` ADD COLUMN `artistId` INTEGER DEFAULT NULL")
         callback.onPostMigrate(db)
     }
diff --git a/room/room-compiler/src/test/test-data/autoMigrationWriter/output/kotlin/ValidAutoMigrationWithDefault.kt b/room/room-compiler/src/test/test-data/autoMigrationWriter/output/kotlin/ValidAutoMigrationWithDefault.kt
index 6b4d52e6..31f43d7 100644
--- a/room/room-compiler/src/test/test-data/autoMigrationWriter/output/kotlin/ValidAutoMigrationWithDefault.kt
+++ b/room/room-compiler/src/test/test-data/autoMigrationWriter/output/kotlin/ValidAutoMigrationWithDefault.kt
@@ -5,7 +5,6 @@
 import androidx.sqlite.db.SupportSQLiteDatabase
 import javax.`annotation`.processing.Generated
 import kotlin.Suppress
-import kotlin.Unit
 
 @Generated(value = ["androidx.room.RoomProcessor"])
 @Suppress(names = ["UNCHECKED_CAST", "DEPRECATION", "REDUNDANT_PROJECTION"])
@@ -14,7 +13,7 @@
 
     public constructor() : super(1, 2)
 
-    public override fun migrate(db: SupportSQLiteDatabase): Unit {
+    public override fun migrate(db: SupportSQLiteDatabase) {
         db.execSQL("ALTER TABLE `Song` ADD COLUMN `artistId` INTEGER NOT NULL DEFAULT 0")
         callback.onPostMigrate(db)
     }
diff --git a/room/room-compiler/src/test/test-data/autoMigrationWriter/output/kotlin/ValidAutoMigrationWithoutDefault.kt b/room/room-compiler/src/test/test-data/autoMigrationWriter/output/kotlin/ValidAutoMigrationWithoutDefault.kt
index 93eab8f..f0ffd3a 100644
--- a/room/room-compiler/src/test/test-data/autoMigrationWriter/output/kotlin/ValidAutoMigrationWithoutDefault.kt
+++ b/room/room-compiler/src/test/test-data/autoMigrationWriter/output/kotlin/ValidAutoMigrationWithoutDefault.kt
@@ -5,7 +5,6 @@
 import androidx.sqlite.db.SupportSQLiteDatabase
 import javax.`annotation`.processing.Generated
 import kotlin.Suppress
-import kotlin.Unit
 
 @Generated(value = ["androidx.room.RoomProcessor"])
 @Suppress(names = ["UNCHECKED_CAST", "DEPRECATION", "REDUNDANT_PROJECTION"])
@@ -14,7 +13,7 @@
 
     public constructor() : super(1, 2)
 
-    public override fun migrate(db: SupportSQLiteDatabase): Unit {
+    public override fun migrate(db: SupportSQLiteDatabase) {
         db.execSQL("ALTER TABLE `Song` ADD COLUMN `artistId` INTEGER DEFAULT NULL")
         callback.onPostMigrate(db)
     }
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/callableQuery_rx2.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/callableQuery_rx2.kt
index 32595b7..fd018379 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/callableQuery_rx2.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/callableQuery_rx2.kt
@@ -19,7 +19,6 @@
 import kotlin.Int
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -73,7 +72,7 @@
                 }
             }
 
-            protected fun finalize(): Unit {
+            protected fun finalize() {
                 _statement.release()
             }
         })
@@ -119,7 +118,7 @@
                 }
             }
 
-            protected fun finalize(): Unit {
+            protected fun finalize() {
                 _statement.release()
             }
         })
@@ -168,7 +167,7 @@
                 }
             }
 
-            protected fun finalize(): Unit {
+            protected fun finalize() {
                 _statement.release()
             }
         })
@@ -214,7 +213,7 @@
                 }
             }
 
-            protected fun finalize(): Unit {
+            protected fun finalize() {
                 _statement.release()
             }
         })
@@ -260,7 +259,7 @@
                 }
             }
 
-            protected fun finalize(): Unit {
+            protected fun finalize() {
                 _statement.release()
             }
         })
@@ -306,7 +305,7 @@
                 }
             }
 
-            protected fun finalize(): Unit {
+            protected fun finalize() {
                 _statement.release()
             }
         })
@@ -355,7 +354,7 @@
                 }
             }
 
-            protected fun finalize(): Unit {
+            protected fun finalize() {
                 _statement.release()
             }
         })
@@ -401,7 +400,7 @@
                 }
             }
 
-            protected fun finalize(): Unit {
+            protected fun finalize() {
                 _statement.release()
             }
         })
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/callableQuery_rx3.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/callableQuery_rx3.kt
index 07fbdeb5..48492a3 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/callableQuery_rx3.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/callableQuery_rx3.kt
@@ -19,7 +19,6 @@
 import kotlin.Int
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -73,7 +72,7 @@
                 }
             }
 
-            protected fun finalize(): Unit {
+            protected fun finalize() {
                 _statement.release()
             }
         })
@@ -119,7 +118,7 @@
                 }
             }
 
-            protected fun finalize(): Unit {
+            protected fun finalize() {
                 _statement.release()
             }
         })
@@ -168,7 +167,7 @@
                 }
             }
 
-            protected fun finalize(): Unit {
+            protected fun finalize() {
                 _statement.release()
             }
         })
@@ -214,7 +213,7 @@
                 }
             }
 
-            protected fun finalize(): Unit {
+            protected fun finalize() {
                 _statement.release()
             }
         })
@@ -260,7 +259,7 @@
                 }
             }
 
-            protected fun finalize(): Unit {
+            protected fun finalize() {
                 _statement.release()
             }
         })
@@ -306,7 +305,7 @@
                 }
             }
 
-            protected fun finalize(): Unit {
+            protected fun finalize() {
                 _statement.release()
             }
         })
@@ -355,7 +354,7 @@
                 }
             }
 
-            protected fun finalize(): Unit {
+            protected fun finalize() {
                 _statement.release()
             }
         })
@@ -401,7 +400,7 @@
                 }
             }
 
-            protected fun finalize(): Unit {
+            protected fun finalize() {
                 _statement.release()
             }
         })
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/coroutines.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/coroutines.kt
index d99e339..3cf1b4c 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/coroutines.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/coroutines.kt
@@ -18,7 +18,6 @@
 import kotlin.Int
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.collections.MutableList
 import kotlin.jvm.JvmStatic
@@ -74,7 +73,7 @@
                 }
             }
 
-            protected fun finalize(): Unit {
+            protected fun finalize() {
                 _statement.release()
             }
         })
@@ -121,7 +120,7 @@
                 }
             }
 
-            protected fun finalize(): Unit {
+            protected fun finalize() {
                 _statement.release()
             }
         })
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/database_internalVisibility.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/database_internalVisibility.kt
index 585f176..442641a 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/database_internalVisibility.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/database_internalVisibility.kt
@@ -18,7 +18,6 @@
 import kotlin.Lazy
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.collections.Map
 import kotlin.collections.MutableList
@@ -35,13 +34,13 @@
     protected override fun createOpenHelper(config: DatabaseConfiguration): SupportSQLiteOpenHelper {
         val _openCallback: SupportSQLiteOpenHelper.Callback = RoomOpenHelper(config, object :
             RoomOpenHelper.Delegate(1) {
-            public override fun createAllTables(db: SupportSQLiteDatabase): Unit {
+            public override fun createAllTables(db: SupportSQLiteDatabase) {
                 db.execSQL("CREATE TABLE IF NOT EXISTS `MyEntity` (`pk` INTEGER NOT NULL, PRIMARY KEY(`pk`))")
                 db.execSQL("CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)")
                 db.execSQL("INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '195d7974660177325bd1a32d2c7b8b8c')")
             }
 
-            public override fun dropAllTables(db: SupportSQLiteDatabase): Unit {
+            public override fun dropAllTables(db: SupportSQLiteDatabase) {
                 db.execSQL("DROP TABLE IF EXISTS `MyEntity`")
                 val _callbacks: List<RoomDatabase.Callback>? = mCallbacks
                 if (_callbacks != null) {
@@ -51,7 +50,7 @@
                 }
             }
 
-            public override fun onCreate(db: SupportSQLiteDatabase): Unit {
+            public override fun onCreate(db: SupportSQLiteDatabase) {
                 val _callbacks: List<RoomDatabase.Callback>? = mCallbacks
                 if (_callbacks != null) {
                     for (_callback: RoomDatabase.Callback in _callbacks) {
@@ -60,7 +59,7 @@
                 }
             }
 
-            public override fun onOpen(db: SupportSQLiteDatabase): Unit {
+            public override fun onOpen(db: SupportSQLiteDatabase) {
                 mDatabase = db
                 internalInitInvalidationTracker(db)
                 val _callbacks: List<RoomDatabase.Callback>? = mCallbacks
@@ -71,11 +70,11 @@
                 }
             }
 
-            public override fun onPreMigrate(db: SupportSQLiteDatabase): Unit {
+            public override fun onPreMigrate(db: SupportSQLiteDatabase) {
                 dropFtsSyncTriggers(db)
             }
 
-            public override fun onPostMigrate(db: SupportSQLiteDatabase): Unit {
+            public override fun onPostMigrate(db: SupportSQLiteDatabase) {
             }
 
             public override fun onValidateSchema(db: SupportSQLiteDatabase):
@@ -113,7 +112,7 @@
         return InvalidationTracker(this, _shadowTablesMap, _viewTables, "MyEntity")
     }
 
-    public override fun clearAllTables(): Unit {
+    public override fun clearAllTables() {
         super.assertNotMainThread()
         val _db: SupportSQLiteDatabase = super.openHelper.writableDatabase
         try {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/database_simple.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/database_simple.kt
index 6209c74..50eb478 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/database_simple.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/database_simple.kt
@@ -18,7 +18,6 @@
 import kotlin.Lazy
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.collections.Map
 import kotlin.collections.MutableList
@@ -34,13 +33,13 @@
     protected override fun createOpenHelper(config: DatabaseConfiguration): SupportSQLiteOpenHelper {
         val _openCallback: SupportSQLiteOpenHelper.Callback = RoomOpenHelper(config, object :
             RoomOpenHelper.Delegate(1) {
-            public override fun createAllTables(db: SupportSQLiteDatabase): Unit {
+            public override fun createAllTables(db: SupportSQLiteDatabase) {
                 db.execSQL("CREATE TABLE IF NOT EXISTS `MyEntity` (`pk` INTEGER NOT NULL, PRIMARY KEY(`pk`))")
                 db.execSQL("CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)")
                 db.execSQL("INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '195d7974660177325bd1a32d2c7b8b8c')")
             }
 
-            public override fun dropAllTables(db: SupportSQLiteDatabase): Unit {
+            public override fun dropAllTables(db: SupportSQLiteDatabase) {
                 db.execSQL("DROP TABLE IF EXISTS `MyEntity`")
                 val _callbacks: List<RoomDatabase.Callback>? = mCallbacks
                 if (_callbacks != null) {
@@ -50,7 +49,7 @@
                 }
             }
 
-            public override fun onCreate(db: SupportSQLiteDatabase): Unit {
+            public override fun onCreate(db: SupportSQLiteDatabase) {
                 val _callbacks: List<RoomDatabase.Callback>? = mCallbacks
                 if (_callbacks != null) {
                     for (_callback: RoomDatabase.Callback in _callbacks) {
@@ -59,7 +58,7 @@
                 }
             }
 
-            public override fun onOpen(db: SupportSQLiteDatabase): Unit {
+            public override fun onOpen(db: SupportSQLiteDatabase) {
                 mDatabase = db
                 internalInitInvalidationTracker(db)
                 val _callbacks: List<RoomDatabase.Callback>? = mCallbacks
@@ -70,11 +69,11 @@
                 }
             }
 
-            public override fun onPreMigrate(db: SupportSQLiteDatabase): Unit {
+            public override fun onPreMigrate(db: SupportSQLiteDatabase) {
                 dropFtsSyncTriggers(db)
             }
 
-            public override fun onPostMigrate(db: SupportSQLiteDatabase): Unit {
+            public override fun onPostMigrate(db: SupportSQLiteDatabase) {
             }
 
             public override fun onValidateSchema(db: SupportSQLiteDatabase):
@@ -112,7 +111,7 @@
         return InvalidationTracker(this, _shadowTablesMap, _viewTables, "MyEntity")
     }
 
-    public override fun clearAllTables(): Unit {
+    public override fun clearAllTables() {
         super.assertNotMainThread()
         val _db: SupportSQLiteDatabase = super.openHelper.writableDatabase
         try {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/database_withFtsAndView.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/database_withFtsAndView.kt
index 8ab3bb8..2ec181a 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/database_withFtsAndView.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/database_withFtsAndView.kt
@@ -6,7 +6,6 @@
 import androidx.room.migration.Migration
 import androidx.room.util.FtsTableInfo
 import androidx.room.util.TableInfo
-import androidx.room.util.TableInfo.Companion.read
 import androidx.room.util.ViewInfo
 import androidx.room.util.dropFtsSyncTriggers
 import androidx.sqlite.db.SupportSQLiteDatabase
@@ -21,11 +20,13 @@
 import kotlin.Lazy
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.collections.Map
 import kotlin.collections.MutableList
 import kotlin.collections.Set
+import androidx.room.util.FtsTableInfo.Companion.read as ftsTableInfoRead
+import androidx.room.util.TableInfo.Companion.read as tableInfoRead
+import androidx.room.util.ViewInfo.Companion.read as viewInfoRead
 
 @Generated(value = ["androidx.room.RoomProcessor"])
 @Suppress(names = ["UNCHECKED_CAST", "DEPRECATION", "REDUNDANT_PROJECTION"])
@@ -34,10 +35,11 @@
         MyDao_Impl(this)
     }
 
+
     protected override fun createOpenHelper(config: DatabaseConfiguration): SupportSQLiteOpenHelper {
         val _openCallback: SupportSQLiteOpenHelper.Callback = RoomOpenHelper(config, object :
             RoomOpenHelper.Delegate(1) {
-            public override fun createAllTables(db: SupportSQLiteDatabase): Unit {
+            public override fun createAllTables(db: SupportSQLiteDatabase) {
                 db.execSQL("CREATE TABLE IF NOT EXISTS `MyParentEntity` (`parentKey` INTEGER NOT NULL, PRIMARY KEY(`parentKey`))")
                 db.execSQL("CREATE TABLE IF NOT EXISTS `MyEntity` (`pk` INTEGER NOT NULL, `indexedCol` TEXT NOT NULL, PRIMARY KEY(`pk`), FOREIGN KEY(`indexedCol`) REFERENCES `MyParentEntity`(`parentKey`) ON UPDATE NO ACTION ON DELETE CASCADE )")
                 db.execSQL("CREATE INDEX IF NOT EXISTS `index_MyEntity_indexedCol` ON `MyEntity` (`indexedCol`)")
@@ -47,7 +49,7 @@
                 db.execSQL("INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '89ba16fb8b062b50acf0eb06c853efcb')")
             }
 
-            public override fun dropAllTables(db: SupportSQLiteDatabase): Unit {
+            public override fun dropAllTables(db: SupportSQLiteDatabase) {
                 db.execSQL("DROP TABLE IF EXISTS `MyParentEntity`")
                 db.execSQL("DROP TABLE IF EXISTS `MyEntity`")
                 db.execSQL("DROP TABLE IF EXISTS `MyFtsEntity`")
@@ -60,7 +62,7 @@
                 }
             }
 
-            public override fun onCreate(db: SupportSQLiteDatabase): Unit {
+            public override fun onCreate(db: SupportSQLiteDatabase) {
                 val _callbacks: List<RoomDatabase.Callback>? = mCallbacks
                 if (_callbacks != null) {
                     for (_callback: RoomDatabase.Callback in _callbacks) {
@@ -69,7 +71,7 @@
                 }
             }
 
-            public override fun onOpen(db: SupportSQLiteDatabase): Unit {
+            public override fun onOpen(db: SupportSQLiteDatabase) {
                 mDatabase = db
                 db.execSQL("PRAGMA foreign_keys = ON")
                 internalInitInvalidationTracker(db)
@@ -81,11 +83,11 @@
                 }
             }
 
-            public override fun onPreMigrate(db: SupportSQLiteDatabase): Unit {
+            public override fun onPreMigrate(db: SupportSQLiteDatabase) {
                 dropFtsSyncTriggers(db)
             }
 
-            public override fun onPostMigrate(db: SupportSQLiteDatabase): Unit {
+            public override fun onPostMigrate(db: SupportSQLiteDatabase) {
             }
 
             public override fun onValidateSchema(db: SupportSQLiteDatabase):
@@ -99,15 +101,15 @@
                 val _indicesMyParentEntity: HashSet<TableInfo.Index> = HashSet<TableInfo.Index>(0)
                 val _infoMyParentEntity: TableInfo = TableInfo("MyParentEntity", _columnsMyParentEntity,
                     _foreignKeysMyParentEntity, _indicesMyParentEntity)
-                val _existingMyParentEntity: TableInfo = read(db, "MyParentEntity")
+                val _existingMyParentEntity: TableInfo = tableInfoRead(db, "MyParentEntity")
                 if (!_infoMyParentEntity.equals(_existingMyParentEntity)) {
                     return RoomOpenHelper.ValidationResult(false, """
-                  |MyParentEntity(MyParentEntity).
-                  | Expected:
-                  |""".trimMargin() + _infoMyParentEntity + """
-                  |
-                  | Found:
-                  |""".trimMargin() + _existingMyParentEntity)
+              |MyParentEntity(MyParentEntity).
+              | Expected:
+              |""".trimMargin() + _infoMyParentEntity + """
+              |
+              | Found:
+              |""".trimMargin() + _existingMyParentEntity)
                 }
                 val _columnsMyEntity: HashMap<String, TableInfo.Column> =
                     HashMap<String, TableInfo.Column>(2)
@@ -123,41 +125,41 @@
                     listOf("indexedCol"), listOf("ASC")))
                 val _infoMyEntity: TableInfo = TableInfo("MyEntity", _columnsMyEntity, _foreignKeysMyEntity,
                     _indicesMyEntity)
-                val _existingMyEntity: TableInfo = read(db, "MyEntity")
+                val _existingMyEntity: TableInfo = tableInfoRead(db, "MyEntity")
                 if (!_infoMyEntity.equals(_existingMyEntity)) {
                     return RoomOpenHelper.ValidationResult(false, """
-                  |MyEntity(MyEntity).
-                  | Expected:
-                  |""".trimMargin() + _infoMyEntity + """
-                  |
-                  | Found:
-                  |""".trimMargin() + _existingMyEntity)
+              |MyEntity(MyEntity).
+              | Expected:
+              |""".trimMargin() + _infoMyEntity + """
+              |
+              | Found:
+              |""".trimMargin() + _existingMyEntity)
                 }
                 val _columnsMyFtsEntity: HashSet<String> = HashSet<String>(2)
                 _columnsMyFtsEntity.add("text")
                 val _infoMyFtsEntity: FtsTableInfo = FtsTableInfo("MyFtsEntity", _columnsMyFtsEntity,
                     "CREATE VIRTUAL TABLE IF NOT EXISTS `MyFtsEntity` USING FTS4(`text` TEXT NOT NULL)")
-                val _existingMyFtsEntity: FtsTableInfo = FtsTableInfo.Companion.read(db, "MyFtsEntity")
+                val _existingMyFtsEntity: FtsTableInfo = ftsTableInfoRead(db, "MyFtsEntity")
                 if (!_infoMyFtsEntity.equals(_existingMyFtsEntity)) {
                     return RoomOpenHelper.ValidationResult(false, """
-                  |MyFtsEntity(MyFtsEntity).
-                  | Expected:
-                  |""".trimMargin() + _infoMyFtsEntity + """
-                  |
-                  | Found:
-                  |""".trimMargin() + _existingMyFtsEntity)
+              |MyFtsEntity(MyFtsEntity).
+              | Expected:
+              |""".trimMargin() + _infoMyFtsEntity + """
+              |
+              | Found:
+              |""".trimMargin() + _existingMyFtsEntity)
                 }
                 val _infoMyView: ViewInfo = ViewInfo("MyView",
                     "CREATE VIEW `MyView` AS SELECT text FROM MyFtsEntity")
-                val _existingMyView: ViewInfo = ViewInfo.Companion.read(db, "MyView")
+                val _existingMyView: ViewInfo = viewInfoRead(db, "MyView")
                 if (!_infoMyView.equals(_existingMyView)) {
                     return RoomOpenHelper.ValidationResult(false, """
-                  |MyView(MyView).
-                  | Expected:
-                  |""".trimMargin() + _infoMyView + """
-                  |
-                  | Found:
-                  |""".trimMargin() + _existingMyView)
+              |MyView(MyView).
+              | Expected:
+              |""".trimMargin() + _infoMyView + """
+              |
+              | Found:
+              |""".trimMargin() + _existingMyView)
                 }
                 return RoomOpenHelper.ValidationResult(true, null)
             }
@@ -179,7 +181,7 @@
             "MyParentEntity","MyEntity","MyFtsEntity")
     }
 
-    public override fun clearAllTables(): Unit {
+    public override fun clearAllTables() {
         super.assertNotMainThread()
         val _db: SupportSQLiteDatabase = super.openHelper.writableDatabase
         val _supportsDeferForeignKeys: Boolean = android.os.Build.VERSION.SDK_INT >=
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/deleteOrUpdateMethodAdapter.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/deleteOrUpdateMethodAdapter.kt
index 1166ce2..233b23a 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/deleteOrUpdateMethodAdapter.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/deleteOrUpdateMethodAdapter.kt
@@ -6,7 +6,6 @@
 import kotlin.Int
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -25,7 +24,7 @@
         this.__deletionAdapterOfMyEntity = object : EntityDeletionOrUpdateAdapter<MyEntity>(__db) {
             public override fun createQuery(): String = "DELETE FROM `MyEntity` WHERE `pk` = ?"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk)
             }
         }
@@ -33,7 +32,7 @@
             public override fun createQuery(): String =
                 "UPDATE OR ABORT `MyEntity` SET `pk` = ?,`data` = ? WHERE `pk` = ?"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk)
                 statement.bindString(2, entity.data)
                 statement.bindLong(3, entity.pk)
@@ -41,7 +40,7 @@
         }
     }
 
-    public override fun deleteEntity(item: MyEntity): Unit {
+    public override fun deleteEntity(item: MyEntity) {
         __db.assertNotSuspendingTransaction()
         __db.beginTransaction()
         try {
@@ -65,7 +64,7 @@
         }
     }
 
-    public override fun updateEntity(item: MyEntity): Unit {
+    public override fun updateEntity(item: MyEntity) {
         __db.assertNotSuspendingTransaction()
         __db.beginTransaction()
         try {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/entityRowAdapter.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/entityRowAdapter.kt
index e823e0a..d00f043 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/entityRowAdapter.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/entityRowAdapter.kt
@@ -13,7 +13,6 @@
 import kotlin.Long
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -31,7 +30,7 @@
             public override fun createQuery(): String =
                 "INSERT OR ABORT INTO `MyEntity` (`valuePrimitive`,`valueBoolean`,`valueString`,`valueNullableString`,`variablePrimitive`,`variableNullableBoolean`,`variableString`,`variableNullableString`) VALUES (?,?,?,?,?,?,?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.valuePrimitive)
                 val _tmp: Int = if (entity.valueBoolean) 1 else 0
                 statement.bindLong(2, _tmp.toLong())
@@ -61,7 +60,7 @@
         }
     }
 
-    public override fun addEntity(item: MyEntity): Unit {
+    public override fun addEntity(item: MyEntity) {
         __db.assertNotSuspendingTransaction()
         __db.beginTransaction()
         try {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/guavaCallable.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/guavaCallable.kt
index 7a118e5..3d51fc4 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/guavaCallable.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/guavaCallable.kt
@@ -22,7 +22,6 @@
 import kotlin.Long
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -46,7 +45,7 @@
             public override fun createQuery(): String =
                 "INSERT OR ABORT INTO `MyEntity` (`pk`,`other`) VALUES (?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 statement.bindString(2, entity.other)
             }
@@ -54,7 +53,7 @@
         this.__deletionAdapterOfMyEntity = object : EntityDeletionOrUpdateAdapter<MyEntity>(__db) {
             public override fun createQuery(): String = "DELETE FROM `MyEntity` WHERE `pk` = ?"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
             }
         }
@@ -62,7 +61,7 @@
             public override fun createQuery(): String =
                 "UPDATE OR ABORT `MyEntity` SET `pk` = ?,`other` = ? WHERE `pk` = ?"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 statement.bindString(2, entity.other)
                 statement.bindLong(3, entity.pk.toLong())
@@ -73,7 +72,7 @@
             public override fun createQuery(): String =
                 "INSERT INTO `MyEntity` (`pk`,`other`) VALUES (?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 statement.bindString(2, entity.other)
             }
@@ -81,7 +80,7 @@
             public override fun createQuery(): String =
                 "UPDATE `MyEntity` SET `pk` = ?,`other` = ? WHERE `pk` = ?"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 statement.bindString(2, entity.other)
                 statement.bindLong(3, entity.pk.toLong())
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/insertOrUpsertMethodAdapter.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/insertOrUpsertMethodAdapter.kt
index 9f9874c..246af07 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/insertOrUpsertMethodAdapter.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/insertOrUpsertMethodAdapter.kt
@@ -9,7 +9,6 @@
 import kotlin.Long
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -29,7 +28,7 @@
             public override fun createQuery(): String =
                 "INSERT OR ABORT INTO `MyEntity` (`pk`,`data`) VALUES (?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk)
                 statement.bindString(2, entity.data)
             }
@@ -39,7 +38,7 @@
             public override fun createQuery(): String =
                 "INSERT INTO `MyEntity` (`pk`,`data`) VALUES (?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk)
                 statement.bindString(2, entity.data)
             }
@@ -47,7 +46,7 @@
             public override fun createQuery(): String =
                 "UPDATE `MyEntity` SET `pk` = ?,`data` = ? WHERE `pk` = ?"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk)
                 statement.bindString(2, entity.data)
                 statement.bindLong(3, entity.pk)
@@ -55,7 +54,7 @@
         })
     }
 
-    public override fun insertEntity(item: MyEntity): Unit {
+    public override fun insertEntity(item: MyEntity) {
         __db.assertNotSuspendingTransaction()
         __db.beginTransaction()
         try {
@@ -90,7 +89,7 @@
         }
     }
 
-    public override fun upsertEntity(item: MyEntity): Unit {
+    public override fun upsertEntity(item: MyEntity) {
         __db.assertNotSuspendingTransaction()
         __db.beginTransaction()
         try {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/liveDataCallable.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/liveDataCallable.kt
index aad53d7..ed6eb98 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/liveDataCallable.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/liveDataCallable.kt
@@ -14,7 +14,6 @@
 import kotlin.Int
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -69,7 +68,7 @@
                 }
             }
 
-            protected fun finalize(): Unit {
+            protected fun finalize() {
                 _statement.release()
             }
         })
@@ -116,7 +115,7 @@
                 }
             }
 
-            protected fun finalize(): Unit {
+            protected fun finalize() {
                 _statement.release()
             }
         })
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/multiTypedPagingSourceResultBinder.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/multiTypedPagingSourceResultBinder.kt
index 921b262..70659ef 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/multiTypedPagingSourceResultBinder.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/multiTypedPagingSourceResultBinder.kt
@@ -1,13 +1,11 @@
 import android.database.Cursor
 import androidx.paging.ListenableFuturePagingSource
 import androidx.paging.PagingSource
-import androidx.paging.rxjava2.RxPagingSource
 import androidx.room.RoomDatabase
 import androidx.room.RoomSQLiteQuery
 import androidx.room.RoomSQLiteQuery.Companion.acquire
 import androidx.room.paging.LimitOffsetPagingSource
 import androidx.room.paging.guava.LimitOffsetListenableFuturePagingSource
-import androidx.room.paging.rxjava2.LimitOffsetRxPagingSource
 import java.lang.Class
 import java.util.ArrayList
 import javax.`annotation`.processing.Generated
@@ -17,6 +15,10 @@
 import kotlin.collections.List
 import kotlin.collections.MutableList
 import kotlin.jvm.JvmStatic
+import androidx.paging.rxjava2.RxPagingSource as Rxjava2RxPagingSource
+import androidx.paging.rxjava3.RxPagingSource as Rxjava3RxPagingSource
+import androidx.room.paging.rxjava2.LimitOffsetRxPagingSource as Rxjava2LimitOffsetRxPagingSource
+import androidx.room.paging.rxjava3.LimitOffsetRxPagingSource as Rxjava3LimitOffsetRxPagingSource
 
 @Generated(value = ["androidx.room.RoomProcessor"])
 @Suppress(names = ["UNCHECKED_CAST", "DEPRECATION", "REDUNDANT_PROJECTION"])
@@ -47,10 +49,10 @@
         }
     }
 
-    public override fun getAllIdsRx2(): RxPagingSource<Int, MyEntity> {
+    public override fun getAllIdsRx2(): Rxjava2RxPagingSource<Int, MyEntity> {
         val _sql: String = "SELECT pk FROM MyEntity"
         val _statement: RoomSQLiteQuery = acquire(_sql, 0)
-        return object : LimitOffsetRxPagingSource<MyEntity>(_statement, __db, "MyEntity") {
+        return object : Rxjava2LimitOffsetRxPagingSource<MyEntity>(_statement, __db, "MyEntity") {
             protected override fun convertRows(cursor: Cursor): List<MyEntity> {
                 val _cursorIndexOfPk: Int = 0
                 val _result: MutableList<MyEntity> = ArrayList<MyEntity>(cursor.getCount())
@@ -66,11 +68,10 @@
         }
     }
 
-    public override fun getAllIdsRx3(): androidx.paging.rxjava3.RxPagingSource<Int, MyEntity> {
+    public override fun getAllIdsRx3(): Rxjava3RxPagingSource<Int, MyEntity> {
         val _sql: String = "SELECT pk FROM MyEntity"
         val _statement: RoomSQLiteQuery = acquire(_sql, 0)
-        return object : androidx.room.paging.rxjava3.LimitOffsetRxPagingSource<MyEntity>(_statement,
-            __db, "MyEntity") {
+        return object : Rxjava3LimitOffsetRxPagingSource<MyEntity>(_statement, __db, "MyEntity") {
             protected override fun convertRows(cursor: Cursor): List<MyEntity> {
                 val _cursorIndexOfPk: Int = 0
                 val _result: MutableList<MyEntity> = ArrayList<MyEntity>(cursor.getCount())
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_boolean.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_boolean.kt
index e4aa4f3..0c57f0e 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_boolean.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_boolean.kt
@@ -12,7 +12,6 @@
 import kotlin.Int
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -30,7 +29,7 @@
             public override fun createQuery(): String =
                 "INSERT OR ABORT INTO `MyEntity` (`pk`,`boolean`,`nullableBoolean`) VALUES (?,?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 val _tmp: Int = if (entity.boolean) 1 else 0
                 statement.bindLong(2, _tmp.toLong())
@@ -45,7 +44,7 @@
         }
     }
 
-    public override fun addEntity(item: MyEntity): Unit {
+    public override fun addEntity(item: MyEntity) {
         __db.assertNotSuspendingTransaction()
         __db.beginTransaction()
         try {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_byteArray.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_byteArray.kt
index 284798e..f369507 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_byteArray.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_byteArray.kt
@@ -12,7 +12,6 @@
 import kotlin.Int
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -30,7 +29,7 @@
             public override fun createQuery(): String =
                 "INSERT OR ABORT INTO `MyEntity` (`pk`,`byteArray`,`nullableByteArray`) VALUES (?,?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 statement.bindBlob(2, entity.byteArray)
                 val _tmpNullableByteArray: ByteArray? = entity.nullableByteArray
@@ -43,7 +42,7 @@
         }
     }
 
-    public override fun addEntity(item: MyEntity): Unit {
+    public override fun addEntity(item: MyEntity) {
         __db.assertNotSuspendingTransaction()
         __db.beginTransaction()
         try {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter.kt
index 709f270..6e7794e 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter.kt
@@ -11,7 +11,6 @@
 import kotlin.Int
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -31,7 +30,7 @@
             public override fun createQuery(): String =
                 "INSERT OR ABORT INTO `MyEntity` (`pk`,`foo`) VALUES (?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 val _tmp: String = __fooConverter.toString(entity.foo)
                 statement.bindString(2, _tmp)
@@ -39,7 +38,7 @@
         }
     }
 
-    public override fun addEntity(item: MyEntity): Unit {
+    public override fun addEntity(item: MyEntity) {
         __db.assertNotSuspendingTransaction()
         __db.beginTransaction()
         try {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_composite.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_composite.kt
index 71d915e..96566e6 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_composite.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_composite.kt
@@ -11,7 +11,6 @@
 import kotlin.Int
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -29,7 +28,7 @@
             public override fun createQuery(): String =
                 "INSERT OR ABORT INTO `MyEntity` (`pk`,`bar`) VALUES (?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 val _tmp: Foo = FooBarConverter.toFoo(entity.bar)
                 val _tmp_1: String = FooBarConverter.toString(_tmp)
@@ -38,7 +37,7 @@
         }
     }
 
-    public override fun addEntity(item: MyEntity): Unit {
+    public override fun addEntity(item: MyEntity) {
         __db.assertNotSuspendingTransaction()
         __db.beginTransaction()
         try {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_internalVisibility.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_internalVisibility.kt
index 9376fc3..0ba6f82 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_internalVisibility.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_internalVisibility.kt
@@ -11,7 +11,6 @@
 import kotlin.Int
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -31,7 +30,7 @@
             public override fun createQuery(): String =
                 "INSERT OR ABORT INTO `MyEntity` (`pk`,`foo`) VALUES (?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 val _tmp: String = __fooConverter.toString(entity.foo)
                 statement.bindString(2, _tmp)
@@ -39,7 +38,7 @@
         }
     }
 
-    internal override fun addEntity(item: MyEntity): Unit {
+    internal override fun addEntity(item: MyEntity) {
         __db.assertNotSuspendingTransaction()
         __db.beginTransaction()
         try {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_nullAware.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_nullAware.kt
index a52bf88..0ea0420 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_nullAware.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_nullAware.kt
@@ -11,7 +11,6 @@
 import kotlin.Int
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -29,7 +28,7 @@
             public override fun createQuery(): String =
                 "INSERT OR ABORT INTO `MyEntity` (`pk`,`foo`,`bar`) VALUES (?,?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 val _tmp: String? = FooBarConverter.toString(entity.foo)
                 if (_tmp == null) {
@@ -48,7 +47,7 @@
         }
     }
 
-    public override fun addEntity(item: MyEntity): Unit {
+    public override fun addEntity(item: MyEntity) {
         __db.assertNotSuspendingTransaction()
         __db.beginTransaction()
         try {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_provided.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_provided.kt
index a464f74..3895395 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_provided.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_provided.kt
@@ -12,7 +12,6 @@
 import kotlin.Lazy
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -35,7 +34,7 @@
             public override fun createQuery(): String =
                 "INSERT OR ABORT INTO `MyEntity` (`pk`,`foo`) VALUES (?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 val _tmp: String = __fooConverter().toString(entity.foo)
                 statement.bindString(2, _tmp)
@@ -43,7 +42,7 @@
         }
     }
 
-    public override fun addEntity(item: MyEntity): Unit {
+    public override fun addEntity(item: MyEntity) {
         __db.assertNotSuspendingTransaction()
         __db.beginTransaction()
         try {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_upcast.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_upcast.kt
index 56fb164..e9eebbc 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_upcast.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_upcast.kt
@@ -11,7 +11,6 @@
 import kotlin.Int
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -29,7 +28,7 @@
             public override fun createQuery(): String =
                 "INSERT OR ABORT INTO `MyEntity` (`pk`,`foo`) VALUES (?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 val _tmp: String = FooConverter.nullableFooToString(entity.foo)
                 if (_tmp == null) {
@@ -41,7 +40,7 @@
         }
     }
 
-    public override fun addEntity(item: MyEntity): Unit {
+    public override fun addEntity(item: MyEntity) {
         __db.assertNotSuspendingTransaction()
         __db.beginTransaction()
         try {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_embedded.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_embedded.kt
index 7986fc1..b9a01bc 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_embedded.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_embedded.kt
@@ -12,7 +12,6 @@
 import kotlin.Long
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -30,7 +29,7 @@
             public override fun createQuery(): String =
                 "INSERT OR ABORT INTO `MyEntity` (`pk`,`numberData`,`stringData`,`nullablenumberData`,`nullablestringData`) VALUES (?,?,?,?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 val _tmpFoo: Foo = entity.foo
                 statement.bindLong(2, _tmpFoo.numberData)
@@ -47,7 +46,7 @@
         }
     }
 
-    public override fun addEntity(item: MyEntity): Unit {
+    public override fun addEntity(item: MyEntity) {
         __db.assertNotSuspendingTransaction()
         __db.beginTransaction()
         try {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_enum.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_enum.kt
index 25aa37e..a6f83a7 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_enum.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_enum.kt
@@ -12,7 +12,6 @@
 import kotlin.Int
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -30,7 +29,7 @@
             public override fun createQuery(): String =
                 "INSERT OR ABORT INTO `MyEntity` (`pk`,`enum`,`nullableEnum`) VALUES (?,?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 statement.bindString(2, __Fruit_enumToString(entity.enum))
                 val _tmpNullableEnum: Fruit? = entity.nullableEnum
@@ -43,7 +42,7 @@
         }
     }
 
-    public override fun addEntity(item: MyEntity): Unit {
+    public override fun addEntity(item: MyEntity) {
         __db.assertNotSuspendingTransaction()
         __db.beginTransaction()
         try {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_internalVisibility.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_internalVisibility.kt
index a81a93e..db1f98c 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_internalVisibility.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_internalVisibility.kt
@@ -12,7 +12,6 @@
 import kotlin.Long
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -30,7 +29,7 @@
             public override fun createQuery(): String =
                 "INSERT OR ABORT INTO `MyEntity` (`pk`,`internalVal`,`internalVar`,`internalSetterVar`) VALUES (?,?,?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 statement.bindLong(2, entity.internalVal)
                 statement.bindLong(3, entity.internalVar)
@@ -39,7 +38,7 @@
         }
     }
 
-    public override fun addEntity(item: MyEntity): Unit {
+    public override fun addEntity(item: MyEntity) {
         __db.assertNotSuspendingTransaction()
         __db.beginTransaction()
         try {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_otherModule.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_otherModule.kt
index 8b4945d..320a33b 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_otherModule.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_otherModule.kt
@@ -12,7 +12,6 @@
 import kotlin.Long
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -30,7 +29,7 @@
             public override fun createQuery(): String =
                 "INSERT OR ABORT INTO `MyEntity` (`pk`,`primitive`,`string`,`nullableString`,`fieldString`,`nullableFieldString`,`variablePrimitive`,`variableString`,`variableNullableString`,`variableFieldString`,`variableNullableFieldString`) VALUES (?,?,?,?,?,?,?,?,?,?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 statement.bindLong(2, entity.primitive)
                 statement.bindString(3, entity.string)
@@ -66,7 +65,7 @@
         }
     }
 
-    public override fun addEntity(item: MyEntity): Unit {
+    public override fun addEntity(item: MyEntity) {
         __db.assertNotSuspendingTransaction()
         __db.beginTransaction()
         try {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_primitives.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_primitives.kt
index 28ed7ee..520ecbb 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_primitives.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_primitives.kt
@@ -17,7 +17,6 @@
 import kotlin.Short
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -35,7 +34,7 @@
             public override fun createQuery(): String =
                 "INSERT OR ABORT INTO `MyEntity` (`int`,`short`,`byte`,`long`,`char`,`float`,`double`) VALUES (?,?,?,?,?,?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.int.toLong())
                 statement.bindLong(2, entity.short.toLong())
                 statement.bindLong(3, entity.byte.toLong())
@@ -47,7 +46,7 @@
         }
     }
 
-    public override fun addEntity(item: MyEntity): Unit {
+    public override fun addEntity(item: MyEntity) {
         __db.assertNotSuspendingTransaction()
         __db.beginTransaction()
         try {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_primitives_nullable.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_primitives_nullable.kt
index 3ec68d0..109df70 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_primitives_nullable.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_primitives_nullable.kt
@@ -17,7 +17,6 @@
 import kotlin.Short
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -35,7 +34,7 @@
             public override fun createQuery(): String =
                 "INSERT OR ABORT INTO `MyEntity` (`int`,`short`,`byte`,`long`,`char`,`float`,`double`) VALUES (?,?,?,?,?,?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 val _tmpInt: Int? = entity.int
                 if (_tmpInt == null) {
                     statement.bindNull(1)
@@ -82,7 +81,7 @@
         }
     }
 
-    public override fun addEntity(item: MyEntity): Unit {
+    public override fun addEntity(item: MyEntity) {
         __db.assertNotSuspendingTransaction()
         __db.beginTransaction()
         try {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_string.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_string.kt
index 0a4b23b..213539a 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_string.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_string.kt
@@ -11,7 +11,6 @@
 import kotlin.Int
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -29,7 +28,7 @@
             public override fun createQuery(): String =
                 "INSERT OR ABORT INTO `MyEntity` (`string`,`nullableString`) VALUES (?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindString(1, entity.string)
                 val _tmpNullableString: String? = entity.nullableString
                 if (_tmpNullableString == null) {
@@ -41,7 +40,7 @@
         }
     }
 
-    public override fun addEntity(item: MyEntity): Unit {
+    public override fun addEntity(item: MyEntity) {
         __db.assertNotSuspendingTransaction()
         __db.beginTransaction()
         try {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_uuid.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_uuid.kt
index dd7f1df..a4770df 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_uuid.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_uuid.kt
@@ -14,7 +14,6 @@
 import kotlin.Int
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -32,7 +31,7 @@
             public override fun createQuery(): String =
                 "INSERT OR ABORT INTO `MyEntity` (`pk`,`uuid`,`nullableUuid`) VALUES (?,?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 statement.bindBlob(2, convertUUIDToByte(entity.uuid))
                 val _tmpNullableUuid: UUID? = entity.nullableUuid
@@ -45,7 +44,7 @@
         }
     }
 
-    public override fun addEntity(item: MyEntity): Unit {
+    public override fun addEntity(item: MyEntity) {
         __db.assertNotSuspendingTransaction()
         __db.beginTransaction()
         try {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_valueClassConverter.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_valueClassConverter.kt
index 501c3ed..6ea9026 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_valueClassConverter.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_valueClassConverter.kt
@@ -15,7 +15,6 @@
 import kotlin.Long
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -33,7 +32,7 @@
             public override fun createQuery(): String =
                 "INSERT OR ABORT INTO `MyEntity` (`pk`,`uuidData`,`nullableUuidData`,`nullableLongData`,`doubleNullableLongData`,`genericData`) VALUES (?,?,?,?,?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 val _data: Long = checkNotNull(entity.pk.data) {
                     "Cannot bind nullable value of inline class to a NOT NULL column." }
                 statement.bindLong(1, _data)
@@ -64,7 +63,7 @@
         }
     }
 
-    public override fun addEntity(item: MyEntity): Unit {
+    public override fun addEntity(item: MyEntity) {
         __db.assertNotSuspendingTransaction()
         __db.beginTransaction()
         try {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_variableProperty.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_variableProperty.kt
index ea44a4f..6276d06 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_variableProperty.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_variableProperty.kt
@@ -11,7 +11,6 @@
 import kotlin.Int
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -29,7 +28,7 @@
             public override fun createQuery(): String =
                 "INSERT OR ABORT INTO `MyEntity` (`pk`,`variablePrimitive`,`variableString`,`variableNullableString`) VALUES (?,?,?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 statement.bindLong(2, entity.variablePrimitive)
                 statement.bindString(3, entity.variableString)
@@ -43,7 +42,7 @@
         }
     }
 
-    public override fun addEntity(item: MyEntity): Unit {
+    public override fun addEntity(item: MyEntity) {
         __db.assertNotSuspendingTransaction()
         __db.beginTransaction()
         try {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_variableProperty_java.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_variableProperty_java.kt
index 9ffae96..127eb24 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_variableProperty_java.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_variableProperty_java.kt
@@ -12,7 +12,6 @@
 import kotlin.Long
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -30,7 +29,7 @@
             public override fun createQuery(): String =
                 "INSERT OR ABORT INTO `MyEntity` (`mValue`,`mNullableValue`) VALUES (?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.getValue())
                 val _tmpMNullableValue: String? = entity.getNullableValue()
                 if (_tmpMNullableValue == null) {
@@ -42,7 +41,7 @@
         }
     }
 
-    public override fun addEntity(item: MyEntity): Unit {
+    public override fun addEntity(item: MyEntity) {
         __db.assertNotSuspendingTransaction()
         __db.beginTransaction()
         try {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/preparedQueryAdapter.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/preparedQueryAdapter.kt
index 8ce5067..687bc16 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/preparedQueryAdapter.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/preparedQueryAdapter.kt
@@ -7,7 +7,6 @@
 import kotlin.Long
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -53,7 +52,7 @@
         }
     }
 
-    public override fun insertEntity(id: Long): Unit {
+    public override fun insertEntity(id: Long) {
         __db.assertNotSuspendingTransaction()
         val _stmt: SupportSQLiteStatement = __preparedStmtOfInsertEntity.acquire()
         var _argIndex: Int = 1
@@ -90,7 +89,7 @@
         }
     }
 
-    public override fun updateEntity(text: String): Unit {
+    public override fun updateEntity(text: String) {
         __db.assertNotSuspendingTransaction()
         val _stmt: SupportSQLiteStatement = __preparedStmtOfUpdateEntity.acquire()
         var _argIndex: Int = 1
@@ -129,7 +128,7 @@
         }
     }
 
-    public override fun deleteEntity(): Unit {
+    public override fun deleteEntity() {
         __db.assertNotSuspendingTransaction()
         val _stmt: SupportSQLiteStatement = __preparedStmtOfDeleteEntity.acquire()
         try {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations.kt
index e2a614c..cf8c543 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations.kt
@@ -17,7 +17,6 @@
 import kotlin.Long
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.collections.Set
 import kotlin.jvm.JvmStatic
@@ -150,7 +149,7 @@
         }
     }
 
-    private fun __fetchRelationshipArtistAsArtist(_map: HashMap<Long, Artist?>): Unit {
+    private fun __fetchRelationshipArtistAsArtist(_map: HashMap<Long, Artist?>) {
         val __mapKeySet: Set<Long> = _map.keys
         if (__mapKeySet.isEmpty()) {
             return
@@ -197,7 +196,7 @@
         }
     }
 
-    private fun __fetchRelationshipSongAsSong(_map: HashMap<Long, ArrayList<Song>>): Unit {
+    private fun __fetchRelationshipSongAsSong(_map: HashMap<Long, ArrayList<Song>>) {
         val __mapKeySet: Set<Long> = _map.keys
         if (__mapKeySet.isEmpty()) {
             return
@@ -248,7 +247,7 @@
         }
     }
 
-    private fun __fetchRelationshipSongAsSong_1(_map: HashMap<Long, ArrayList<Song>>): Unit {
+    private fun __fetchRelationshipSongAsSong_1(_map: HashMap<Long, ArrayList<Song>>) {
         val __mapKeySet: Set<Long> = _map.keys
         if (__mapKeySet.isEmpty()) {
             return
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_arrayMap.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_arrayMap.kt
index e4c901e..7d2189f 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_arrayMap.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_arrayMap.kt
@@ -17,7 +17,6 @@
 import kotlin.Long
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.collections.Set
 import kotlin.jvm.JvmStatic
@@ -150,7 +149,7 @@
         }
     }
 
-    private fun __fetchRelationshipArtistAsArtist(_map: ArrayMap<Long, Artist?>): Unit {
+    private fun __fetchRelationshipArtistAsArtist(_map: ArrayMap<Long, Artist?>) {
         val __mapKeySet: Set<Long> = _map.keys
         if (__mapKeySet.isEmpty()) {
             return
@@ -197,7 +196,7 @@
         }
     }
 
-    private fun __fetchRelationshipSongAsSong(_map: ArrayMap<Long, ArrayList<Song>>): Unit {
+    private fun __fetchRelationshipSongAsSong(_map: ArrayMap<Long, ArrayList<Song>>) {
         val __mapKeySet: Set<Long> = _map.keys
         if (__mapKeySet.isEmpty()) {
             return
@@ -248,7 +247,7 @@
         }
     }
 
-    private fun __fetchRelationshipSongAsSong_1(_map: ArrayMap<Long, ArrayList<Song>>): Unit {
+    private fun __fetchRelationshipSongAsSong_1(_map: ArrayMap<Long, ArrayList<Song>>) {
         val __mapKeySet: Set<Long> = _map.keys
         if (__mapKeySet.isEmpty()) {
             return
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_byteBufferKey.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_byteBufferKey.kt
index 576dfdc..c8e642f 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_byteBufferKey.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_byteBufferKey.kt
@@ -18,7 +18,6 @@
 import kotlin.Long
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.collections.Set
 import kotlin.jvm.JvmStatic
@@ -75,7 +74,7 @@
         }
     }
 
-    private fun __fetchRelationshipArtistAsArtist(_map: HashMap<ByteBuffer, Artist?>): Unit {
+    private fun __fetchRelationshipArtistAsArtist(_map: HashMap<ByteBuffer, Artist?>) {
         val __mapKeySet: Set<ByteBuffer> = _map.keys
         if (__mapKeySet.isEmpty()) {
             return
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_longSparseArray.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_longSparseArray.kt
index 3100619..0a73c03 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_longSparseArray.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_longSparseArray.kt
@@ -17,7 +17,6 @@
 import kotlin.Long
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -149,7 +148,7 @@
         }
     }
 
-    private fun __fetchRelationshipArtistAsArtist(_map: LongSparseArray<Artist?>): Unit {
+    private fun __fetchRelationshipArtistAsArtist(_map: LongSparseArray<Artist?>) {
         if (_map.isEmpty()) {
             return
         }
@@ -196,7 +195,7 @@
         }
     }
 
-    private fun __fetchRelationshipSongAsSong(_map: LongSparseArray<ArrayList<Song>>): Unit {
+    private fun __fetchRelationshipSongAsSong(_map: LongSparseArray<ArrayList<Song>>) {
         if (_map.isEmpty()) {
             return
         }
@@ -247,7 +246,7 @@
         }
     }
 
-    private fun __fetchRelationshipSongAsSong_1(_map: LongSparseArray<ArrayList<Song>>): Unit {
+    private fun __fetchRelationshipSongAsSong_1(_map: LongSparseArray<ArrayList<Song>>) {
         if (_map.isEmpty()) {
             return
         }
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_nullable.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_nullable.kt
index 5ab786c..4618226 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_nullable.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_nullable.kt
@@ -17,7 +17,6 @@
 import kotlin.Long
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.collections.Set
 import kotlin.jvm.JvmStatic
@@ -165,7 +164,7 @@
         }
     }
 
-    private fun __fetchRelationshipArtistAsArtist(_map: HashMap<Long, Artist?>): Unit {
+    private fun __fetchRelationshipArtistAsArtist(_map: HashMap<Long, Artist?>) {
         val __mapKeySet: Set<Long> = _map.keys
         if (__mapKeySet.isEmpty()) {
             return
@@ -212,7 +211,7 @@
         }
     }
 
-    private fun __fetchRelationshipSongAsSong(_map: HashMap<Long, ArrayList<Song>>): Unit {
+    private fun __fetchRelationshipSongAsSong(_map: HashMap<Long, ArrayList<Song>>) {
         val __mapKeySet: Set<Long> = _map.keys
         if (__mapKeySet.isEmpty()) {
             return
@@ -273,7 +272,7 @@
         }
     }
 
-    private fun __fetchRelationshipSongAsSong_1(_map: HashMap<Long, ArrayList<Song>>): Unit {
+    private fun __fetchRelationshipSongAsSong_1(_map: HashMap<Long, ArrayList<Song>>) {
         val __mapKeySet: Set<Long> = _map.keys
         if (__mapKeySet.isEmpty()) {
             return
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_rx2.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_rx2.kt
index 57294c6..dd1a9ca 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_rx2.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_rx2.kt
@@ -13,7 +13,6 @@
 import kotlin.Long
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -37,7 +36,7 @@
             public override fun createQuery(): String =
                 "INSERT OR ABORT INTO `MyEntity` (`pk`,`other`) VALUES (?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 statement.bindString(2, entity.other)
             }
@@ -45,7 +44,7 @@
         this.__deletionAdapterOfMyEntity = object : EntityDeletionOrUpdateAdapter<MyEntity>(__db) {
             public override fun createQuery(): String = "DELETE FROM `MyEntity` WHERE `pk` = ?"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
             }
         }
@@ -53,7 +52,7 @@
             public override fun createQuery(): String =
                 "UPDATE OR ABORT `MyEntity` SET `pk` = ?,`other` = ? WHERE `pk` = ?"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 statement.bindString(2, entity.other)
                 statement.bindLong(3, entity.pk.toLong())
@@ -64,7 +63,7 @@
             public override fun createQuery(): String =
                 "INSERT INTO `MyEntity` (`pk`,`other`) VALUES (?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 statement.bindString(2, entity.other)
             }
@@ -72,7 +71,7 @@
             public override fun createQuery(): String =
                 "UPDATE `MyEntity` SET `pk` = ?,`other` = ? WHERE `pk` = ?"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 statement.bindString(2, entity.other)
                 statement.bindLong(3, entity.pk.toLong())
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_rx3.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_rx3.kt
index 614e7de..bbab573 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_rx3.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_rx3.kt
@@ -13,7 +13,6 @@
 import kotlin.Long
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -37,7 +36,7 @@
             public override fun createQuery(): String =
                 "INSERT OR ABORT INTO `MyEntity` (`pk`,`other`) VALUES (?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 statement.bindString(2, entity.other)
             }
@@ -45,7 +44,7 @@
         this.__deletionAdapterOfMyEntity = object : EntityDeletionOrUpdateAdapter<MyEntity>(__db) {
             public override fun createQuery(): String = "DELETE FROM `MyEntity` WHERE `pk` = ?"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
             }
         }
@@ -53,7 +52,7 @@
             public override fun createQuery(): String =
                 "UPDATE OR ABORT `MyEntity` SET `pk` = ?,`other` = ? WHERE `pk` = ?"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 statement.bindString(2, entity.other)
                 statement.bindLong(3, entity.pk.toLong())
@@ -64,7 +63,7 @@
             public override fun createQuery(): String =
                 "INSERT INTO `MyEntity` (`pk`,`other`) VALUES (?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 statement.bindString(2, entity.other)
             }
@@ -72,7 +71,7 @@
             public override fun createQuery(): String =
                 "UPDATE `MyEntity` SET `pk` = ?,`other` = ? WHERE `pk` = ?"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 statement.bindString(2, entity.other)
                 statement.bindLong(3, entity.pk.toLong())
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_suspend.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_suspend.kt
index 01c90ca..3658985 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_suspend.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_suspend.kt
@@ -11,7 +11,6 @@
 import kotlin.Long
 import kotlin.String
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -35,7 +34,7 @@
             public override fun createQuery(): String =
                 "INSERT OR ABORT INTO `MyEntity` (`pk`,`other`) VALUES (?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 statement.bindString(2, entity.other)
             }
@@ -43,7 +42,7 @@
         this.__deletionAdapterOfMyEntity = object : EntityDeletionOrUpdateAdapter<MyEntity>(__db) {
             public override fun createQuery(): String = "DELETE FROM `MyEntity` WHERE `pk` = ?"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
             }
         }
@@ -51,7 +50,7 @@
             public override fun createQuery(): String =
                 "UPDATE OR ABORT `MyEntity` SET `pk` = ?,`other` = ? WHERE `pk` = ?"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 statement.bindString(2, entity.other)
                 statement.bindLong(3, entity.pk.toLong())
@@ -62,7 +61,7 @@
             public override fun createQuery(): String =
                 "INSERT INTO `MyEntity` (`pk`,`other`) VALUES (?,?)"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 statement.bindString(2, entity.other)
             }
@@ -70,7 +69,7 @@
             public override fun createQuery(): String =
                 "UPDATE `MyEntity` SET `pk` = ?,`other` = ? WHERE `pk` = ?"
 
-            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity): Unit {
+            public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
                 statement.bindLong(1, entity.pk.toLong())
                 statement.bindString(2, entity.other)
                 statement.bindLong(3, entity.pk.toLong())
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/transactionMethodAdapter_abstractClass.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/transactionMethodAdapter_abstractClass.kt
index 361a336..4093cf2 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/transactionMethodAdapter_abstractClass.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/transactionMethodAdapter_abstractClass.kt
@@ -4,7 +4,6 @@
 import javax.`annotation`.processing.Generated
 import kotlin.Long
 import kotlin.Suppress
-import kotlin.Unit
 import kotlin.collections.List
 import kotlin.jvm.JvmStatic
 
@@ -18,7 +17,7 @@
         this.__db = __db
     }
 
-    public override fun baseConcrete(): Unit {
+    public override fun baseConcrete() {
         __db.beginTransaction()
         try {
             super@MyDao_Impl.baseConcrete()
@@ -28,13 +27,13 @@
         }
     }
 
-    public override suspend fun baseSuspendConcrete(): Unit {
+    public override suspend fun baseSuspendConcrete() {
         __db.withTransaction {
             super@MyDao_Impl.baseSuspendConcrete()
         }
     }
 
-    public override fun concrete(): Unit {
+    public override fun concrete() {
         __db.beginTransaction()
         try {
             super@MyDao_Impl.concrete()
@@ -44,7 +43,7 @@
         }
     }
 
-    internal override fun concreteInternal(): Unit {
+    internal override fun concreteInternal() {
         __db.beginTransaction()
         try {
             super@MyDao_Impl.concreteInternal()
@@ -54,13 +53,13 @@
         }
     }
 
-    public override suspend fun suspendConcrete(): Unit {
+    public override suspend fun suspendConcrete() {
         __db.withTransaction {
             super@MyDao_Impl.suspendConcrete()
         }
     }
 
-    public override fun concreteWithVararg(vararg arr: Long): Unit {
+    public override fun concreteWithVararg(vararg arr: Long) {
         __db.beginTransaction()
         try {
             super@MyDao_Impl.concreteWithVararg(*arr)
@@ -70,7 +69,7 @@
         }
     }
 
-    public override suspend fun suspendConcreteWithVararg(vararg arr: Long): Unit {
+    public override suspend fun suspendConcreteWithVararg(vararg arr: Long) {
         __db.withTransaction {
             super@MyDao_Impl.suspendConcreteWithVararg(*arr)
         }
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/transactionMethodAdapter_interface.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/transactionMethodAdapter_interface.kt
index 0fec3af..9716d56 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/transactionMethodAdapter_interface.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/transactionMethodAdapter_interface.kt
@@ -21,7 +21,7 @@
         this.__db = __db
     }
 
-    public override fun baseConcrete(): Unit {
+    public override fun baseConcrete() {
         __db.beginTransaction()
         try {
             super@MyDao_Impl.baseConcrete()
@@ -31,13 +31,13 @@
         }
     }
 
-    public override suspend fun baseSuspendConcrete(): Unit {
+    public override suspend fun baseSuspendConcrete() {
         __db.withTransaction {
             super@MyDao_Impl.baseSuspendConcrete()
         }
     }
 
-    public override fun concrete(): Unit {
+    public override fun concrete() {
         __db.beginTransaction()
         try {
             super@MyDao_Impl.concrete()
@@ -71,7 +71,7 @@
         }
     }
 
-    public override fun concreteWithFunctionalParam(block: Function0<Unit>): Unit {
+    public override fun concreteWithFunctionalParam(block: Function0<Unit>) {
         __db.beginTransaction()
         try {
             super@MyDao_Impl.concreteWithFunctionalParam(block)
@@ -81,7 +81,7 @@
         }
     }
 
-    public override suspend fun suspendConcrete(): Unit {
+    public override suspend fun suspendConcrete() {
         __db.withTransaction {
             super@MyDao_Impl.suspendConcrete()
         }
@@ -92,7 +92,7 @@
     }
 
     public override suspend
-    fun suspendConcreteWithSuspendFunctionalParam(block: SuspendFunction0<Unit>): Unit {
+    fun suspendConcreteWithSuspendFunctionalParam(block: SuspendFunction0<Unit>) {
         __db.withTransaction {
             super@MyDao_Impl.suspendConcreteWithSuspendFunctionalParam(block)
         }
diff --git a/room/room-runtime/src/androidTest/java/androidx/room/AutoClosingRoomOpenHelperTest.kt b/room/room-runtime/src/androidTest/java/androidx/room/AutoClosingRoomOpenHelperTest.kt
index c8d60c9..e212492 100644
--- a/room/room-runtime/src/androidTest/java/androidx/room/AutoClosingRoomOpenHelperTest.kt
+++ b/room/room-runtime/src/androidTest/java/androidx/room/AutoClosingRoomOpenHelperTest.kt
@@ -29,7 +29,6 @@
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.SdkSuppress
 import androidx.testutils.assertThrows
-import com.google.common.truth.Truth
 import java.io.IOException
 import java.util.concurrent.Executors
 import java.util.concurrent.TimeUnit
@@ -238,7 +237,7 @@
         db.query("select * from users").useCursor {
             assertThat(it.moveToFirst()).isTrue()
             assertThat(it.getInt(0)).isEqualTo(123)
-            Truth.assertThat(it.getDouble(1)).isWithin(.01).of(1.23)
+            assertThat(it.getDouble(1)).isWithin(.01).of(1.23)
 
             assertThat(it.getBlob(2)).isEqualTo(byteArrayOf(1, 2, 3))
             assertThat(it.isNull(3)).isTrue()
diff --git a/room/room-runtime/src/main/java/androidx/room/QueryInterceptorStatement.kt b/room/room-runtime/src/main/java/androidx/room/QueryInterceptorStatement.kt
index 50cf02c..37eca34 100644
--- a/room/room-runtime/src/main/java/androidx/room/QueryInterceptorStatement.kt
+++ b/room/room-runtime/src/main/java/androidx/room/QueryInterceptorStatement.kt
@@ -67,7 +67,7 @@
     }
 
     override fun bindNull(index: Int) {
-        saveArgsToCache(index, arrayOf(*bindArgsCache.toTypedArray()))
+        saveArgsToCache(index, null)
         delegate.bindNull(index)
     }
 
diff --git a/room/room-testing/src/androidTest/java/androidx/room/testing/kotlintestapp/migration/AutoMigrationAndMigrationTest.kt b/room/room-testing/src/androidTest/java/androidx/room/testing/kotlintestapp/migration/AutoMigrationAndMigrationTest.kt
index b0231ab..7269905 100644
--- a/room/room-testing/src/androidTest/java/androidx/room/testing/kotlintestapp/migration/AutoMigrationAndMigrationTest.kt
+++ b/room/room-testing/src/androidTest/java/androidx/room/testing/kotlintestapp/migration/AutoMigrationAndMigrationTest.kt
@@ -24,7 +24,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.platform.app.InstrumentationRegistry
-import com.google.common.truth.Truth
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -64,7 +63,7 @@
                 MIGRATION_1_2
             )
         } catch (e: SQLiteException) {
-            Truth.assertThat(e.message).containsMatch("no such table: Entity0")
+            assertThat(e.message).contains("no such table: Entity0")
         }
     }
 
diff --git a/samples/AndroidXDemos/build.gradle b/samples/AndroidXDemos/build.gradle
index 56df8c2..810fd6b 100644
--- a/samples/AndroidXDemos/build.gradle
+++ b/samples/AndroidXDemos/build.gradle
@@ -10,7 +10,6 @@
     implementation(project(":cardview:cardview"))
     implementation(project(":drawerlayout:drawerlayout"))
     implementation(project(":gridlayout:gridlayout"))
-    implementation(project(":mediarouter:mediarouter"))
     implementation(project(":palette:palette"))
     implementation(project(":recyclerview:recyclerview"))
     implementation(project(":recyclerview:recyclerview-selection"))
diff --git a/samples/MediaRoutingDemo/src/main/AndroidManifest.xml b/samples/MediaRoutingDemo/src/main/AndroidManifest.xml
index bb037aa..8afa6de 100644
--- a/samples/MediaRoutingDemo/src/main/AndroidManifest.xml
+++ b/samples/MediaRoutingDemo/src/main/AndroidManifest.xml
@@ -24,6 +24,7 @@
 
     <uses-permission android:name="android.permission.BLUETOOTH"
         android:maxSdkVersion="30" />
+    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
     <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
 
     <application
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRouteItem.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRouteItem.java
index 2bffdee..c92ced8 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRouteItem.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRouteItem.java
@@ -18,11 +18,8 @@
 
 import android.text.TextUtils;
 
-import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
 
 import javax.annotation.Nullable;
@@ -32,31 +29,11 @@
  *
  * Can represent media routers' routes, bluetooth routes, or audio routes.
  */
-public final class SystemRouteItem {
-
-    public static final int ROUTE_SOURCE_MEDIA_ROUTER = 0;
-    public static final int ROUTE_SOURCE_MEDIA_ROUTER2 = 1;
-    public static final int ROUTE_SOURCE_ANDROIDX_ROUTER = 2;
-    public static final int ROUTE_SOURCE_BLUETOOTH_MANAGER = 3;
-    public static final int ROUTE_SOURCE_AUDIO_MANAGER = 4;
-
-    @IntDef({
-            ROUTE_SOURCE_MEDIA_ROUTER,
-            ROUTE_SOURCE_MEDIA_ROUTER2,
-            ROUTE_SOURCE_ANDROIDX_ROUTER,
-            ROUTE_SOURCE_BLUETOOTH_MANAGER,
-            ROUTE_SOURCE_AUDIO_MANAGER
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Type {
-    }
+public final class SystemRouteItem implements SystemRoutesAdapterItem {
 
     @NonNull
     private final String mId;
 
-    @Type
-    private final int mType;
-
     @NonNull
     private final String mName;
 
@@ -72,7 +49,6 @@
 
         mId = builder.mId;
         mName = builder.mName;
-        mType = builder.mType;
 
         mAddress = builder.mAddress;
         mDescription = builder.mDescription;
@@ -87,15 +63,6 @@
     }
 
     /**
-     * Returns a route source.
-     *
-     * see {@link SystemRouteItem.Type}
-     */
-    public int getType() {
-        return mType;
-    }
-
-    /**
      * Returns a human-readable name of the route.
      */
     @NonNull
@@ -124,14 +91,14 @@
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         SystemRouteItem that = (SystemRouteItem) o;
-        return mType == that.mType && mId.equals(that.mId) && mName.equals(that.mName)
+        return mId.equals(that.mId) && mName.equals(that.mName)
                 && Objects.equals(mAddress, that.mAddress) && Objects.equals(
                 mDescription, that.mDescription);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mId, mType, mName, mAddress, mDescription);
+        return Objects.hash(mId, mName, mAddress, mDescription);
     }
 
     /**
@@ -141,8 +108,6 @@
 
         @NonNull
         private final String mId;
-        @Type
-        private final int mType;
 
         @NonNull
         private String mName;
@@ -153,9 +118,8 @@
         @Nullable
         private String mDescription;
 
-        public Builder(@NonNull String id, @Type int type) {
+        public Builder(@NonNull String id) {
             mId = id;
-            mType = type;
         }
 
         /**
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRouteUtils.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRouteUtils.java
index 2be09a0..461350d 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRouteUtils.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRouteUtils.java
@@ -39,21 +39,21 @@
     }
 
     /**
-     * Converts {@link SystemRouteItem.Type} to a human-readable string.
+     * Converts {@link SystemRoutesSourceItem.Type} to a human-readable string.
      */
     @NonNull
-    public static String getDescriptionForSource(@SystemRouteItem.Type int type) {
+    public static String getDescriptionForSource(@SystemRoutesSourceItem.Type int type) {
         switch (type) {
-            case SystemRouteItem.ROUTE_SOURCE_MEDIA_ROUTER:
-                return "Media Router";
-            case SystemRouteItem.ROUTE_SOURCE_MEDIA_ROUTER2:
-                return "Media Router 2";
-            case SystemRouteItem.ROUTE_SOURCE_BLUETOOTH_MANAGER:
-                return "Bluetooth Manager";
-            case SystemRouteItem.ROUTE_SOURCE_ANDROIDX_ROUTER:
-                return "Androidx Router";
-            case SystemRouteItem.ROUTE_SOURCE_AUDIO_MANAGER:
-                return "Audio Manager";
+            case SystemRoutesSourceItem.ROUTE_SOURCE_MEDIA_ROUTER:
+                return "Legacy MediaRouter";
+            case SystemRoutesSourceItem.ROUTE_SOURCE_MEDIA_ROUTER2:
+                return "MediaRouter2";
+            case SystemRoutesSourceItem.ROUTE_SOURCE_BLUETOOTH_MANAGER:
+                return "BluetoothManager";
+            case SystemRoutesSourceItem.ROUTE_SOURCE_ANDROIDX_ROUTER:
+                return "AndroidX MediaRouter";
+            case SystemRoutesSourceItem.ROUTE_SOURCE_AUDIO_MANAGER:
+                return "AudioManager";
             default:
                 throw new IllegalArgumentException("Unknown system route type: " + type);
         }
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRoutesAdapter.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRoutesAdapter.java
index 543dcd8..f1dc42f 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRoutesAdapter.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRoutesAdapter.java
@@ -32,72 +32,59 @@
 
 import java.util.List;
 
-class SystemRoutesAdapter extends RecyclerView.Adapter<SystemRoutesAdapter.ViewHolder> {
+/**
+ * @link RecyclerView.Adapter} for showing system route sources and the routes discovered by each
+ * source.
+ */
+class SystemRoutesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
 
-    private final AsyncListDiffer<SystemRouteItem> mListDiffer =
+    private static final int VIEW_TYPE_HEADER = 0;
+    private static final int VIEW_TYPE_ITEM = 1;
+
+    private final AsyncListDiffer<SystemRoutesAdapterItem> mListDiffer =
             new AsyncListDiffer<>(this, new ItemCallback());
 
-    public void setItems(@NonNull List<SystemRouteItem> newItems) {
+    public void setItems(@NonNull List<SystemRoutesAdapterItem> newItems) {
         mListDiffer.submitList(newItems);
     }
 
     @NonNull
-    public List<SystemRouteItem> getItems() {
+    public List<SystemRoutesAdapterItem> getItems() {
         return mListDiffer.getCurrentList();
     }
 
     @NonNull
     @Override
-    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
         Context context = parent.getContext();
-        View view = LayoutInflater.from(context).inflate(R.layout.item_system_route, parent, false);
-        return new ViewHolder(view);
-    }
-
-    @Override
-    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
-        SystemRouteItem route = getItems().get(position);
-
-        holder.mRouteSourceTextView.setText(
-                SystemRouteUtils.getDescriptionForSource(route.getType()));
-        holder.mRouteNameTextView.setText(route.getName());
-        holder.mRouteIdTextView.setText(route.getId());
-
-        showViewIfNotNull(holder.mRouteAddressTextView, route.getAddress());
-        holder.mRouteAddressTextView.setText(route.getAddress());
-
-        showViewIfNotNull(holder.mRouteDescriptionTextView, route.getDescription());
-        holder.mRouteDescriptionTextView.setText(route.getDescription());
-    }
-
-    @Override
-    public void onBindViewHolder(@NonNull ViewHolder holder, int position,
-            @NonNull List<Object> payloads) {
-        if (payloads.isEmpty()) {
-            onBindViewHolder(holder, position);
-            return;
+        if (viewType == VIEW_TYPE_HEADER) {
+            View view = LayoutInflater.from(context).inflate(R.layout.item_system_route_header,
+                    parent, false);
+            return new HeaderViewHolder(view);
+        } else {
+            View view = LayoutInflater.from(context).inflate(R.layout.item_system_route, parent,
+                    false);
+            return new ItemViewHolder(view);
         }
+    }
 
-        for (Object rawPayload : payloads) {
-            if (!(rawPayload instanceof Payload)) {
-                continue;
-            }
+    @Override
+    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
+        SystemRoutesAdapterItem routeItem = getItems().get(position);
+        if (routeItem instanceof SystemRoutesSourceItem && holder instanceof HeaderViewHolder) {
+            ((HeaderViewHolder) holder).bind((SystemRoutesSourceItem) routeItem);
+        } else if (routeItem instanceof SystemRouteItem && holder instanceof ItemViewHolder) {
+            ((ItemViewHolder) holder).bind((SystemRouteItem) routeItem);
+        }
+    }
 
-            Payload payload = (Payload) rawPayload;
-
-            if (payload.mName != null) {
-                holder.mRouteNameTextView.setText(payload.mName);
-            }
-
-            showViewIfNotNull(holder.mRouteAddressTextView, payload.mAddress);
-            if (payload.mAddress != null) {
-                holder.mRouteAddressTextView.setText(payload.mAddress);
-            }
-
-            showViewIfNotNull(holder.mRouteDescriptionTextView, payload.mDescription);
-            if (payload.mDescription != null) {
-                holder.mRouteDescriptionTextView.setText(payload.mDescription);
-            }
+    @Override
+    public int getItemViewType(int position) {
+        SystemRoutesAdapterItem routeItem = getItems().get(position);
+        if (routeItem instanceof SystemRoutesSourceItem) {
+            return VIEW_TYPE_HEADER;
+        } else {
+            return VIEW_TYPE_ITEM;
         }
     }
 
@@ -106,67 +93,81 @@
         return getItems().size();
     }
 
-    static class ViewHolder extends RecyclerView.ViewHolder {
+    static class HeaderViewHolder extends RecyclerView.ViewHolder {
 
-        final AppCompatTextView mRouteSourceTextView;
-        final AppCompatTextView mRouteNameTextView;
-        final AppCompatTextView mRouteIdTextView;
-        final AppCompatTextView mRouteAddressTextView;
-        final AppCompatTextView mRouteDescriptionTextView;
+        private final AppCompatTextView mHeaderTitleTextView;
 
-        ViewHolder(@NonNull View itemView) {
+        HeaderViewHolder(@NonNull View itemView) {
             super(itemView);
 
-            mRouteSourceTextView = itemView.findViewById(R.id.route_source);
+            mHeaderTitleTextView = itemView.findViewById(R.id.header_title);
+        }
+
+        void bind(SystemRoutesSourceItem systemRoutesSourceItem) {
+            mHeaderTitleTextView.setText(
+                    SystemRouteUtils.getDescriptionForSource(systemRoutesSourceItem.getType()));
+        }
+    }
+
+    static class ItemViewHolder extends RecyclerView.ViewHolder {
+
+        private final AppCompatTextView mRouteNameTextView;
+        private final AppCompatTextView mRouteIdTextView;
+        private final AppCompatTextView mRouteAddressTextView;
+        private final AppCompatTextView mRouteDescriptionTextView;
+
+        ItemViewHolder(@NonNull View itemView) {
+            super(itemView);
+
             mRouteNameTextView = itemView.findViewById(R.id.route_name);
             mRouteIdTextView = itemView.findViewById(R.id.route_id);
             mRouteAddressTextView = itemView.findViewById(R.id.route_address);
             mRouteDescriptionTextView = itemView.findViewById(R.id.route_description);
         }
-    }
 
-    private static class ItemCallback extends DiffUtil.ItemCallback<SystemRouteItem> {
+        void bind(SystemRouteItem systemRouteItem) {
+            mRouteNameTextView.setText(systemRouteItem.getName());
+            mRouteIdTextView.setText(systemRouteItem.getId());
 
-        @Override
-        public boolean areItemsTheSame(@NonNull SystemRouteItem oldItem,
-                @NonNull SystemRouteItem newItem) {
-            return oldItem.getId().equals(newItem.getId())
-                    && oldItem.getType() == newItem.getType();
-        }
+            showViewIfNotNull(mRouteAddressTextView, systemRouteItem.getAddress());
+            if (systemRouteItem.getAddress() != null) {
+                mRouteAddressTextView.setText(systemRouteItem.getAddress());
+            }
 
-        @Override
-        public boolean areContentsTheSame(@NonNull SystemRouteItem oldItem,
-                @NonNull SystemRouteItem newItem) {
-            return oldItem.equals(newItem);
-        }
-
-        @Nullable
-        @Override
-        public Payload getChangePayload(@NonNull SystemRouteItem oldItem,
-                @NonNull SystemRouteItem newItem) {
-            return new Payload(takeIfChanged(oldItem.getName(), newItem.getName()),
-                    takeIfChanged(oldItem.getAddress(), newItem.getAddress()),
-                    takeIfChanged(oldItem.getDescription(), newItem.getDescription()));
+            showViewIfNotNull(mRouteDescriptionTextView, systemRouteItem.getDescription());
+            if (systemRouteItem.getDescription() != null) {
+                mRouteDescriptionTextView.setText(systemRouteItem.getDescription());
+            }
         }
     }
 
-    private static class Payload {
+    private static class ItemCallback extends DiffUtil.ItemCallback<SystemRoutesAdapterItem> {
+        @Override
+        public boolean areItemsTheSame(@NonNull SystemRoutesAdapterItem oldItem,
+                @NonNull SystemRoutesAdapterItem newItem) {
+            if (oldItem instanceof SystemRouteItem && newItem instanceof SystemRouteItem) {
+                return ((SystemRouteItem) oldItem).getId().equals(
+                        ((SystemRouteItem) newItem).getId());
+            } else if (oldItem instanceof SystemRoutesSourceItem
+                    && newItem instanceof SystemRoutesSourceItem) {
+                return ((SystemRoutesSourceItem) oldItem).getType()
+                        == ((SystemRoutesSourceItem) newItem).getType();
+            } else {
+                return false;
+            }
+        }
 
-        @Nullable
-        final String mName;
-
-        @Nullable
-        final String mAddress;
-
-        @Nullable
-        final String mDescription;
-
-
-        Payload(@Nullable String name, @Nullable String address,
-                @Nullable String description) {
-            mName = name;
-            mAddress = address;
-            mDescription = description;
+        @Override
+        public boolean areContentsTheSame(@NonNull SystemRoutesAdapterItem oldItem,
+                @NonNull SystemRoutesAdapterItem newItem) {
+            if (oldItem instanceof SystemRouteItem && newItem instanceof SystemRouteItem) {
+                return oldItem.equals(newItem);
+            } else if (oldItem instanceof SystemRoutesSourceItem
+                    && newItem instanceof SystemRoutesSourceItem) {
+                return oldItem.equals(newItem);
+            } else {
+                return false;
+            }
         }
     }
 
@@ -177,21 +178,4 @@
             view.setVisibility(View.VISIBLE);
         }
     }
-
-    @Nullable
-    private static <T> T takeIfChanged(@Nullable T oldObj, @Nullable T newObj) {
-        if (oldObj == null && newObj == null) {
-            return null;
-        }
-
-        if (oldObj == null || newObj == null) {
-            return newObj;
-        }
-
-        if (oldObj.equals(newObj)) {
-            return null;
-        }
-
-        return newObj;
-    }
 }
diff --git a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence1.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRoutesAdapterItem.java
similarity index 69%
copy from lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence1.java
copy to samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRoutesAdapterItem.java
index ae9c85f..f38be06 100644
--- a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence1.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRoutesAdapterItem.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-package androidx.lifecycle.observers;
+package com.example.androidx.mediarouting.activities.systemrouting;
 
-public class DerivedSequence1 extends Base {
-
-    public void something() {
-    }
+/**
+ * A Marker interface representing an item in the {@link SystemRoutesAdapter}.
+ */
+public interface SystemRoutesAdapterItem {
 }
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRoutesSourceItem.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRoutesSourceItem.java
new file mode 100644
index 0000000..243cd81
--- /dev/null
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRoutesSourceItem.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.androidx.mediarouting.activities.systemrouting;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * A model that holds data about a system routes source.
+ */
+public final class SystemRoutesSourceItem implements SystemRoutesAdapterItem {
+    public static final int ROUTE_SOURCE_MEDIA_ROUTER = 0;
+    public static final int ROUTE_SOURCE_MEDIA_ROUTER2 = 1;
+    public static final int ROUTE_SOURCE_ANDROIDX_ROUTER = 2;
+    public static final int ROUTE_SOURCE_BLUETOOTH_MANAGER = 3;
+    public static final int ROUTE_SOURCE_AUDIO_MANAGER = 4;
+
+    @IntDef({
+            ROUTE_SOURCE_MEDIA_ROUTER,
+            ROUTE_SOURCE_MEDIA_ROUTER2,
+            ROUTE_SOURCE_ANDROIDX_ROUTER,
+            ROUTE_SOURCE_BLUETOOTH_MANAGER,
+            ROUTE_SOURCE_AUDIO_MANAGER
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {
+    }
+
+    @Type
+    private final int mType;
+
+    private SystemRoutesSourceItem(@NonNull Builder builder) {
+        mType = builder.mType;
+    }
+
+    /**
+     * Returns a route source item type.
+     * see {@link SystemRoutesSourceItem.Type}
+     */
+    public int getType() {
+        return mType;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        SystemRoutesSourceItem that = (SystemRoutesSourceItem) o;
+        return mType == that.mType;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mType);
+    }
+
+    /**
+     * Helps to construct {@link SystemRoutesSourceItem}.
+     */
+    public static final class Builder {
+
+        @Type
+        private final int mType;
+
+        public Builder(@Type int type) {
+            mType = type;
+        }
+
+        /**
+         * Builds {@link SystemRoutesSourceItem}.
+         */
+        @NonNull
+        public SystemRoutesSourceItem build() {
+            return new SystemRoutesSourceItem(this);
+        }
+    }
+}
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRoutingActivity.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRoutingActivity.java
index cabff1a..965156a 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRoutingActivity.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/SystemRoutingActivity.java
@@ -16,10 +16,12 @@
 
 package com.example.androidx.mediarouting.activities.systemrouting;
 
-import android.Manifest;
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_SCAN;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.os.Build;
 import android.os.Bundle;
 import android.widget.Toast;
@@ -51,8 +53,16 @@
 
     private static final int REQUEST_CODE_BLUETOOTH_CONNECT = 4199;
 
+    @NonNull
     private final SystemRoutesAdapter mSystemRoutesAdapter = new SystemRoutesAdapter();
+    @NonNull
     private final List<SystemRoutesSource> mSystemRoutesSources = new ArrayList<>();
+    @NonNull
+    private final SystemRoutesSourceCallback mSystemRoutesSourceCallback =
+            new SystemRoutesSourceCallback();
+
+    @NonNull
+    private SwipeRefreshLayout mSwipeRefreshLayout;
 
     /**
      * Creates and launches an intent to start current activity.
@@ -68,16 +78,12 @@
         setContentView(R.layout.activity_system_routing);
 
         RecyclerView recyclerView = findViewById(R.id.recycler_view);
-        SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.pull_to_refresh_layout);
+        mSwipeRefreshLayout = findViewById(R.id.pull_to_refresh_layout);
 
         recyclerView.setAdapter(mSystemRoutesAdapter);
         recyclerView.setLayoutManager(new LinearLayoutManager(this));
 
-        swipeRefreshLayout.setOnRefreshListener(
-                () -> {
-                    refreshSystemRoutesList();
-                    swipeRefreshLayout.setRefreshing(false);
-                });
+        mSwipeRefreshLayout.setOnRefreshListener(this::refreshSystemRoutesList);
 
         if (hasBluetoothPermission()) {
             initializeSystemRoutesSources();
@@ -88,13 +94,22 @@
     }
 
     @Override
+    protected void onDestroy() {
+        for (SystemRoutesSource source: mSystemRoutesSources) {
+            source.stop();
+        }
+
+        super.onDestroy();
+    }
+
+    @Override
     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
             @NonNull int[] grantResults) {
         super.onRequestPermissionsResult(requestCode, permissions, grantResults);
 
         if (requestCode == REQUEST_CODE_BLUETOOTH_CONNECT
                 && grantResults.length > 0) {
-            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+            if (grantResults[0] == PERMISSION_GRANTED) {
                 onBluetoothPermissionGranted();
             } else {
                 onBluetoothPermissionDenied();
@@ -103,23 +118,25 @@
     }
 
     private void refreshSystemRoutesList() {
-        List<SystemRouteItem> systemRoutes = new ArrayList<>();
+        List<SystemRoutesAdapterItem> systemRoutesSourceItems = new ArrayList<>();
         for (SystemRoutesSource source : mSystemRoutesSources) {
-            systemRoutes.addAll(source.fetchRoutes());
+            systemRoutesSourceItems.add(source.getSourceItem());
+            systemRoutesSourceItems.addAll(source.fetchSourceRouteItems());
         }
-        mSystemRoutesAdapter.setItems(systemRoutes);
+        mSystemRoutesAdapter.setItems(systemRoutesSourceItems);
+        mSwipeRefreshLayout.setRefreshing(false);
     }
 
     private boolean hasBluetoothPermission() {
-        return ContextCompat.checkSelfPermission(
-                        /* context= */ this, Manifest.permission.BLUETOOTH_CONNECT)
-                == PackageManager.PERMISSION_GRANTED;
+        return ContextCompat.checkSelfPermission(/* context= */ this, BLUETOOTH_CONNECT)
+                == PERMISSION_GRANTED
+                && ContextCompat.checkSelfPermission(/* context= */ this, BLUETOOTH_SCAN)
+                == PERMISSION_GRANTED;
     }
 
     private void requestBluetoothPermission() {
         ActivityCompat.requestPermissions(this,
-                new String[]{Manifest.permission.BLUETOOTH_CONNECT},
-                REQUEST_CODE_BLUETOOTH_CONNECT);
+                new String[]{BLUETOOTH_CONNECT, BLUETOOTH_SCAN}, REQUEST_CODE_BLUETOOTH_CONNECT);
     }
 
     private void onBluetoothPermissionGranted() {
@@ -154,5 +171,22 @@
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
             mSystemRoutesSources.add(AudioManagerSystemRoutesSource.create(/* context= */ this));
         }
+
+        for (SystemRoutesSource source: mSystemRoutesSources) {
+            source.setOnRoutesChangedListener(mSystemRoutesSourceCallback);
+            source.start();
+        }
+    }
+
+    private class SystemRoutesSourceCallback implements SystemRoutesSource.OnRoutesChangedListener {
+        @Override
+        public void onRouteAdded(@NonNull SystemRouteItem routeItem) {
+            refreshSystemRoutesList();
+        }
+
+        @Override
+        public void onRouteRemoved(@NonNull SystemRouteItem routeItem) {
+            refreshSystemRoutesList();
+        }
     }
 }
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/AndroidXMediaRouterSystemRoutesSource.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/AndroidXMediaRouterSystemRoutesSource.java
index 628fb0f..c8b5a0f 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/AndroidXMediaRouterSystemRoutesSource.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/AndroidXMediaRouterSystemRoutesSource.java
@@ -19,19 +19,37 @@
 import android.content.Context;
 
 import androidx.annotation.NonNull;
+import androidx.mediarouter.media.MediaControlIntent;
+import androidx.mediarouter.media.MediaRouteSelector;
 import androidx.mediarouter.media.MediaRouter;
 
 import com.example.androidx.mediarouting.activities.systemrouting.SystemRouteItem;
+import com.example.androidx.mediarouting.activities.systemrouting.SystemRoutesSourceItem;
 
 import java.util.ArrayList;
 import java.util.List;
 
 /** Implements {@link SystemRoutesSource} using {@link MediaRouter}. */
-public final class AndroidXMediaRouterSystemRoutesSource implements SystemRoutesSource {
+public final class AndroidXMediaRouterSystemRoutesSource extends SystemRoutesSource {
 
     @NonNull
     private final MediaRouter mMediaRouter;
 
+    @NonNull
+    private final MediaRouter.Callback mMediaRouterCallback = new MediaRouter.Callback() {
+        @Override
+        public void onRouteAdded(@NonNull MediaRouter router,
+                @NonNull MediaRouter.RouteInfo route) {
+            mOnRoutesChangedListener.onRouteAdded(createRouteItemFor(route));
+        }
+
+        @Override
+        public void onRouteRemoved(@NonNull MediaRouter router,
+                @NonNull MediaRouter.RouteInfo route) {
+            mOnRoutesChangedListener.onRouteRemoved(createRouteItemFor(route));
+        }
+    };
+
     /** Returns a new instance. */
     @NonNull
     public static AndroidXMediaRouterSystemRoutesSource create(@NonNull Context context) {
@@ -43,9 +61,32 @@
         mMediaRouter = mediaRouter;
     }
 
+    @Override
+    public void start() {
+        MediaRouteSelector selector = new MediaRouteSelector.Builder()
+                .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
+                .addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO)
+                .build();
+
+        mMediaRouter.addCallback(selector, mMediaRouterCallback);
+    }
+
+    @Override
+    public void stop() {
+        mMediaRouter.removeCallback(mMediaRouterCallback);
+    }
+
     @NonNull
     @Override
-    public List<SystemRouteItem> fetchRoutes() {
+    public SystemRoutesSourceItem getSourceItem() {
+        return new SystemRoutesSourceItem.Builder(
+                SystemRoutesSourceItem.ROUTE_SOURCE_ANDROIDX_ROUTER)
+                .build();
+    }
+
+    @NonNull
+    @Override
+    public List<SystemRouteItem> fetchSourceRouteItems() {
         List<SystemRouteItem> out = new ArrayList<>();
 
         for (MediaRouter.RouteInfo routeInfo : mMediaRouter.getRoutes()) {
@@ -53,18 +94,22 @@
                 continue;
             }
 
-            SystemRouteItem.Builder builder = new SystemRouteItem.Builder(routeInfo.getId(),
-                    SystemRouteItem.ROUTE_SOURCE_ANDROIDX_ROUTER)
-                    .setName(routeInfo.getName());
-
-            String description = routeInfo.getDescription();
-            if (description != null) {
-                builder.setDescription(description);
-            }
-
-            out.add(builder.build());
+            out.add(createRouteItemFor(routeInfo));
         }
 
         return out;
     }
+
+    @NonNull
+    private static SystemRouteItem createRouteItemFor(@NonNull MediaRouter.RouteInfo routeInfo) {
+        SystemRouteItem.Builder builder = new SystemRouteItem.Builder(routeInfo.getId())
+                .setName(routeInfo.getName());
+
+        String description = routeInfo.getDescription();
+        if (description != null) {
+            builder.setDescription(description);
+        }
+
+        return builder.build();
+    }
 }
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/AudioManagerSystemRoutesSource.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/AudioManagerSystemRoutesSource.java
index c821e2c..7d37502 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/AudioManagerSystemRoutesSource.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/AudioManagerSystemRoutesSource.java
@@ -17,6 +17,7 @@
 package com.example.androidx.mediarouting.activities.systemrouting.source;
 
 import android.content.Context;
+import android.media.AudioDeviceCallback;
 import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
 import android.os.Build;
@@ -26,17 +27,35 @@
 import androidx.annotation.RequiresApi;
 
 import com.example.androidx.mediarouting.activities.systemrouting.SystemRouteItem;
+import com.example.androidx.mediarouting.activities.systemrouting.SystemRoutesSourceItem;
 
 import java.util.ArrayList;
 import java.util.List;
 
 /** Implements {@link SystemRoutesSource} using {@link AudioManager}. */
 @RequiresApi(Build.VERSION_CODES.M)
-public final class AudioManagerSystemRoutesSource implements SystemRoutesSource {
+public final class AudioManagerSystemRoutesSource extends SystemRoutesSource {
 
     @NonNull
     private final AudioManager mAudioManager;
 
+    @NonNull
+    private final AudioDeviceCallback mAudioDeviceCallback = new AudioDeviceCallback() {
+        @Override
+        public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
+            for (AudioDeviceInfo audioDeviceInfo: addedDevices) {
+                mOnRoutesChangedListener.onRouteAdded(createRouteItemFor(audioDeviceInfo));
+            }
+        }
+
+        @Override
+        public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
+            for (AudioDeviceInfo audioDeviceInfo: removedDevices) {
+                mOnRoutesChangedListener.onRouteRemoved(createRouteItemFor(audioDeviceInfo));
+            }
+        }
+    };
+
     /** Returns a new instance. */
     @NonNull
     public static AudioManagerSystemRoutesSource create(@NonNull Context context) {
@@ -48,30 +67,51 @@
         mAudioManager = audioManager;
     }
 
+    @Override
+    public void start() {
+        mAudioManager.registerAudioDeviceCallback(mAudioDeviceCallback, /* handler= */ null);
+    }
+
+    @Override
+    public void stop() {
+        mAudioManager.unregisterAudioDeviceCallback(mAudioDeviceCallback);
+    }
+
     @NonNull
     @Override
-    public List<SystemRouteItem> fetchRoutes() {
+    public SystemRoutesSourceItem getSourceItem() {
+        return new SystemRoutesSourceItem.Builder(SystemRoutesSourceItem.ROUTE_SOURCE_AUDIO_MANAGER)
+                .build();
+    }
+
+    @NonNull
+    @Override
+    public List<SystemRouteItem> fetchSourceRouteItems() {
         List<SystemRouteItem> out = new ArrayList<>();
 
-        for (AudioDeviceInfo audioDeviceInfo :
-                mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)) {
-            SystemRouteItem.Builder builder = new SystemRouteItem.Builder(
-                    String.valueOf(audioDeviceInfo.getId()),
-                    SystemRouteItem.ROUTE_SOURCE_AUDIO_MANAGER)
-                    .setName(audioDeviceInfo.getProductName().toString());
-
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
-                builder.setAddress(Api28Impl.getAddress(audioDeviceInfo));
-            }
-
-            out.add(builder.build());
+        AudioDeviceInfo[] deviceInfos = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
+        for (AudioDeviceInfo audioDeviceInfo : deviceInfos) {
+            out.add(createRouteItemFor(audioDeviceInfo));
         }
 
         return out;
     }
 
+    @NonNull
+    private static SystemRouteItem createRouteItemFor(@NonNull AudioDeviceInfo audioDeviceInfo) {
+        SystemRouteItem.Builder builder = new SystemRouteItem.Builder(
+                String.valueOf(audioDeviceInfo.getId()))
+                .setName(audioDeviceInfo.getProductName().toString());
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+            builder.setAddress(Api28Impl.getAddress(audioDeviceInfo));
+        }
+
+        return builder.build();
+    }
+
     @RequiresApi(Build.VERSION_CODES.P)
-    static class Api28Impl {
+    private static final class Api28Impl {
         private Api28Impl() {
             // This class is not instantiable.
         }
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/BluetoothManagerSystemRoutesSource.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/BluetoothManagerSystemRoutesSource.java
index 560cd07..d58bd15 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/BluetoothManagerSystemRoutesSource.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/BluetoothManagerSystemRoutesSource.java
@@ -17,57 +17,133 @@
 package com.example.androidx.mediarouting.activities.systemrouting.source;
 
 import android.Manifest;
+import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothLeAudio;
 import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.os.Build;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RequiresPermission;
+import androidx.core.content.IntentCompat;
 
 import com.example.androidx.mediarouting.activities.systemrouting.SystemRouteItem;
+import com.example.androidx.mediarouting.activities.systemrouting.SystemRoutesSourceItem;
 
 import java.util.ArrayList;
 import java.util.List;
 
 /** Implements {@link SystemRoutesSource} using {@link BluetoothManager}. */
 @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
-public final class BluetoothManagerSystemRoutesSource implements SystemRoutesSource {
+public final class BluetoothManagerSystemRoutesSource extends SystemRoutesSource {
 
     @NonNull
+    private final Context mContext;
+    @NonNull
     private final BluetoothManager mBluetoothManager;
     @NonNull
     private final BluetoothAdapter mBluetoothAdapter;
+    @NonNull
+    private final DeviceStateChangedReceiver mDeviceStateChangedReceiver =
+            new DeviceStateChangedReceiver();
 
     /** Returns a new instance. */
     @NonNull
     public static BluetoothManagerSystemRoutesSource create(@NonNull Context context) {
         BluetoothManager bluetoothManager =
                 (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
-        return new BluetoothManagerSystemRoutesSource(bluetoothManager);
+        return new BluetoothManagerSystemRoutesSource(context, bluetoothManager);
     }
 
-    BluetoothManagerSystemRoutesSource(@NonNull BluetoothManager bluetoothManager) {
+    BluetoothManagerSystemRoutesSource(@NonNull Context context,
+            @NonNull BluetoothManager bluetoothManager) {
+        mContext = context;
         mBluetoothManager = bluetoothManager;
         mBluetoothAdapter = mBluetoothManager.getAdapter();
     }
 
+    @Override
+    public void start() {
+        IntentFilter deviceStateChangedIntentFilter = new IntentFilter();
+
+        deviceStateChangedIntentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
+        deviceStateChangedIntentFilter.addAction(
+                BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
+        deviceStateChangedIntentFilter.addAction(
+                BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED);
+
+        mContext.registerReceiver(mDeviceStateChangedReceiver, deviceStateChangedIntentFilter);
+    }
+
+    @Override
+    public void stop() {
+        mContext.unregisterReceiver(mDeviceStateChangedReceiver);
+    }
+
+    @NonNull
+    @Override
+    public SystemRoutesSourceItem getSourceItem() {
+        return new SystemRoutesSourceItem.Builder(
+                SystemRoutesSourceItem.ROUTE_SOURCE_BLUETOOTH_MANAGER)
+                .build();
+    }
+
     @NonNull
     @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
     @Override
-    public List<SystemRouteItem> fetchRoutes() {
+    public List<SystemRouteItem> fetchSourceRouteItems() {
         List<SystemRouteItem> out = new ArrayList<>();
 
         for (BluetoothDevice device : mBluetoothAdapter.getBondedDevices()) {
-            out.add(new SystemRouteItem.Builder(/* id= */ device.getAddress(),
-                    /* type= */ SystemRouteItem.ROUTE_SOURCE_BLUETOOTH_MANAGER)
-                    .setName(device.getName())
-                    .setAddress(device.getAddress())
-                    .build());
+            out.add(createRouteItemFor(device));
         }
 
         return out;
     }
+
+    @NonNull
+    @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
+    private static SystemRouteItem createRouteItemFor(@NonNull BluetoothDevice device) {
+        return new SystemRouteItem.Builder(/* id= */ device.getAddress())
+                .setName(device.getName())
+                .setAddress(device.getAddress())
+                .build();
+    }
+
+    private class DeviceStateChangedReceiver extends BroadcastReceiver {
+        @Override
+        @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
+        public void onReceive(Context context, Intent intent) {
+            BluetoothDevice device = IntentCompat.getParcelableExtra(intent,
+                    BluetoothDevice.EXTRA_DEVICE, android.bluetooth.BluetoothDevice.class);
+
+            switch (intent.getAction()) {
+                case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED:
+                case BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED:
+                case BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED:
+                    handleConnectionStateChanged(intent, device);
+                    break;
+            }
+        }
+
+        @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
+        private void handleConnectionStateChanged(Intent intent,
+                BluetoothDevice device) {
+            int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
+            if (state == BluetoothProfile.STATE_CONNECTED) {
+                mOnRoutesChangedListener.onRouteAdded(createRouteItemFor(device));
+            } else if (state == BluetoothProfile.STATE_DISCONNECTING
+                    || state == BluetoothProfile.STATE_DISCONNECTED) {
+                mOnRoutesChangedListener.onRouteRemoved(createRouteItemFor(device));
+            }
+        }
+    }
 }
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/MediaRouter2SystemRoutesSource.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/MediaRouter2SystemRoutesSource.java
index 14fbc1f..2fc08fa 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/MediaRouter2SystemRoutesSource.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/MediaRouter2SystemRoutesSource.java
@@ -19,37 +19,97 @@
 import android.content.Context;
 import android.media.MediaRoute2Info;
 import android.media.MediaRouter2;
+import android.media.RouteDiscoveryPreference;
 import android.os.Build;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
 import com.example.androidx.mediarouting.activities.systemrouting.SystemRouteItem;
+import com.example.androidx.mediarouting.activities.systemrouting.SystemRoutesSourceItem;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /** Implements {@link SystemRoutesSource} using {@link MediaRouter2}. */
 @RequiresApi(Build.VERSION_CODES.R)
-public final class MediaRouter2SystemRoutesSource implements SystemRoutesSource {
+public final class MediaRouter2SystemRoutesSource extends SystemRoutesSource {
 
     @NonNull
+    private Context mContext;
+    @NonNull
     private MediaRouter2 mMediaRouter2;
 
+    @NonNull
+    private final Map<String, MediaRoute2Info> mLastKnownRoutes = new HashMap<>();
+    @NonNull
+    private final MediaRouter2.RouteCallback mRouteCallback = new MediaRouter2.RouteCallback() {
+        @Override
+        public void onRoutesUpdated(@NonNull List<MediaRoute2Info> routes) {
+            super.onRoutesUpdated(routes);
+
+            Map<String, MediaRoute2Info> routesLookup = new HashMap<>();
+            for (MediaRoute2Info route: routes) {
+                if (!mLastKnownRoutes.containsKey(route.getId())) {
+                    mOnRoutesChangedListener.onRouteAdded(createRouteItemFor(route));
+                }
+                routesLookup.put(route.getId(), route);
+            }
+
+            for (MediaRoute2Info route: mLastKnownRoutes.values()) {
+                if (!routesLookup.containsKey(route.getId())) {
+                    mOnRoutesChangedListener.onRouteRemoved(createRouteItemFor(route));
+                }
+            }
+
+            mLastKnownRoutes.clear();
+            mLastKnownRoutes.putAll(routesLookup);
+        }
+    };
+
     /** Returns a new instance. */
     @NonNull
     public static MediaRouter2SystemRoutesSource create(@NonNull Context context) {
         MediaRouter2 mediaRouter2 = MediaRouter2.getInstance(context);
-        return new MediaRouter2SystemRoutesSource(mediaRouter2);
+        return new MediaRouter2SystemRoutesSource(context, mediaRouter2);
     }
 
-    MediaRouter2SystemRoutesSource(@NonNull MediaRouter2 mediaRouter2) {
+    MediaRouter2SystemRoutesSource(@NonNull Context context,
+            @NonNull MediaRouter2 mediaRouter2) {
+        mContext = context;
         mMediaRouter2 = mediaRouter2;
     }
 
+    @Override
+    public void start() {
+        RouteDiscoveryPreference routeDiscoveryPreference =
+                new RouteDiscoveryPreference.Builder(
+                        /* preferredFeatures= */ Collections.emptyList(),
+                        /* activeScan= */ false)
+                        .build();
+
+        mMediaRouter2.registerRouteCallback(mContext.getMainExecutor(),
+                mRouteCallback, routeDiscoveryPreference);
+    }
+
+    @Override
+    public void stop() {
+        mMediaRouter2.unregisterRouteCallback(mRouteCallback);
+    }
+
     @NonNull
     @Override
-    public List<SystemRouteItem> fetchRoutes() {
+    public SystemRoutesSourceItem getSourceItem() {
+        return new SystemRoutesSourceItem.Builder(SystemRoutesSourceItem.ROUTE_SOURCE_MEDIA_ROUTER2)
+                .build();
+    }
+
+    @NonNull
+    @Override
+    public List<SystemRouteItem> fetchSourceRouteItems() {
         List<SystemRouteItem> out = new ArrayList<>();
 
         for (MediaRoute2Info routeInfo : mMediaRouter2.getRoutes()) {
@@ -57,13 +117,21 @@
                 continue;
             }
 
-            out.add(new SystemRouteItem.Builder(routeInfo.getId(),
-                    SystemRouteItem.ROUTE_SOURCE_MEDIA_ROUTER2)
-                    .setName(String.valueOf(routeInfo.getName()))
-                    .setDescription(String.valueOf(routeInfo.getDescription()))
-                    .build());
+            if (!mLastKnownRoutes.containsKey(routeInfo.getId())) {
+                mLastKnownRoutes.put(routeInfo.getId(), routeInfo);
+            }
+
+            out.add(createRouteItemFor(routeInfo));
         }
 
         return out;
     }
+
+    @NonNull
+    private static SystemRouteItem createRouteItemFor(@NonNull MediaRoute2Info routeInfo) {
+        return new SystemRouteItem.Builder(routeInfo.getId())
+                .setName(String.valueOf(routeInfo.getName()))
+                .setDescription(String.valueOf(routeInfo.getDescription()))
+                .build();
+    }
 }
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/MediaRouterSystemRoutesSource.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/MediaRouterSystemRoutesSource.java
index 5307c66..9457823 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/MediaRouterSystemRoutesSource.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/MediaRouterSystemRoutesSource.java
@@ -27,17 +27,33 @@
 
 import com.example.androidx.mediarouting.activities.systemrouting.SystemRouteItem;
 import com.example.androidx.mediarouting.activities.systemrouting.SystemRouteUtils;
+import com.example.androidx.mediarouting.activities.systemrouting.SystemRoutesSourceItem;
 
 import java.util.ArrayList;
 import java.util.List;
 
 /** Implements {@link SystemRoutesSource} using {@link MediaRouter}. */
 @RequiresApi(Build.VERSION_CODES.JELLY_BEAN)
-public final class MediaRouterSystemRoutesSource implements SystemRoutesSource {
+public final class MediaRouterSystemRoutesSource extends SystemRoutesSource {
 
     @NonNull
     private final MediaRouter mMediaRouter;
 
+    @NonNull
+    private final MediaRouter.Callback mCallback = new MediaRouter.SimpleCallback() {
+        @Override
+        public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) {
+            super.onRouteAdded(router, info);
+            mOnRoutesChangedListener.onRouteAdded(createRouteItemFor(info));
+        }
+
+        @Override
+        public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) {
+            super.onRouteRemoved(router, info);
+            mOnRoutesChangedListener.onRouteRemoved(createRouteItemFor(info));
+        }
+    };
+
     /** Returns a new instance. */
     @NonNull
     public static MediaRouterSystemRoutesSource create(@NonNull Context context) {
@@ -50,9 +66,26 @@
         mMediaRouter = mediaRouter;
     }
 
+    @Override
+    public void start() {
+        mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_LIVE_AUDIO, mCallback);
+    }
+
+    @Override
+    public void stop() {
+        mMediaRouter.removeCallback(mCallback);
+    }
+
     @NonNull
     @Override
-    public List<SystemRouteItem> fetchRoutes() {
+    public SystemRoutesSourceItem getSourceItem() {
+        return new SystemRoutesSourceItem.Builder(SystemRoutesSourceItem.ROUTE_SOURCE_MEDIA_ROUTER)
+                .build();
+    }
+
+    @NonNull
+    @Override
+    public List<SystemRouteItem> fetchSourceRouteItems() {
         int count = mMediaRouter.getRouteCount();
 
         List<SystemRouteItem> out = new ArrayList<>();
@@ -64,25 +97,29 @@
                 continue;
             }
 
-            SystemRouteItem.Builder builder =
-                    new SystemRouteItem.Builder(info.getName().toString() /* id */,
-                            SystemRouteItem.ROUTE_SOURCE_MEDIA_ROUTER)
-                            .setName(info.getName().toString());
-
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
-                CharSequence description = Api18Impl.getDescription(info);
-
-                if (description != null) {
-                    builder.setDescription(String.valueOf(description));
-                }
-            }
-
-            out.add(builder.build());
+            out.add(createRouteItemFor(info));
         }
 
         return out;
     }
 
+    @NonNull
+    private static SystemRouteItem createRouteItemFor(@NonNull MediaRouter.RouteInfo routeInfo) {
+        SystemRouteItem.Builder builder =
+                new SystemRouteItem.Builder(/* id= */ routeInfo.getName().toString())
+                        .setName(routeInfo.getName().toString());
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+            CharSequence description = Api18Impl.getDescription(routeInfo);
+
+            if (description != null) {
+                builder.setDescription(String.valueOf(description));
+            }
+        }
+
+        return builder.build();
+    }
+
     @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
     static class Api18Impl {
         private Api18Impl() {
@@ -94,6 +131,5 @@
         static CharSequence getDescription(MediaRouter.RouteInfo routeInfo) {
             return routeInfo.getDescription();
         }
-
     }
 }
diff --git a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/SystemRoutesSource.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/SystemRoutesSource.java
index f58f724..f5a32db 100644
--- a/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/SystemRoutesSource.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/activities/systemrouting/source/SystemRoutesSource.java
@@ -17,20 +17,98 @@
 package com.example.androidx.mediarouting.activities.systemrouting.source;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.example.androidx.mediarouting.activities.systemrouting.SystemRouteItem;
+import com.example.androidx.mediarouting.activities.systemrouting.SystemRoutesSourceItem;
 
 import java.util.List;
 
 /**
  * Abstracts different route sources.
  */
-public interface SystemRoutesSource {
+public abstract class SystemRoutesSource {
+
+    private static final NoOpOnRoutesChangedListener NO_OP_ON_ROUTES_CHANGED_LISTENER =
+            new NoOpOnRoutesChangedListener();
+
+    @NonNull
+    protected OnRoutesChangedListener mOnRoutesChangedListener = NO_OP_ON_ROUTES_CHANGED_LISTENER;
 
     /**
-     * Fetches system routes and returns a list of {@link SystemRouteItem}.
+     * Sets {@link OnRoutesChangedListener} and subscribes to the source updates.
+     * To unsubscribe from the routes update pass {@code null} instead of the listener.
+     */
+    public void setOnRoutesChangedListener(
+            @Nullable OnRoutesChangedListener onRoutesChangedListener) {
+        if (onRoutesChangedListener != null) {
+            mOnRoutesChangedListener = onRoutesChangedListener;
+        } else {
+            mOnRoutesChangedListener = NO_OP_ON_ROUTES_CHANGED_LISTENER;
+        }
+    }
+
+    /**
+     * Starts the source. The source may use this opportunity to subscribe to changes that
+     * happen in the abstractions at a lower level.
+     */
+    public void start() {
+        // Empty on purpose.
+    }
+
+    /**
+     * Stops the source. The source releases resources if applicable.
+     */
+    public void stop() {
+        // Empty on purpose.
+    }
+
+    /**
+     * Gets a source item containing source type.
      */
     @NonNull
-    List<SystemRouteItem> fetchRoutes();
+    public abstract SystemRoutesSourceItem getSourceItem();
 
+    /**
+     * Fetches a list of {@link SystemRouteItem} discovered by this source.
+     */
+    @NonNull
+    public abstract List<SystemRouteItem> fetchSourceRouteItems();
+
+    /**
+     * An interface for listening to routes changes: whether the route has been added or removed
+     * from the source.
+     */
+    public interface OnRoutesChangedListener {
+
+        /**
+         * Called when a route has been added to the source's routes list.
+         *
+         * @param routeItem a newly added route.
+         */
+        void onRouteAdded(@NonNull SystemRouteItem routeItem);
+
+        /**
+         * Called when a route has been removed from the source's routes list.
+         *
+         * @param routeItem a recently removed route.
+         */
+        void onRouteRemoved(@NonNull SystemRouteItem routeItem);
+    }
+
+    /**
+     * Default no-op implementation of {@link OnRoutesChangedListener}.
+     * Used as a fallback implement when there is no listener.
+     */
+    private static final class NoOpOnRoutesChangedListener implements OnRoutesChangedListener {
+        @Override
+        public void onRouteAdded(@NonNull SystemRouteItem routeItem) {
+            // Empty on purpose.
+        }
+
+        @Override
+        public void onRouteRemoved(@NonNull SystemRouteItem routeItem) {
+            // Empty on purpose.
+        }
+    }
 }
diff --git a/samples/MediaRoutingDemo/src/main/res/layout/item_system_route.xml b/samples/MediaRoutingDemo/src/main/res/layout/item_system_route.xml
index 3034f1d..5d54748 100644
--- a/samples/MediaRoutingDemo/src/main/res/layout/item_system_route.xml
+++ b/samples/MediaRoutingDemo/src/main/res/layout/item_system_route.xml
@@ -23,31 +23,13 @@
     <androidx.cardview.widget.CardView
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginBottom="8dp"
-        android:layout_marginTop="8dp"
         app:cardCornerRadius="0dp">
 
         <RelativeLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:orientation="vertical"
-            android:padding="8dp">
-
-            <androidx.appcompat.widget.AppCompatTextView
-                android:id="@+id/route_source"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_alignParentTop="true"
-                android:layout_alignParentRight="true"
-                android:layout_alignParentEnd="true"
-                android:textSize="12sp"
-                android:paddingTop="2dp"
-                android:paddingBottom="2dp"
-                android:paddingLeft="6dp"
-                android:paddingRight="6dp"
-                android:background="@drawable/rounded_rectangle_gray"
-                android:textColor="@android:color/white"
-                tools:text="Media Router 1"/>
+            android:padding="16dp">
 
             <androidx.appcompat.widget.AppCompatTextView
                 android:id="@+id/route_name"
@@ -69,7 +51,7 @@
                 android:layout_alignParentStart="true"
                 android:singleLine="true"
                 android:ellipsize="marquee"
-                android:textSize="12sp"
+                android:textSize="14sp"
                 android:textStyle="italic"
                 tools:text="id12345678" />
 
@@ -92,7 +74,7 @@
                 android:layout_below="@+id/route_address"
                 android:layout_alignParentLeft="true"
                 android:layout_alignParentStart="true"
-                android:textSize="12sp"
+                android:textSize="14sp"
                 tools:text="This is a description of an amazing system route." />
 
         </RelativeLayout>
diff --git a/samples/MediaRoutingDemo/src/main/res/layout/item_system_route_header.xml b/samples/MediaRoutingDemo/src/main/res/layout/item_system_route_header.xml
new file mode 100644
index 0000000..e989063
--- /dev/null
+++ b/samples/MediaRoutingDemo/src/main/res/layout/item_system_route_header.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <androidx.cardview.widget.CardView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:background="@android:color/background_dark"
+        app:cardCornerRadius="0dp">
+
+        <androidx.appcompat.widget.AppCompatTextView
+            android:id="@+id/header_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentStart="true"
+            android:padding="16dp"
+            android:ellipsize="marquee"
+            android:singleLine="true"
+            android:textSize="18sp"
+            android:textStyle="italic"
+            tools:text="id12345678" />
+
+    </androidx.cardview.widget.CardView>
+
+</FrameLayout>
+
diff --git a/settings.gradle b/settings.gradle
index 68ac5e9..ae52660 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -603,6 +603,7 @@
 includeProject(":compose:material:material-icons-extended-sharp", [BuildType.COMPOSE])
 includeProject(":compose:material:material-icons-extended-twotone", [BuildType.COMPOSE])
 includeProject(":compose:material:material-ripple", [BuildType.COMPOSE])
+includeProject(":compose:material:material-ripple:material-ripple-benchmark", "compose/material/material-ripple/benchmark", [BuildType.COMPOSE])
 includeProject(":compose:material:material:icons:generator", [BuildType.COMPOSE])
 includeProject(":compose:material:material:integration-tests:material-demos", [BuildType.COMPOSE])
 includeProject(":compose:material:material:integration-tests:material-catalog", [BuildType.COMPOSE])
@@ -666,6 +667,7 @@
 includeProject(":constraintlayout:constraintlayout-compose:integration-tests:demos", [BuildType.COMPOSE])
 includeProject(":constraintlayout:constraintlayout-compose:integration-tests:macrobenchmark", [BuildType.COMPOSE])
 includeProject(":constraintlayout:constraintlayout-compose:integration-tests:macrobenchmark-target", [BuildType.COMPOSE])
+includeProject(":constraintlayout:constraintlayout-compose:integration-tests:compose-benchmark", [BuildType.COMPOSE])
 includeProject(":constraintlayout:constraintlayout", [BuildType.MAIN])
 includeProject(":constraintlayout:constraintlayout-core", [BuildType.MAIN, BuildType.COMPOSE])
 includeProject(":contentpager:contentpager", [BuildType.MAIN])
@@ -971,6 +973,7 @@
 includeProject(":slice:slice-test", [BuildType.MAIN])
 includeProject(":slice:slice-view", [BuildType.MAIN])
 includeProject(":slidingpanelayout:slidingpanelayout", [BuildType.MAIN, BuildType.FLAN])
+includeProject(":slidingpanelayout:slidingpanelayout-testapp", [BuildType.MAIN])
 includeProject(":sqlite:integration-tests:inspection-room-testapp", [BuildType.MAIN])
 includeProject(":sqlite:integration-tests:inspection-sqldelight-testapp", [BuildType.MAIN])
 includeProject(":sqlite:sqlite", [BuildType.MAIN, BuildType.COMPOSE])
@@ -1088,6 +1091,8 @@
 includeProject(":window:extensions:extensions", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.WINDOW])
 includeProject(":window:extensions:core:core", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.WINDOW])
 includeProject(":window:integration-tests:configuration-change-tests", [BuildType.MAIN, BuildType.WINDOW])
+includeProject(":window:integration-tests:macrobenchmark-target", [BuildType.MAIN, BuildType.WINDOW])
+includeProject(":window:integration-tests:macrobenchmark", [BuildType.MAIN, BuildType.WINDOW])
 includeProject(":window:sidecar:sidecar", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.WINDOW])
 includeProject(":window:window-java", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.WINDOW])
 includeProject(":window:window-core", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.WINDOW])
diff --git a/slidingpanelayout/slidingpanelayout-testapp/build.gradle b/slidingpanelayout/slidingpanelayout-testapp/build.gradle
new file mode 100644
index 0000000..7d5ebec
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/build.gradle
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import androidx.build.LibraryType
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.application")
+    id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+    api(libs.kotlinStdlib)
+    // Add dependencies here
+    implementation(project(":slidingpanelayout:slidingpanelayout"))
+    implementation("androidx.recyclerview:recyclerview:1.2.1")
+}
+
+android {
+    namespace "androidx.slidingpanelayout.demo"
+}
+
+androidx {
+    name = "androidx.slidingpanelayout:slidingpanelayout-demo"
+    type = LibraryType.SAMPLES
+    inceptionYear = "2023"
+    description = "SlidingPaneLayout Demos"
+}
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/AndroidManifest.xml b/slidingpanelayout/slidingpanelayout-testapp/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..e026b28
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+    <application
+        android:allowBackup="true"
+        android:label="SlidingPaneLayout Demo"
+        android:supportsRtl="true">
+
+        <activity android:name="androidx.slidingpanelayout.SlidingPaneLayoutDemos"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name="androidx.slidingpanelayout.SlidingPaneLayoutSample"
+            android:exported="true" />
+    </application>
+</manifest>
\ No newline at end of file
diff --git a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence1.java b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/DemoItem.kt
similarity index 71%
copy from lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence1.java
copy to slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/DemoItem.kt
index ae9c85f..5415371 100644
--- a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence1.java
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/DemoItem.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,11 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package androidx.slidingpanelayout
 
-package androidx.lifecycle.observers;
+import android.app.Activity
 
-public class DerivedSequence1 extends Base {
-
-    public void something() {
-    }
-}
+class DemoItem<T : Activity>(
+    val clazz: Class<T>,
+    val title: String,
+    val description: String
+)
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/ItemAdapter.kt b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/ItemAdapter.kt
new file mode 100644
index 0000000..93fac85
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/ItemAdapter.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.slidingpanelayout
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import androidx.slidingpanelayout.demo.R
+
+class ItemAdapter(private val items: List<DemoItem<*>>) : RecyclerView.Adapter<ItemViewHolder>() {
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
+        val inflater = LayoutInflater.from(parent.context)
+        val rootView = inflater.inflate(R.layout.item_viewholder, parent, false)
+        return ItemViewHolder(rootView)
+    }
+
+    override fun getItemCount(): Int {
+        return items.size
+    }
+
+    override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
+        holder.bind(items[position])
+    }
+}
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/ItemViewHolder.kt b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/ItemViewHolder.kt
new file mode 100644
index 0000000..2c48ee1
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/ItemViewHolder.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.slidingpanelayout
+
+import android.content.Intent
+import android.view.View
+import android.widget.Button
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import androidx.slidingpanelayout.demo.R
+
+class ItemViewHolder(rootView: View) : RecyclerView.ViewHolder(rootView) {
+
+    private val titleTextView = rootView.findViewById<TextView>(R.id.title_textview)
+    private val descriptionTextView = rootView.findViewById<TextView>(R.id.description_textview)
+    private val launchButton = rootView.findViewById<Button>(R.id.button_launch)
+
+    fun bind(item: DemoItem<*>) {
+        titleTextView.text = item.title
+        descriptionTextView.text = item.description
+        launchButton.setOnClickListener { view ->
+            val context = view.context
+            val intent = Intent(context, item.clazz)
+            context.startActivity(intent)
+        }
+    }
+}
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/SlidingPaneLayoutDemos.kt b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/SlidingPaneLayoutDemos.kt
new file mode 100644
index 0000000..b6d4a1e1
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/SlidingPaneLayoutDemos.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.slidingpanelayout
+
+import android.app.Activity
+import android.os.Bundle
+import androidx.recyclerview.widget.RecyclerView
+import androidx.slidingpanelayout.demo.R
+
+class SlidingPaneLayoutDemos : Activity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_slidingpanelayout_demos)
+        val recyclerView = findViewById<RecyclerView>(R.id.demo_recyclerview)
+
+        recyclerView.adapter = ItemAdapter(
+            listOf(
+                DemoItem(
+                    SlidingPaneLayoutSample::class.java,
+                    "SlidingPaneLayoutSample",
+                    "Basic SlidingPaneLayoutSample"
+                )
+            )
+        )
+    }
+}
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/SlidingPaneLayoutSample.kt b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/SlidingPaneLayoutSample.kt
new file mode 100644
index 0000000..59463f5
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/java/androidx/slidingpanelayout/SlidingPaneLayoutSample.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.slidingpanelayout
+
+import android.app.Activity
+import android.os.Bundle
+
+class SlidingPaneLayoutSample : Activity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(androidx.slidingpanelayout.demo.R.layout.activity_slidingpanelayout_sample)
+    }
+}
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/res/layout/activity_slidingpanelayout_demos.xml b/slidingpanelayout/slidingpanelayout-testapp/src/main/res/layout/activity_slidingpanelayout_demos.xml
new file mode 100644
index 0000000..58bbd7b
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/res/layout/activity_slidingpanelayout_demos.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:orientation="vertical"
+    android:id="@+id/demo_recyclerview"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
+
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/res/layout/activity_slidingpanelayout_sample.xml b/slidingpanelayout/slidingpanelayout-testapp/src/main/res/layout/activity_slidingpanelayout_sample.xml
new file mode 100644
index 0000000..436cc28
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/res/layout/activity_slidingpanelayout_sample.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<androidx.slidingpanelayout.widget.SlidingPaneLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <View
+        android:background="@android:color/holo_blue_light"
+        android:layout_width="200dp"
+        android:layout_height="match_parent"/>
+
+
+    <View
+        android:background="@android:color/holo_green_dark"
+        android:layout_width="200dp"
+        android:layout_weight="1"
+        android:layout_height="match_parent"/>
+
+</androidx.slidingpanelayout.widget.SlidingPaneLayout>
\ No newline at end of file
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/res/layout/item_viewholder.xml b/slidingpanelayout/slidingpanelayout-testapp/src/main/res/layout/item_viewholder.xml
new file mode 100644
index 0000000..631c063
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/res/layout/item_viewholder.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <TextView
+        android:id="@+id/title_textview"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"/>
+
+    <TextView
+        android:id="@+id/description_textview"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+    <Button
+        android:id="@+id/button_launch"
+        android:text="@string/launch"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/slidingpanelayout/slidingpanelayout-testapp/src/main/res/values/strings.xml b/slidingpanelayout/slidingpanelayout-testapp/src/main/res/values/strings.xml
new file mode 100644
index 0000000..cbe5256
--- /dev/null
+++ b/slidingpanelayout/slidingpanelayout-testapp/src/main/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<resources>
+    <string name="launch">Launch</string>
+</resources>
\ No newline at end of file
diff --git a/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/StableAidlTasks.kt b/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/StableAidlTasks.kt
index a8c07759..93bcbf8 100644
--- a/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/StableAidlTasks.kt
+++ b/stableaidl/stableaidl-gradle-plugin/src/main/java/androidx/stableaidl/StableAidlTasks.kt
@@ -24,7 +24,6 @@
 import com.android.build.api.variant.SourceDirectories
 import com.android.build.api.variant.Variant
 import com.android.utils.usLocaleCapitalize
-import java.io.File
 import org.gradle.api.Action
 import org.gradle.api.DomainObjectCollection
 import org.gradle.api.Project
@@ -36,6 +35,7 @@
 import org.gradle.api.file.FileCollection
 import org.gradle.api.file.RegularFile
 import org.gradle.api.provider.Provider
+import org.gradle.api.tasks.TaskOutputFilePropertyBuilder
 import org.gradle.api.tasks.TaskProvider
 
 // Gradle task group used to identify Stable AIDL tasks.
@@ -68,7 +68,6 @@
     }
 }
 
-@Suppress("UnstableApiUsage") // SourceDirectories.Flat
 fun registerCompileAidlApi(
     project: Project,
     variant: Variant,
@@ -277,18 +276,16 @@
 /**
  * Tells Gradle to skip running this task, even if this task declares no output files.
  */
-private fun Task.cacheEvenIfNoOutputs() {
-    this.outputs.file(this.getPlaceholderOutput())
-}
+private fun Task.cacheEvenIfNoOutputs(): TaskOutputFilePropertyBuilder =
+    outputs.file(getPlaceholderOutput())
 
 /**
  * Returns an unused output path that we can pass to Gradle to prevent Gradle from thinking that we
  * forgot to declare outputs of this task, and instead to skip this task if its inputs are
  * unchanged.
  */
-private fun Task.getPlaceholderOutput(): File {
-    return File(this.project.buildDir, "placeholderOutput/" + this.name.replace(":", "-"))
-}
+private fun Task.getPlaceholderOutput(): Provider<RegularFile> =
+    project.layout.buildDirectory.file("placeholderOutput/${name.replace(':', '-')}")
 
 private fun computeTaskName(prefix: String, variant: Variant, suffix: String) =
     "$prefix${variant.name.usLocaleCapitalize()}$suffix"
diff --git a/stableaidl/stableaidl-gradle-plugin/src/test/java/com/android/build/gradle/internal/fixtures/FakeGradleProperty.kt b/stableaidl/stableaidl-gradle-plugin/src/test/java/com/android/build/gradle/internal/fixtures/FakeGradleProperty.kt
index bd198da..1d18803 100644
--- a/stableaidl/stableaidl-gradle-plugin/src/test/java/com/android/build/gradle/internal/fixtures/FakeGradleProperty.kt
+++ b/stableaidl/stableaidl-gradle-plugin/src/test/java/com/android/build/gradle/internal/fixtures/FakeGradleProperty.kt
@@ -34,6 +34,7 @@
 
     override fun isPresent() = value != null || valueProvider != null
 
+    @Suppress("WRONG_NULLABILITY_FOR_JAVA_OVERRIDE") // KT-36770
     override fun getOrElse(defaultValue: T) =
         value ?: valueProvider?.get() ?: convention ?: defaultValue
 
diff --git a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/MultiDisplayTest.java b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/MultiDisplayTest.java
index 8d9ef75..6b229e7 100644
--- a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/MultiDisplayTest.java
+++ b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/MultiDisplayTest.java
@@ -27,12 +27,14 @@
 import android.content.Intent;
 import android.hardware.display.DisplayManager;
 import android.view.Display;
+import android.view.Surface;
 
 import androidx.annotation.NonNull;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.SdkSuppress;
 import androidx.test.uiautomator.By;
 import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.Orientation;
 import androidx.test.uiautomator.UiObject2;
 import androidx.test.uiautomator.Until;
 
@@ -129,6 +131,24 @@
         }
     }
 
+    @Test
+    public void testMultiDisplay_orientations() {
+        int secondaryDisplayId = getSecondaryDisplayId();
+
+        try {
+            mDevice.setOrientation(Orientation.ROTATION_180, secondaryDisplayId);
+            assertEquals(Surface.ROTATION_180, mDevice.getDisplayRotation(secondaryDisplayId));
+
+            mDevice.setOrientation(Orientation.FROZEN, secondaryDisplayId);
+            assertEquals(Surface.ROTATION_180, mDevice.getDisplayRotation(secondaryDisplayId));
+
+            mDevice.setOrientation(Orientation.ROTATION_270, secondaryDisplayId);
+            assertEquals(Surface.ROTATION_270, mDevice.getDisplayRotation(secondaryDisplayId));
+        } finally {
+            mDevice.setOrientation(Orientation.ROTATION_0, secondaryDisplayId);
+        }
+    }
+
     // Helper to launch an activity on a specific display.
     private void launchTestActivityOnDisplay(@NonNull Class<? extends Activity> activity,
             int displayId) {
diff --git a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiDeviceTest.java b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiDeviceTest.java
index f14e6e6..8f94cc4 100644
--- a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiDeviceTest.java
+++ b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiDeviceTest.java
@@ -25,6 +25,7 @@
 import android.app.UiAutomation;
 import android.graphics.Point;
 import android.view.KeyEvent;
+import android.view.Surface;
 import android.widget.TextView;
 
 import androidx.test.filters.LargeTest;
@@ -32,6 +33,7 @@
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.uiautomator.By;
 import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.Orientation;
 import androidx.test.uiautomator.UiDevice;
 import androidx.test.uiautomator.UiObject2;
 import androidx.test.uiautomator.Until;
@@ -390,24 +392,23 @@
     }
 
     @Test
-    public void testSetOrientationPortrait() throws Exception {
+    public void testSetOrientation() {
         launchTestActivity(KeycodeTestActivity.class);
-        try {
-            mDevice.setOrientationPortrait();
-            assertTrue(mDevice.getDisplayHeight() > mDevice.getDisplayWidth());
-        } finally {
-            mDevice.unfreezeRotation();
-        }
-    }
 
-    @Test
-    public void testSetOrientationLandscape() throws Exception {
-        launchTestActivity(KeycodeTestActivity.class);
         try {
-            mDevice.setOrientationLandscape();
-            assertTrue(mDevice.getDisplayWidth() > mDevice.getDisplayHeight());
+            mDevice.setOrientation(Orientation.ROTATION_0);
+            assertEquals(Surface.ROTATION_0, mDevice.getDisplayRotation());
+
+            mDevice.setOrientation(Orientation.ROTATION_90);
+            assertEquals(Surface.ROTATION_90, mDevice.getDisplayRotation());
+
+            mDevice.setOrientation(Orientation.PORTRAIT);
+            assertTrue(mDevice.getDisplayHeight() >= mDevice.getDisplayWidth());
+
+            mDevice.setOrientation(Orientation.LANDSCAPE);
+            assertTrue(mDevice.getDisplayHeight() <= mDevice.getDisplayWidth());
         } finally {
-            mDevice.unfreezeRotation();
+            mDevice.setOrientation(Orientation.ROTATION_0);
         }
     }
 
diff --git a/test/uiautomator/uiautomator/api/current.txt b/test/uiautomator/uiautomator/api/current.txt
index f926023..f50179d 100644
--- a/test/uiautomator/uiautomator/api/current.txt
+++ b/test/uiautomator/uiautomator/api/current.txt
@@ -132,6 +132,17 @@
     method public void sendStatus(int, android.os.Bundle);
   }
 
+  public enum Orientation {
+    enum_constant public static final androidx.test.uiautomator.Orientation FROZEN;
+    enum_constant public static final androidx.test.uiautomator.Orientation LANDSCAPE;
+    enum_constant public static final androidx.test.uiautomator.Orientation PORTRAIT;
+    enum_constant public static final androidx.test.uiautomator.Orientation ROTATION_0;
+    enum_constant public static final androidx.test.uiautomator.Orientation ROTATION_180;
+    enum_constant public static final androidx.test.uiautomator.Orientation ROTATION_270;
+    enum_constant public static final androidx.test.uiautomator.Orientation ROTATION_90;
+    enum_constant public static final androidx.test.uiautomator.Orientation UNFROZEN;
+  }
+
   public abstract class SearchCondition<U> implements androidx.test.uiautomator.Condition<androidx.test.uiautomator.Searchable,U> {
     ctor public SearchCondition();
   }
@@ -169,6 +180,7 @@
     method public void dumpWindowHierarchy(java.io.File) throws java.io.IOException;
     method public void dumpWindowHierarchy(java.io.OutputStream) throws java.io.IOException;
     method @Deprecated public void dumpWindowHierarchy(String);
+    method @Discouraged(message="Can be useful for simple commands, but lacks support for proper error handling, input data, or complex commands (quotes, pipes) that can be obtained from UiAutomation#executeShellCommandRwe or similar utilities.") @RequiresApi(21) public String executeShellCommand(String) throws java.io.IOException;
     method public androidx.test.uiautomator.UiObject2! findObject(androidx.test.uiautomator.BySelector);
     method public androidx.test.uiautomator.UiObject findObject(androidx.test.uiautomator.UiSelector);
     method public java.util.List<androidx.test.uiautomator.UiObject2!> findObjects(androidx.test.uiautomator.BySelector);
@@ -178,6 +190,7 @@
     method @Px public int getDisplayHeight();
     method @Px public int getDisplayHeight(int);
     method public int getDisplayRotation();
+    method public int getDisplayRotation(int);
     method public android.graphics.Point getDisplaySizeDp();
     method @Px public int getDisplayWidth();
     method @Px public int getDisplayWidth(int);
@@ -216,10 +229,10 @@
     method public void runWatchers();
     method @Deprecated public void setCompressedLayoutHeirarchy(boolean);
     method public void setCompressedLayoutHierarchy(boolean);
-    method public void setOrientationLandscape() throws android.os.RemoteException;
+    method public void setOrientation(androidx.test.uiautomator.Orientation);
+    method @RequiresApi(30) public void setOrientation(androidx.test.uiautomator.Orientation, int);
     method public void setOrientationLeft() throws android.os.RemoteException;
     method public void setOrientationNatural() throws android.os.RemoteException;
-    method public void setOrientationPortrait() throws android.os.RemoteException;
     method public void setOrientationRight() throws android.os.RemoteException;
     method public void sleep() throws android.os.RemoteException;
     method public boolean swipe(android.graphics.Point![], int);
diff --git a/test/uiautomator/uiautomator/api/restricted_current.txt b/test/uiautomator/uiautomator/api/restricted_current.txt
index f926023..f50179d 100644
--- a/test/uiautomator/uiautomator/api/restricted_current.txt
+++ b/test/uiautomator/uiautomator/api/restricted_current.txt
@@ -132,6 +132,17 @@
     method public void sendStatus(int, android.os.Bundle);
   }
 
+  public enum Orientation {
+    enum_constant public static final androidx.test.uiautomator.Orientation FROZEN;
+    enum_constant public static final androidx.test.uiautomator.Orientation LANDSCAPE;
+    enum_constant public static final androidx.test.uiautomator.Orientation PORTRAIT;
+    enum_constant public static final androidx.test.uiautomator.Orientation ROTATION_0;
+    enum_constant public static final androidx.test.uiautomator.Orientation ROTATION_180;
+    enum_constant public static final androidx.test.uiautomator.Orientation ROTATION_270;
+    enum_constant public static final androidx.test.uiautomator.Orientation ROTATION_90;
+    enum_constant public static final androidx.test.uiautomator.Orientation UNFROZEN;
+  }
+
   public abstract class SearchCondition<U> implements androidx.test.uiautomator.Condition<androidx.test.uiautomator.Searchable,U> {
     ctor public SearchCondition();
   }
@@ -169,6 +180,7 @@
     method public void dumpWindowHierarchy(java.io.File) throws java.io.IOException;
     method public void dumpWindowHierarchy(java.io.OutputStream) throws java.io.IOException;
     method @Deprecated public void dumpWindowHierarchy(String);
+    method @Discouraged(message="Can be useful for simple commands, but lacks support for proper error handling, input data, or complex commands (quotes, pipes) that can be obtained from UiAutomation#executeShellCommandRwe or similar utilities.") @RequiresApi(21) public String executeShellCommand(String) throws java.io.IOException;
     method public androidx.test.uiautomator.UiObject2! findObject(androidx.test.uiautomator.BySelector);
     method public androidx.test.uiautomator.UiObject findObject(androidx.test.uiautomator.UiSelector);
     method public java.util.List<androidx.test.uiautomator.UiObject2!> findObjects(androidx.test.uiautomator.BySelector);
@@ -178,6 +190,7 @@
     method @Px public int getDisplayHeight();
     method @Px public int getDisplayHeight(int);
     method public int getDisplayRotation();
+    method public int getDisplayRotation(int);
     method public android.graphics.Point getDisplaySizeDp();
     method @Px public int getDisplayWidth();
     method @Px public int getDisplayWidth(int);
@@ -216,10 +229,10 @@
     method public void runWatchers();
     method @Deprecated public void setCompressedLayoutHeirarchy(boolean);
     method public void setCompressedLayoutHierarchy(boolean);
-    method public void setOrientationLandscape() throws android.os.RemoteException;
+    method public void setOrientation(androidx.test.uiautomator.Orientation);
+    method @RequiresApi(30) public void setOrientation(androidx.test.uiautomator.Orientation, int);
     method public void setOrientationLeft() throws android.os.RemoteException;
     method public void setOrientationNatural() throws android.os.RemoteException;
-    method public void setOrientationPortrait() throws android.os.RemoteException;
     method public void setOrientationRight() throws android.os.RemoteException;
     method public void sleep() throws android.os.RemoteException;
     method public boolean swipe(android.graphics.Point![], int);
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Configurator.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Configurator.java
index 6b7931d..ad68c56 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Configurator.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Configurator.java
@@ -224,6 +224,11 @@
     /**
      * Sets the current tool type to use for motion events.
      * @see MotionEvent#getToolType(int)
+     * @see MotionEvent#TOOL_TYPE_FINGER
+     * @see MotionEvent#TOOL_TYPE_STYLUS
+     * @see MotionEvent#TOOL_TYPE_MOUSE
+     * @see MotionEvent#TOOL_TYPE_ERASER
+     * @see MotionEvent#TOOL_TYPE_UNKNOWN
      */
     public @NonNull Configurator setToolType(final int toolType) {
         mToolType = toolType;
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Orientation.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Orientation.java
new file mode 100644
index 0000000..d660ed9
--- /dev/null
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Orientation.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.test.uiautomator;
+
+/**
+ * Specifies the orientation state of a display. Used with
+ * {@link UiDevice#setOrientation(Orientation)} and
+ * {@link UiDevice#setOrientation(Orientation, int)}.
+ */
+public enum Orientation {
+    /** Sets the rotation to be the natural orientation. */
+    ROTATION_0,
+    /** Sets the rotation to be 90 degrees clockwise to the natural orientation. */
+    ROTATION_90,
+    /** Sets the rotation to be 180 degrees clockwise to the natural orientation. */
+    ROTATION_180,
+    /** Sets the rotation to be 270 degrees clockwise to the natural orientation. */
+    ROTATION_270,
+    /** Sets the rotation so that the display height will be >= the display width. */
+    PORTRAIT,
+    /** Sets the rotation so that the display height will be <= the display width. */
+    LANDSCAPE,
+    /** Freezes the current rotation. */
+    FROZEN,
+    /** Unfreezes the current rotation. Need to wait a short period for the rotation animation to
+     *  complete before performing another operation. */
+    UNFROZEN;
+}
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java
index ff5e765..28fd77a 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java
@@ -44,12 +44,12 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityWindowInfo;
 
+import androidx.annotation.Discouraged;
 import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.Px;
 import androidx.annotation.RequiresApi;
-import androidx.annotation.RestrictTo;
 import androidx.test.uiautomator.util.Traces;
 import androidx.test.uiautomator.util.Traces.Section;
 
@@ -797,124 +797,274 @@
     }
 
     /**
-     * @return true if device is in its natural orientation (0 or 180 degrees)
+     * @return true if default display is in its natural or flipped (180 degrees) orientation
      */
     public boolean isNaturalOrientation() {
-        int ret = getDisplayRotation();
-        return ret == UiAutomation.ROTATION_FREEZE_0 ||
-                ret == UiAutomation.ROTATION_FREEZE_180;
+        return isNaturalOrientation(Display.DEFAULT_DISPLAY);
     }
 
     /**
-     * @return the current rotation of the display, as defined in {@link Surface}
+     * @return true if display with {@code displayId} is in its natural or flipped (180 degrees)
+     * orientation
+     */
+    private boolean isNaturalOrientation(int displayId) {
+        int ret = getDisplayRotation(displayId);
+        return ret == UiAutomation.ROTATION_FREEZE_0
+                || ret == UiAutomation.ROTATION_FREEZE_180;
+    }
+
+    /**
+     * @return the current rotation of the default display
+     * @see Display#getRotation()
      */
     public int getDisplayRotation() {
-        waitForIdle();
-        return getDisplayById(Display.DEFAULT_DISPLAY).getRotation();
+        return getDisplayRotation(Display.DEFAULT_DISPLAY);
     }
 
     /**
-     * Freezes the device rotation at its current state.
+     * @return the current rotation of the display with {@code displayId}
+     * @see Display#getRotation()
+     */
+    public int getDisplayRotation(int displayId) {
+        waitForIdle();
+        return getDisplayById(displayId).getRotation();
+    }
+
+    /**
+     * Freezes the default display rotation at its current state.
      * @throws RemoteException never
      */
     public void freezeRotation() throws RemoteException {
-        Log.d(TAG, "Freezing rotation.");
-        getUiAutomation().setRotation(UiAutomation.ROTATION_FREEZE_CURRENT);
+        setOrientation(Orientation.FROZEN);
     }
 
     /**
-     * Un-freezes the device rotation allowing its contents to rotate with the device physical
-     * rotation. During testing, it is best to keep the device frozen in a specific orientation.
+     * Un-freezes the default display rotation allowing its contents to rotate with its physical
+     * rotation. During testing, it is best to keep the default display frozen in a specific
+     * orientation.
+     * <p>Note: Need to wait a short period for the rotation animation to complete before
+     * performing another operation.
      * @throws RemoteException never
      */
     public void unfreezeRotation() throws RemoteException {
-        Log.d(TAG, "Unfreezing rotation.");
-        getUiAutomation().setRotation(UiAutomation.ROTATION_UNFREEZE);
+        setOrientation(Orientation.UNFROZEN);
     }
 
     /**
-     * Orients the device to the left and freezes rotation. Use {@link #unfreezeRotation()} to
-     * un-freeze the rotation.
+     * Orients the default display to the left and freezes its rotation. Use
+     * {@link #unfreezeRotation()} to un-freeze the rotation.
      * <p>Note: This rotation is relative to the natural orientation which depends on the device
-     * type (e.g. phone vs. tablet). Consider using {@link #setOrientationPortrait()} and
-     * {@link #setOrientationLandscape()}.
+     * type (e.g. phone vs. tablet).
      * @throws RemoteException never
      */
     public void setOrientationLeft() throws RemoteException {
-        Log.d(TAG, "Setting orientation to left.");
-        rotate(UiAutomation.ROTATION_FREEZE_90);
+        setOrientation(Orientation.ROTATION_90);
     }
 
     /**
-     * Orients the device to the right and freezes rotation. Use {@link #unfreezeRotation()} to
-     * un-freeze the rotation.
+     * Orients the default display to the right and freezes its rotation. Use
+     * {@link #unfreezeRotation()} to un-freeze the rotation.
      * <p>Note: This rotation is relative to the natural orientation which depends on the device
-     * type (e.g. phone vs. tablet). Consider using {@link #setOrientationPortrait()} and
-     * {@link #setOrientationLandscape()}.
+     * type (e.g. phone vs. tablet).
      * @throws RemoteException never
      */
     public void setOrientationRight() throws RemoteException {
-        Log.d(TAG, "Setting orientation to right.");
-        rotate(UiAutomation.ROTATION_FREEZE_270);
+        setOrientation(Orientation.ROTATION_270);
     }
 
     /**
-     * Orients the device to its natural orientation (0 or 180 degrees) and freezes rotation. Use
-     * {@link #unfreezeRotation()} to un-freeze the rotation.
+     * Orients the default display to its natural or flipped (180 degrees) orientation and
+     * freezes its rotation. Use {@link #unfreezeRotation()} to un-freeze the rotation.
      * <p>Note: The natural orientation depends on the device type (e.g. phone vs. tablet).
-     * Consider using {@link #setOrientationPortrait()} and {@link #setOrientationLandscape()}.
      * @throws RemoteException never
      */
     public void setOrientationNatural() throws RemoteException {
-        Log.d(TAG, "Setting orientation to natural.");
-        rotate(UiAutomation.ROTATION_FREEZE_0);
+        setOrientation(Orientation.ROTATION_0);
     }
 
     /**
-     * Orients the device to its portrait orientation (height > width) and freezes rotation. Use
-     * {@link #unfreezeRotation()} to un-freeze the rotation.
-     * @throws RemoteException never
+     * Sets the default display to {@code orientation} and freezes its rotation.
+     * <p>Note: The orientation is relative to the natural orientation which depends on the
+     * device type (e.g. phone vs. tablet).
+     *
+     * @param orientation the desired orientation
      */
-    public void setOrientationPortrait() throws RemoteException {
-        Log.d(TAG, "Setting orientation to portrait.");
-        if (getDisplayHeight() > getDisplayWidth()) {
-            freezeRotation(); // Already in portrait orientation.
-        } else if (isNaturalOrientation()) {
-            rotate(UiAutomation.ROTATION_FREEZE_90);
-        } else {
-            rotate(UiAutomation.ROTATION_FREEZE_0);
+    public void setOrientation(@NonNull Orientation orientation) {
+        Log.d(TAG, String.format("Setting orientation to %s.",
+                orientation.name()));
+        switch (orientation) {
+            case ROTATION_90:
+                rotateWithUiAutomation(Surface.ROTATION_90);
+                break;
+            case ROTATION_270:
+                rotateWithUiAutomation(Surface.ROTATION_270);
+                break;
+            case ROTATION_0:
+                rotateWithUiAutomation(Surface.ROTATION_0);
+                break;
+            case ROTATION_180:
+                rotateWithUiAutomation(Surface.ROTATION_180);
+                break;
+            case PORTRAIT:
+                if (getDisplayHeight() >= getDisplayWidth()) {
+                    // Freeze. Already in portrait orientation.
+                    getUiAutomation().setRotation(UiAutomation.ROTATION_FREEZE_CURRENT);
+                } else if (isNaturalOrientation()) {
+                    rotateWithUiAutomation(Surface.ROTATION_90);
+                } else {
+                    rotateWithUiAutomation(Surface.ROTATION_0);
+                }
+                break;
+            case LANDSCAPE:
+                if (getDisplayHeight() <= getDisplayWidth()) {
+                    // Freeze. Already in landscape orientation.
+                    getUiAutomation().setRotation(UiAutomation.ROTATION_FREEZE_CURRENT);
+                } else if (isNaturalOrientation()) {
+                    rotateWithUiAutomation(Surface.ROTATION_90);
+                } else {
+                    rotateWithUiAutomation(Surface.ROTATION_0);
+                }
+                break;
+            case FROZEN:
+                getUiAutomation().setRotation(UiAutomation.ROTATION_FREEZE_CURRENT);
+                break;
+            case UNFROZEN:
+                getUiAutomation().setRotation(UiAutomation.ROTATION_UNFREEZE);
+                break;
+            default:
+                throw new IllegalArgumentException(
+                        "Invalid input orientation for UiDevice#setOrientation.");
         }
     }
 
     /**
-     * Orients the device to its landscape orientation (width > height) and freezes rotation. Use
-     * {@link #unfreezeRotation()} to un-freeze the rotation.
-     * @throws RemoteException never
+     * Sets the display with {@code displayId} to {@code orientation} and freezes its rotation.
+     * <p>Note: The orientation is relative to the natural orientation which depends on the
+     * device type (e.g. phone vs. tablet).
+     * <p>Note: Some secondary displays don't have rotation sensors and therefore won't respond
+     * to {@link Orientation#UNFROZEN}.
+     *
+     * @param orientation the desired orientation
+     * @param displayId The display ID to match. Use {@link Display#getDisplayId()} to get the ID.
      */
-    public void setOrientationLandscape() throws RemoteException {
-        Log.d(TAG, "Setting orientation to landscape.");
-        if (getDisplayWidth() > getDisplayHeight()) {
-            freezeRotation(); // Already in landscape orientation.
-        } else if (isNaturalOrientation()) {
-            rotate(UiAutomation.ROTATION_FREEZE_90);
-        } else {
-            rotate(UiAutomation.ROTATION_FREEZE_0);
+    @RequiresApi(30)
+    public void setOrientation(@NonNull Orientation orientation, int displayId) {
+        Log.d(TAG, String.format("Setting orientation of display %d to %s.", displayId,
+                orientation.name()));
+        switch (orientation) {
+            case ROTATION_90:
+                rotateWithCommand(Surface.ROTATION_90, displayId);
+                break;
+            case ROTATION_270:
+                rotateWithCommand(Surface.ROTATION_270, displayId);
+                break;
+            case ROTATION_0:
+                rotateWithCommand(Surface.ROTATION_0, displayId);
+                break;
+            case ROTATION_180:
+                rotateWithCommand(Surface.ROTATION_180, displayId);
+                break;
+            case PORTRAIT:
+                if (getDisplayHeight() >= getDisplayWidth()) {
+                    // Freeze. Already in portrait orientation.
+                    freezeWithCommand(displayId);
+                } else if (isNaturalOrientation(displayId)) {
+                    rotateWithCommand(Surface.ROTATION_90, displayId);
+                } else {
+                    rotateWithCommand(Surface.ROTATION_0, displayId);
+                }
+                break;
+            case LANDSCAPE:
+                if (getDisplayHeight() <= getDisplayWidth()) {
+                    // Freeze. Already in landscape orientation.
+                    freezeWithCommand(displayId);
+                } else if (isNaturalOrientation(displayId)) {
+                    rotateWithCommand(Surface.ROTATION_90, displayId);
+                } else {
+                    rotateWithCommand(Surface.ROTATION_0, displayId);
+                }
+                break;
+            case FROZEN:
+                freezeWithCommand(displayId);
+                break;
+            case UNFROZEN:
+                unfreezeWithCommand(displayId);
+                break;
+            default:
+                throw new IllegalArgumentException(
+                        "Invalid input orientation for UiDevice#setOrientation.");
         }
     }
 
-    // Rotates the device and waits for the rotation to be detected.
-    private void rotate(int rotation) {
+    /** Rotates the display using UiAutomation and waits for the rotation to be detected. */
+    private void rotateWithUiAutomation(int rotation) {
         getUiAutomation().setRotation(rotation);
+        waitRotationComplete(rotation, Display.DEFAULT_DISPLAY);
+    }
+
+    /** Rotates the display using shell command and waits for the rotation to be detected. */
+    @RequiresApi(30)
+    private void rotateWithCommand(int rotation, int displayId) {
+        try {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+                executeShellCommand(String.format("cmd window user-rotation -d %d lock %d",
+                        displayId, rotation));
+            } else {
+                executeShellCommand(String.format("cmd window set-user-rotation lock -d %d %d",
+                        displayId, rotation));
+            }
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        waitRotationComplete(rotation, displayId);
+    }
+
+    /** Freezes the display using shell command. */
+    @RequiresApi(30)
+    private void freezeWithCommand(int displayId) {
+        try {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+                executeShellCommand(String.format("cmd window user-rotation -d %d lock",
+                        displayId));
+            } else {
+                int rotation = getDisplayRotation(displayId);
+                executeShellCommand(String.format("cmd window set-user-rotation lock -d %d %d",
+                        displayId, rotation));
+            }
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /** Unfreezes the display using shell command. */
+    @RequiresApi(30)
+    private void unfreezeWithCommand(int displayId) {
+        try {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+                executeShellCommand(String.format("cmd window user-rotation -d %d free",
+                        displayId));
+            } else {
+                executeShellCommand(String.format("cmd window set-user-rotation free -d %d",
+                        displayId));
+            }
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /** Waits for the display with {@code displayId} to be in {@code rotation}. */
+    private void waitRotationComplete(int rotation, int displayId) {
         Condition<UiDevice, Boolean> rotationCondition = new Condition<UiDevice, Boolean>() {
             @Override
             public Boolean apply(UiDevice device) {
-                return device.getDisplayRotation() == rotation;
+                return device.getDisplayRotation(displayId) == rotation;
             }
 
             @NonNull
             @Override
             public String toString() {
-                return String.format("Condition[displayRotation=%d]", rotation);
+                return String.format("Condition[displayRotation=%d, displayId=%d]", rotation,
+                        displayId);
             }
         };
         if (!wait(rotationCondition, ROTATION_TIMEOUT)) {
@@ -1126,13 +1276,14 @@
      * <p>
      * Calling function with large amount of output will have memory impacts, and the function call
      * will block if the command executed is blocking.
-     * <p>Note: calling this function requires API level 21 or above
+     *
      * @param cmd the command to run
      * @return the standard output of the command
-     * @throws IOException
-     * Legacy hidden method, kept for compatibility with existing tests.
+     * @throws IOException if an I/O error occurs while reading output
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @Discouraged(message = "Can be useful for simple commands, but lacks support for proper error"
+            + " handling, input data, or complex commands (quotes, pipes) that can be obtained "
+            + "from UiAutomation#executeShellCommandRwe or similar utilities.")
     @RequiresApi(21)
     @NonNull
     public String executeShellCommand(@NonNull String cmd) throws IOException {
diff --git a/testutils/testutils-common/src/main/java/androidx/testutils/ParameterizedHelper.kt b/testutils/testutils-common/src/main/java/androidx/testutils/ParameterizedHelper.kt
index 236b362..844f968 100644
--- a/testutils/testutils-common/src/main/java/androidx/testutils/ParameterizedHelper.kt
+++ b/testutils/testutils-common/src/main/java/androidx/testutils/ParameterizedHelper.kt
@@ -33,6 +33,7 @@
  *
  * See [ParameterizedHelperTest] for more examples.
  */
+// TODO(kuanyingchou): Remove and replace with TestParameterInjector"
 fun generateAllEnumerations(vararg args: List<Any>): List<Array<Any>> =
     generateAllEnumerationsIteratively(args.toList()).map { it.toTypedArray() }
 
diff --git a/testutils/testutils-datastore/build.gradle b/testutils/testutils-datastore/build.gradle
index 3b3e7ef..3bb4fcf 100644
--- a/testutils/testutils-datastore/build.gradle
+++ b/testutils/testutils-datastore/build.gradle
@@ -45,6 +45,18 @@
                 api(libs.kotlinTest)
             }
         }
+        commonTest {
+            dependencies {
+                implementation(project(":kruth:kruth"))
+                implementation(libs.kotlinTest)
+            }
+        }
+        jvmTest {
+            dependsOn(commonTest)
+            dependencies {
+                implementation(libs.kotlinTest)
+            }
+        }
     }
 }
 
diff --git a/testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/OkioTestIO.kt b/testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/OkioTestIO.kt
index fdfd10a..0581147 100644
--- a/testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/OkioTestIO.kt
+++ b/testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/OkioTestIO.kt
@@ -19,73 +19,29 @@
 import androidx.datastore.core.InterProcessCoordinator
 import androidx.datastore.core.Storage
 import androidx.datastore.core.okio.OkioStorage
-import kotlin.random.Random
 import kotlin.reflect.KClass
 import okio.FileSystem
 import okio.IOException
 import okio.Path
-import okio.Path.Companion.toPath
 
-open class OkioTestIO(dirName: String = "test-dir") : TestIO<OkioPath, IOException>(dirName) {
+open class OkioTestIO(
     private val fileSystem: FileSystem = FileSystem.SYSTEM
+) : TestIO<OkioPath, IOException>(
+    getTmpDir = {
+        OkioPath(fileSystem = fileSystem, path = FileSystem.SYSTEM_TEMPORARY_DIRECTORY)
+    }
+) {
     override fun getStorage(
         serializerConfig: TestingSerializerConfig,
         coordinatorProducer: () -> InterProcessCoordinator,
-        futureFile: () -> TestFile
+        futureFile: () -> OkioPath
     ): Storage<Byte> {
         return OkioStorage(
             fileSystem = fileSystem,
             serializer = TestingOkioSerializer(serializerConfig),
             coordinatorProducer = { _, _ -> coordinatorProducer() }
         ) {
-            futureFile().getAbsolutePath().toPath()
-        }
-    }
-
-    override fun tempDir(
-        directoryPath: String?,
-        makeDirs: Boolean,
-        parentDir: OkioPath?
-    ): OkioPath {
-        return if (parentDir != null) {
-            if (directoryPath != null) {
-                val newPath = if (directoryPath.startsWith("/"))
-                    directoryPath.substring(1) else directoryPath
-                val dir = parentDir.path / newPath
-                if (makeDirs) {
-                    fileSystem.createDirectories(dir)
-                }
-                OkioPath(fileSystem, dir)
-            } else {
-                OkioPath(fileSystem, parentDir.path / randomFileName(dirName))
-            }
-        } else {
-            if (directoryPath != null) {
-                val newPath = if (directoryPath.startsWith("/"))
-                    directoryPath.substring(1) else directoryPath
-                val dir = FileSystem.SYSTEM_TEMPORARY_DIRECTORY / randomFileName(dirName) / newPath
-                if (makeDirs) {
-                    fileSystem.createDirectories(dir)
-                }
-                OkioPath(fileSystem, dir)
-            } else {
-                OkioPath(
-                    fileSystem, FileSystem.SYSTEM_TEMPORARY_DIRECTORY /
-                        randomFileName(dirName)
-                )
-            }
-        }
-    }
-
-    override fun newTempFile(tempFolder: OkioPath): OkioPath {
-        return OkioPath(fileSystem, tempFolder.path / randomFileName(dirName))
-    }
-
-    private fun randomFileName( // LAME :)
-        prefix: String = "test-file"
-    ): String {
-        return prefix + (0 until 15).joinToString(separator = "") {
-            ('a' + Random.nextInt(from = 0, until = 26)).toString()
+            futureFile().path
         }
     }
 
@@ -95,18 +51,9 @@
 
     override fun ioExceptionClass(): KClass<IOException> =
         IOException::class
-
-    override fun isDirectory(file: OkioPath): Boolean {
-        return fileSystem.metadata(file.path).isDirectory
-    }
 }
 
-class OkioPath(private val fileSystem: FileSystem, val path: Path) : TestFile() {
-
-    override fun getAbsolutePath(): String {
-        return path.toString()
-    }
-
+class OkioPath(private val fileSystem: FileSystem, val path: Path) : TestFile<OkioPath>() {
     override fun delete(): Boolean {
         if (!fileSystem.exists(path)) {
             // to be consistent with the TestFile API.
@@ -120,10 +67,49 @@
         return fileSystem.exists(path)
     }
 
-    override fun createIfNotExists(): Boolean {
-        if (exists()) return false
-        path.parent?.let { fileSystem.createDirectories(it) }
-        fileSystem.write(path, mustCreate = true) { "" }
-        return true
+    override fun mkdirs(mustCreate: Boolean) {
+        if (exists()) {
+            check(fileSystem.metadataOrNull(path)?.isDirectory == true) {
+                "$path already exists but it is not a directory"
+            }
+            check(!mustCreate) {
+                "Directory $path already exists"
+            }
+        }
+        fileSystem.createDirectories(
+            path,
+            mustCreate = mustCreate
+        )
+    }
+
+    override fun isRegularFile(): Boolean {
+        return fileSystem.metadataOrNull(path)?.isRegularFile == true
+    }
+
+    override fun isDirectory(): Boolean {
+        return fileSystem.metadataOrNull(path)?.isDirectory == true
+    }
+
+    override fun protectedResolve(relative: String): OkioPath {
+        return OkioPath(fileSystem, path / relative)
+    }
+
+    override fun parentFile(): OkioPath? {
+        return path.parent?.let {
+            OkioPath(fileSystem = fileSystem, path = it)
+        }
+    }
+
+    override fun protectedWrite(body: ByteArray) {
+        fileSystem.write(path) {
+            write(body)
+            flush()
+        }
+    }
+
+    override fun protectedReadBytes(): ByteArray {
+        return fileSystem.read(path) {
+            readByteArray()
+        }
     }
 }
diff --git a/testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/TestIO.kt b/testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/TestIO.kt
index 2c7790e..f40abff 100644
--- a/testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/TestIO.kt
+++ b/testutils/testutils-datastore/src/commonMain/kotlin/androidx/datastore/TestIO.kt
@@ -20,20 +20,29 @@
 import androidx.datastore.core.DataStoreFactory.create
 import androidx.datastore.core.InterProcessCoordinator
 import androidx.datastore.core.Storage
+import kotlin.random.Random
 import kotlin.reflect.KClass
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 
-@OptIn(ExperimentalCoroutinesApi::class)
-abstract class TestIO<F : TestFile, IOE : Throwable>(
-    protected val dirName: String = "datastore-test-dir"
+abstract class TestIO<F : TestFile<F>, IOE : Throwable>(
+    getTmpDir: () -> F
 ) {
+    private val testRoot: F = getTmpDir().let {
+        createNewRandomChild(
+            parent = it,
+            namePrefix = "datastore-testio-"
+        )
+    }
+
+    init {
+        testRoot.mkdirs(mustCreate = true)
+    }
 
     fun getStore(
         serializerConfig: TestingSerializerConfig,
         scope: CoroutineScope,
         coordinatorProducer: () -> InterProcessCoordinator,
-        futureFile: () -> TestFile
+        futureFile: () -> F
     ): DataStore<Byte> {
         return create(getStorage(serializerConfig, coordinatorProducer, futureFile), scope = scope)
     }
@@ -41,26 +50,52 @@
     abstract fun getStorage(
         serializerConfig: TestingSerializerConfig,
         coordinatorProducer: () -> InterProcessCoordinator,
-        futureFile: () -> TestFile = { newTempFile() }
+        futureFile: () -> F = { newTempFile() }
     ): Storage<Byte>
 
-    abstract fun tempDir(
-        directoryPath: String? = null,
-        makeDirs: Boolean = true,
-        parentDir: F? = null
-    ): F
+    private fun randomName(
+        prefix: String
+    ): String {
+        return prefix + (0 until 15).joinToString(separator = "") {
+            ('a' + Random.nextInt(from = 0, until = 26)).toString()
+        }
+    }
 
-    abstract fun newTempFile(tempFolder: F = tempDir()): F
+    protected fun createNewRandomChild(
+        parent: F,
+        namePrefix: String = "test-file-",
+    ): F {
+        while (true) {
+            val child = parent.resolve(randomName(namePrefix))
+            if (!child.exists()) {
+                return child
+            }
+        }
+    }
+
+    /**
+     * Returns a new file instance without creating it or its parents.
+     */
+    fun newTempFile(
+        parentFile: F? = null,
+        relativePath: String? = null
+    ): F {
+        val parent = parentFile ?: testRoot
+        return if (relativePath == null) {
+            createNewRandomChild(
+                parent = parent
+            )
+        } else {
+            parent.resolve(relativePath)
+        }
+    }
 
     abstract fun ioException(message: String): IOE
 
     abstract fun ioExceptionClass(): KClass<IOE>
-    abstract fun isDirectory(file: F): Boolean
 }
 
-abstract class TestFile {
-    abstract fun getAbsolutePath(): String
-
+abstract class TestFile<T : TestFile<T>> {
     /**
      * Deletes the file if it exists.
      * Will return `false` if the file does not exist or cannot be deleted. (similar to File.delete)
@@ -73,14 +108,91 @@
     abstract fun exists(): Boolean
 
     /**
-     * Creates the file if it doesn't exist.
-     * @return `true` if file didn't exist and gets created and false otherwise.
+     * Creates a directory from the file.
+     *
+     * If it is a regular file, will throw.
+     * If [mustCreate] is `true` and directory already exists, will throw.
      */
-    abstract fun createIfNotExists(): Boolean
+    abstract fun mkdirs(mustCreate: Boolean = false)
 
-    fun deleteIfExists() {
-        if (exists()) {
-            delete()
+    /**
+     * Returns `true` if this exists and a regular file on the filesystem.
+     */
+    abstract fun isRegularFile(): Boolean
+
+    /**
+     * Returns `true` if this exists and is a directory on the filesystem.
+     */
+    abstract fun isDirectory(): Boolean
+
+    /**
+     * Resolves the given [relative] relative to `this`.
+     * (similar to File.resolve in kotlin stdlib).
+     *
+     * Note that this path is sanitized to ensure it is not a root path
+     * (e.g. does not start with `/`)
+     */
+    fun resolve(relative: String): T {
+        return if (relative.startsWith("/")) {
+            protectedResolve(relative.substring(startIndex = 1))
+        } else {
+            protectedResolve(relative)
         }
     }
+
+    /**
+     * Implemented by the subclasses to resolve child from sanitized name.
+     */
+    abstract fun protectedResolve(relative: String): T
+
+    /**
+     * Returns the parent file or null if this has no parent.
+     */
+    abstract fun parentFile(): T?
+
+    /**
+     * Writes the given [body] test into the file using the default encoding
+     * (kotlin stdlib's String.encodeToByteArray)
+     */
+    fun write(body: String) {
+        write(body.encodeToByteArray())
+    }
+    /**
+     * Overrides the file with the given contents.
+     * If parent directories do not exist, they'll be created.
+     */
+    fun write(body: ByteArray) {
+        if (isDirectory()) {
+            error("Cannot write to a directory")
+        }
+        parentFile()?.mkdirs(mustCreate = false)
+        parentFile()?.mkdirs()
+        protectedWrite(body)
+    }
+
+    /**
+     * Reads the byte contents of the file.
+     */
+    fun readBytes(): ByteArray {
+        check(exists()) {
+            "File does not exist"
+        }
+        return protectedReadBytes()
+    }
+
+    fun readText() = readBytes().decodeToString()
+
+    /**
+     * Writes the given [body] into the file. This is called after
+     * necessary checks are done so implementers should only focus on
+     * writing the contents.
+     */
+    abstract fun protectedWrite(body: ByteArray)
+
+    /**
+     * Reads the byte contents of the file. This is called after necessary
+     * checks are done so implementers should only focus on reading the
+     * bytes.
+     */
+    abstract fun protectedReadBytes(): ByteArray
 }
diff --git a/testutils/testutils-datastore/src/commonTest/kotlin/androidx/datastore/TestIOTestBase.kt b/testutils/testutils-datastore/src/commonTest/kotlin/androidx/datastore/TestIOTestBase.kt
new file mode 100644
index 0000000..e9a524a
--- /dev/null
+++ b/testutils/testutils-datastore/src/commonTest/kotlin/androidx/datastore/TestIOTestBase.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.datastore
+
+import androidx.kruth.assertThat
+import androidx.kruth.assertThrows
+import kotlin.test.Test
+
+class OkioTestIOTest : TestIOTestBase(OkioTestIO())
+
+abstract class TestIOTestBase(
+    private val testIO: TestIO<*, *>
+) {
+    @Test
+    fun writeNewFile() {
+        val file = testIO.newTempFile()
+        assertThat(file.exists()).isFalse()
+        file.write("body")
+        assertThat(file.exists()).isTrue()
+        assertThat(file.isDirectory()).isFalse()
+        assertThat(file.isRegularFile()).isTrue()
+    }
+
+    @Test
+    fun createDirectory() {
+        val file = testIO.newTempFile(relativePath = "some/sub/directory")
+        assertThat(file.exists()).isFalse()
+        file.mkdirs(mustCreate = true)
+        assertThat(file.isDirectory()).isTrue()
+
+        // try to re-create over it, should fail
+        assertThrows<Throwable> {
+            file.mkdirs(mustCreate = true)
+        }
+        // don't need to create so should be fine
+        file.mkdirs(mustCreate = false)
+    }
+
+    @Test
+    fun createDirectory_overFile() {
+        val file = testIO.newTempFile(relativePath = "some/sub/file")
+        file.write("test")
+        assertThrows<Throwable> {
+            file.mkdirs(mustCreate = false)
+        }
+        assertThrows<Throwable> {
+            file.mkdirs(mustCreate = true)
+        }
+    }
+
+    @Test
+    fun writeToDirectory() {
+        val file = testIO.newTempFile(relativePath = "some/dir")
+        file.mkdirs()
+        assertThrows<Throwable> {
+            file.write("some text")
+        }
+    }
+
+    @Test
+    fun resolve() {
+        val file = testIO.newTempFile(relativePath = "dir1/dir2/file")
+        file.write("test")
+        val file2 = testIO.newTempFile(relativePath = "dir1/dir2/file")
+        assertThat(file2.readText()).isEqualTo("test")
+        val parent = testIO.newTempFile(relativePath = "dir1")
+        assertThat(
+            parent.resolve("dir2/file").readText()
+        ).isEqualTo("test")
+    }
+
+    @Test
+    fun delete_regularFile() {
+        val file = testIO.newTempFile(relativePath = "foo")
+        file.write("foo")
+        assertThat(file.isRegularFile()).isTrue()
+        file.delete()
+        assertThat(file.exists()).isFalse()
+    }
+
+    @Test
+    fun delete_directory() {
+        val file = testIO.newTempFile(relativePath = "foo/bar")
+        file.mkdirs()
+        assertThat(file.isDirectory()).isTrue()
+        file.delete()
+        assertThat(file.exists()).isFalse()
+    }
+
+    @Test
+    fun readBytes() {
+        val file = testIO.newTempFile()
+        // cannot read non-existing file
+        assertThrows<Throwable> {
+            file.readBytes()
+        }
+        file.mkdirs()
+        // cannot read a directory
+        assertThrows<Throwable> {
+            file.readBytes()
+        }
+    }
+}
diff --git a/testutils/testutils-datastore/src/jvmMain/kotlin/androidx/datastore/FileTestIO.kt b/testutils/testutils-datastore/src/jvmMain/kotlin/androidx/datastore/FileTestIO.kt
index 3257267..f18290f 100644
--- a/testutils/testutils-datastore/src/jvmMain/kotlin/androidx/datastore/FileTestIO.kt
+++ b/testutils/testutils-datastore/src/jvmMain/kotlin/androidx/datastore/FileTestIO.kt
@@ -22,76 +22,23 @@
 import androidx.datastore.core.TestingSerializer
 import java.io.File
 import java.io.IOException
-import java.util.UUID
 import kotlin.reflect.KClass
 
-class FileTestIO(dirName: String = "test-dir") : TestIO<JavaIOFile, IOException>(dirName) {
-
-    override fun tempDir(
-        directoryPath: String?,
-        makeDirs: Boolean,
-        parentDir: JavaIOFile?
-    ): JavaIOFile {
-        return if (parentDir != null) {
-            if (directoryPath != null) {
-                val tempPath = File(parentDir.file, directoryPath)
-                if (makeDirs) {
-                    tempPath.mkdirs()
-                }
-                tempPath.toJavaFile()
-            } else {
-                val tempPath = File(parentDir.file, "tempPath")
-                if (makeDirs) {
-                    tempPath.mkdirs()
-                }
-                tempPath.toJavaFile()
-            }
-        } else {
-            if (directoryPath != null) {
-                val tempRoot = File.createTempFile("placeholder", "placeholder").parentFile
-                val tempPath = File(tempRoot, directoryPath)
-                if (makeDirs) {
-                    tempPath.mkdirs()
-                }
-                tempPath.toJavaFile()
-            } else {
-                File.createTempFile("temp", "tmp").parentFile!!.toJavaFile()
-            }
-        }
+class FileTestIO : TestIO<JavaIOFile, IOException>(
+    getTmpDir = {
+        File(System.getProperty("java.io.tmpdir")).toJavaFile()
     }
-
-    override fun newTempFile(tempFolder: JavaIOFile): JavaIOFile {
-        /**
-         * We need to create a new temp file without creating it so we use a UUID to decide the
-         * file name but also try 100 times in case a collision happens.
-         */
-        // TODO use a consistent naming scheme (e.g. incrementing counter) when b/276983736 is
-        //  fixed. We'll need these TestIO classes to do proper cleanup after themselves to be able
-        //  do that.
-        repeat(100) {
-            val randName = UUID.randomUUID().toString().take(10)
-            val tmpFile = File(tempFolder.file, "temp-$randName-temp")
-            if (!tmpFile.exists()) {
-                return tmpFile.toJavaFile()
-            }
-        }
-        error("Unable to create temp file in $tempFolder")
-    }
-
+) {
     override fun getStorage(
         serializerConfig: TestingSerializerConfig,
         coordinatorProducer: () -> InterProcessCoordinator,
-        futureFile: () -> TestFile
+        futureFile: () -> JavaIOFile
     ): Storage<Byte> {
         return FileStorage(TestingSerializer(serializerConfig), { coordinatorProducer() }) {
-            (futureFile() as JavaIOFile).file
+            futureFile().file
         }
     }
 
-    override fun isDirectory(file: JavaIOFile): Boolean {
-        return file.file.isDirectory
-    }
-
     override fun ioException(message: String): IOException {
         return IOException(message)
     }
@@ -100,11 +47,7 @@
         IOException::class
 }
 
-class JavaIOFile(val file: File) : TestFile() {
-    override fun getAbsolutePath(): String {
-        return file.absolutePath
-    }
-
+class JavaIOFile(val file: File) : TestFile<JavaIOFile>() {
     override fun delete(): Boolean {
         return file.delete()
     }
@@ -113,10 +56,40 @@
         return file.exists()
     }
 
-    override fun createIfNotExists(): Boolean {
-        if (exists()) return false
-        file.createNewFile()
-        return true
+    override fun mkdirs(mustCreate: Boolean) {
+        if (file.exists()) {
+            check(!mustCreate) {
+                "file $file already exists"
+            }
+        }
+        file.mkdirs()
+        check(file.isDirectory) {
+            "Failed to create directories for $file"
+        }
+    }
+
+    override fun isRegularFile(): Boolean {
+        return file.isFile
+    }
+
+    override fun isDirectory(): Boolean {
+        return file.isDirectory
+    }
+
+    override fun protectedResolve(relative: String): JavaIOFile {
+        return file.resolve(relative).toJavaFile()
+    }
+
+    override fun parentFile(): JavaIOFile? {
+        return file.parentFile?.toJavaFile()
+    }
+
+    override fun protectedWrite(body: ByteArray) {
+        file.writeBytes(body)
+    }
+
+    override fun protectedReadBytes(): ByteArray {
+        return file.readBytes()
     }
 }
 
diff --git a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence1.java b/testutils/testutils-datastore/src/jvmTest/kotlin/androidx/datastore/FileIOTest.kt
similarity index 75%
copy from lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence1.java
copy to testutils/testutils-datastore/src/jvmTest/kotlin/androidx/datastore/FileIOTest.kt
index ae9c85f..c526e8f 100644
--- a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence1.java
+++ b/testutils/testutils-datastore/src/jvmTest/kotlin/androidx/datastore/FileIOTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,10 +14,8 @@
  * limitations under the License.
  */
 
-package androidx.lifecycle.observers;
+package androidx.datastore
 
-public class DerivedSequence1 extends Base {
-
-    public void something() {
-    }
-}
+class FileIOTest : TestIOTestBase(
+    FileTestIO()
+)
diff --git a/testutils/testutils-fonts/build.gradle b/testutils/testutils-fonts/build.gradle
index 911cf9b..c731e06 100644
--- a/testutils/testutils-fonts/build.gradle
+++ b/testutils/testutils-fonts/build.gradle
@@ -31,7 +31,7 @@
 }
 
 dependencies {
-    implementation(libs.kotlinStdlib)
+    commonMainImplementation(libs.kotlinStdlib)
 }
 
 android {
diff --git a/testutils/testutils-gradle-plugin/src/main/java/androidx/testutils/gradle/ProjectSetupRule.kt b/testutils/testutils-gradle-plugin/src/main/java/androidx/testutils/gradle/ProjectSetupRule.kt
index 0724f1f..b425ca5 100644
--- a/testutils/testutils-gradle-plugin/src/main/java/androidx/testutils/gradle/ProjectSetupRule.kt
+++ b/testutils/testutils-gradle-plugin/src/main/java/androidx/testutils/gradle/ProjectSetupRule.kt
@@ -220,7 +220,8 @@
     val debugKeystore: String,
     var navigationRuntime: String,
     val kotlinStblib: String,
-    val kotlinVersion: String,
+    val kgpVersion: String,
+    val kgpDependency: String,
     val kspVersion: String,
     val rootProjectPath: String,
     val tipOfTreeMavenRepoPath: String,
@@ -262,7 +263,9 @@
                 minSdkVersion = properties.getProperty("minSdkVersion"),
                 navigationRuntime = properties.getProperty("navigationRuntime"),
                 kotlinStblib = properties.getProperty("kotlinStdlib"),
-                kotlinVersion = properties.getProperty("kotlinVersion"),
+                kgpVersion = properties.getProperty("kgpVersion"),
+                kgpDependency = "org.jetbrains.kotlin:kotlin-gradle-plugin:" +
+                    properties.getProperty("kgpVersion"),
                 kspVersion = properties.getProperty("kspVersion"),
                 agpDependency = properties.getProperty("agpDependency"),
                 buildSrcOutPath = properties.getCanonicalPath("buildSrcOutRelativePath")
diff --git a/tracing/tracing-perfetto-binary/api/1.0.0-beta01.txt b/tracing/tracing-perfetto-binary/api/1.0.0-beta01.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/tracing/tracing-perfetto-binary/api/1.0.0-beta01.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/tracing/tracing-perfetto-binary/api/res-1.0.0-beta01.txt b/tracing/tracing-perfetto-binary/api/res-1.0.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tracing/tracing-perfetto-binary/api/res-1.0.0-beta01.txt
diff --git a/tracing/tracing-perfetto-binary/api/restricted_1.0.0-beta01.txt b/tracing/tracing-perfetto-binary/api/restricted_1.0.0-beta01.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/tracing/tracing-perfetto-binary/api/restricted_1.0.0-beta01.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/tracing/tracing-perfetto-binary/build.gradle b/tracing/tracing-perfetto-binary/build.gradle
index 88c5f3c..13b2445 100644
--- a/tracing/tracing-perfetto-binary/build.gradle
+++ b/tracing/tracing-perfetto-binary/build.gradle
@@ -84,7 +84,6 @@
 androidx {
     name = "Tracing Perfetto Binary"
     publish = Publish.SNAPSHOT_AND_RELEASE
-    mavenVersion = LibraryVersions.TRACING_PERFETTO
     inceptionYear = "2022"
     description = "Provides native binaries required by AndroidX Tracing: Perfetto SDK " +
         "and is not intended to be used outside of that context."
diff --git a/tracing/tracing-perfetto-binary/src/main/cpp/tracing_perfetto.cc b/tracing/tracing-perfetto-binary/src/main/cpp/tracing_perfetto.cc
index 9e377d5..23dac58 100644
--- a/tracing/tracing-perfetto-binary/src/main/cpp/tracing_perfetto.cc
+++ b/tracing/tracing-perfetto-binary/src/main/cpp/tracing_perfetto.cc
@@ -25,7 +25,7 @@
 // Concept of version useful e.g. for human-readable error messages, and stable once released.
 // Does not replace the need for a binary verification mechanism (e.g. checksum check).
 // TODO: populate using CMake
-#define VERSION "1.0.0-alpha17"
+#define VERSION "1.0.0-beta01"
 
 namespace tracing_perfetto {
     void RegisterWithPerfetto() {
diff --git a/tracing/tracing-perfetto-handshake/api/1.0.0-beta01.txt b/tracing/tracing-perfetto-handshake/api/1.0.0-beta01.txt
new file mode 100644
index 0000000..db66f59
--- /dev/null
+++ b/tracing/tracing-perfetto-handshake/api/1.0.0-beta01.txt
@@ -0,0 +1,49 @@
+// Signature format: 4.0
+package androidx.tracing.perfetto.handshake {
+
+  public final class PerfettoSdkHandshake {
+    ctor public PerfettoSdkHandshake(String targetPackage, kotlin.jvm.functions.Function1<? super java.lang.String,? extends java.util.Map<java.lang.String,java.lang.String>> parseJsonMap, kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.String> executeShellCommand);
+    method public androidx.tracing.perfetto.handshake.protocol.Response disableTracingColdStart();
+    method public androidx.tracing.perfetto.handshake.protocol.Response enableTracingColdStart();
+    method public androidx.tracing.perfetto.handshake.protocol.Response enableTracingColdStart(optional boolean persistent);
+    method public androidx.tracing.perfetto.handshake.protocol.Response enableTracingColdStart(optional boolean persistent, optional androidx.tracing.perfetto.handshake.PerfettoSdkHandshake.LibrarySource? librarySource);
+    method public androidx.tracing.perfetto.handshake.protocol.Response enableTracingImmediate(optional androidx.tracing.perfetto.handshake.PerfettoSdkHandshake.LibrarySource? librarySource);
+  }
+
+  public abstract static sealed class PerfettoSdkHandshake.LibrarySource {
+    method public static final androidx.tracing.perfetto.handshake.PerfettoSdkHandshake.LibrarySource aarLibrarySource(java.io.File aarFile, java.io.File tempDirectory, kotlin.jvm.functions.Function2<? super java.io.File,? super java.io.File,kotlin.Unit> moveLibFileFromTmpDirToAppDir);
+    method public static final androidx.tracing.perfetto.handshake.PerfettoSdkHandshake.LibrarySource apkLibrarySource(java.io.File apkFile, java.io.File tempDirectory, kotlin.jvm.functions.Function2<? super java.io.File,? super java.io.File,kotlin.Unit> moveLibFileFromTmpDirToAppDir);
+    field public static final androidx.tracing.perfetto.handshake.PerfettoSdkHandshake.LibrarySource.Companion Companion;
+  }
+
+  public static final class PerfettoSdkHandshake.LibrarySource.Companion {
+    method public androidx.tracing.perfetto.handshake.PerfettoSdkHandshake.LibrarySource aarLibrarySource(java.io.File aarFile, java.io.File tempDirectory, kotlin.jvm.functions.Function2<? super java.io.File,? super java.io.File,kotlin.Unit> moveLibFileFromTmpDirToAppDir);
+    method public androidx.tracing.perfetto.handshake.PerfettoSdkHandshake.LibrarySource apkLibrarySource(java.io.File apkFile, java.io.File tempDirectory, kotlin.jvm.functions.Function2<? super java.io.File,? super java.io.File,kotlin.Unit> moveLibFileFromTmpDirToAppDir);
+  }
+
+}
+
+package androidx.tracing.perfetto.handshake.protocol {
+
+  public final class Response {
+    method public String? getMessage();
+    method public String? getRequiredVersion();
+    method public int getResultCode();
+    property public final String? message;
+    property public final String? requiredVersion;
+    property public final int resultCode;
+  }
+
+  public final class ResponseResultCodes {
+    field public static final androidx.tracing.perfetto.handshake.protocol.ResponseResultCodes INSTANCE;
+    field public static final int RESULT_CODE_ALREADY_ENABLED = 2; // 0x2
+    field public static final int RESULT_CODE_CANCELLED = 0; // 0x0
+    field public static final int RESULT_CODE_ERROR_BINARY_MISSING = 11; // 0xb
+    field public static final int RESULT_CODE_ERROR_BINARY_VERIFICATION_ERROR = 13; // 0xd
+    field public static final int RESULT_CODE_ERROR_BINARY_VERSION_MISMATCH = 12; // 0xc
+    field public static final int RESULT_CODE_ERROR_OTHER = 99; // 0x63
+    field public static final int RESULT_CODE_SUCCESS = 1; // 0x1
+  }
+
+}
+
diff --git a/tracing/tracing-perfetto-handshake/api/restricted_1.0.0-beta01.txt b/tracing/tracing-perfetto-handshake/api/restricted_1.0.0-beta01.txt
new file mode 100644
index 0000000..db66f59
--- /dev/null
+++ b/tracing/tracing-perfetto-handshake/api/restricted_1.0.0-beta01.txt
@@ -0,0 +1,49 @@
+// Signature format: 4.0
+package androidx.tracing.perfetto.handshake {
+
+  public final class PerfettoSdkHandshake {
+    ctor public PerfettoSdkHandshake(String targetPackage, kotlin.jvm.functions.Function1<? super java.lang.String,? extends java.util.Map<java.lang.String,java.lang.String>> parseJsonMap, kotlin.jvm.functions.Function1<? super java.lang.String,java.lang.String> executeShellCommand);
+    method public androidx.tracing.perfetto.handshake.protocol.Response disableTracingColdStart();
+    method public androidx.tracing.perfetto.handshake.protocol.Response enableTracingColdStart();
+    method public androidx.tracing.perfetto.handshake.protocol.Response enableTracingColdStart(optional boolean persistent);
+    method public androidx.tracing.perfetto.handshake.protocol.Response enableTracingColdStart(optional boolean persistent, optional androidx.tracing.perfetto.handshake.PerfettoSdkHandshake.LibrarySource? librarySource);
+    method public androidx.tracing.perfetto.handshake.protocol.Response enableTracingImmediate(optional androidx.tracing.perfetto.handshake.PerfettoSdkHandshake.LibrarySource? librarySource);
+  }
+
+  public abstract static sealed class PerfettoSdkHandshake.LibrarySource {
+    method public static final androidx.tracing.perfetto.handshake.PerfettoSdkHandshake.LibrarySource aarLibrarySource(java.io.File aarFile, java.io.File tempDirectory, kotlin.jvm.functions.Function2<? super java.io.File,? super java.io.File,kotlin.Unit> moveLibFileFromTmpDirToAppDir);
+    method public static final androidx.tracing.perfetto.handshake.PerfettoSdkHandshake.LibrarySource apkLibrarySource(java.io.File apkFile, java.io.File tempDirectory, kotlin.jvm.functions.Function2<? super java.io.File,? super java.io.File,kotlin.Unit> moveLibFileFromTmpDirToAppDir);
+    field public static final androidx.tracing.perfetto.handshake.PerfettoSdkHandshake.LibrarySource.Companion Companion;
+  }
+
+  public static final class PerfettoSdkHandshake.LibrarySource.Companion {
+    method public androidx.tracing.perfetto.handshake.PerfettoSdkHandshake.LibrarySource aarLibrarySource(java.io.File aarFile, java.io.File tempDirectory, kotlin.jvm.functions.Function2<? super java.io.File,? super java.io.File,kotlin.Unit> moveLibFileFromTmpDirToAppDir);
+    method public androidx.tracing.perfetto.handshake.PerfettoSdkHandshake.LibrarySource apkLibrarySource(java.io.File apkFile, java.io.File tempDirectory, kotlin.jvm.functions.Function2<? super java.io.File,? super java.io.File,kotlin.Unit> moveLibFileFromTmpDirToAppDir);
+  }
+
+}
+
+package androidx.tracing.perfetto.handshake.protocol {
+
+  public final class Response {
+    method public String? getMessage();
+    method public String? getRequiredVersion();
+    method public int getResultCode();
+    property public final String? message;
+    property public final String? requiredVersion;
+    property public final int resultCode;
+  }
+
+  public final class ResponseResultCodes {
+    field public static final androidx.tracing.perfetto.handshake.protocol.ResponseResultCodes INSTANCE;
+    field public static final int RESULT_CODE_ALREADY_ENABLED = 2; // 0x2
+    field public static final int RESULT_CODE_CANCELLED = 0; // 0x0
+    field public static final int RESULT_CODE_ERROR_BINARY_MISSING = 11; // 0xb
+    field public static final int RESULT_CODE_ERROR_BINARY_VERIFICATION_ERROR = 13; // 0xd
+    field public static final int RESULT_CODE_ERROR_BINARY_VERSION_MISMATCH = 12; // 0xc
+    field public static final int RESULT_CODE_ERROR_OTHER = 99; // 0x63
+    field public static final int RESULT_CODE_SUCCESS = 1; // 0x1
+  }
+
+}
+
diff --git a/tracing/tracing-perfetto-handshake/build.gradle b/tracing/tracing-perfetto-handshake/build.gradle
index 1081279..9f6adaa 100644
--- a/tracing/tracing-perfetto-handshake/build.gradle
+++ b/tracing/tracing-perfetto-handshake/build.gradle
@@ -35,7 +35,6 @@
 androidx {
     name = "Tracing Perfetto Handshake"
     publish = Publish.SNAPSHOT_AND_RELEASE
-    mavenVersion = LibraryVersions.TRACING_PERFETTO
     inceptionYear = "2022"
     description = "AndroidX Tracing: Perfetto Handshake"
 }
diff --git a/tracing/tracing-perfetto/api/1.0.0-beta01.txt b/tracing/tracing-perfetto/api/1.0.0-beta01.txt
new file mode 100644
index 0000000..17a19c5
--- /dev/null
+++ b/tracing/tracing-perfetto/api/1.0.0-beta01.txt
@@ -0,0 +1,19 @@
+// Signature format: 4.0
+package androidx.tracing.perfetto {
+
+  public final class PerfettoSdkTrace {
+    method public void beginSection(String sectionName);
+    method public void endSection();
+    method public boolean isEnabled();
+    property public final boolean isEnabled;
+    field public static final androidx.tracing.perfetto.PerfettoSdkTrace INSTANCE;
+  }
+
+  public final class StartupTracingInitializer implements androidx.startup.Initializer<kotlin.Unit> {
+    ctor public StartupTracingInitializer();
+    method public void create(android.content.Context context);
+    method public java.util.List<java.lang.Class<? extends androidx.startup.Initializer<?>>> dependencies();
+  }
+
+}
+
diff --git a/tracing/tracing-perfetto/api/res-1.0.0-beta01.txt b/tracing/tracing-perfetto/api/res-1.0.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tracing/tracing-perfetto/api/res-1.0.0-beta01.txt
diff --git a/tracing/tracing-perfetto/api/restricted_1.0.0-beta01.txt b/tracing/tracing-perfetto/api/restricted_1.0.0-beta01.txt
new file mode 100644
index 0000000..17a19c5
--- /dev/null
+++ b/tracing/tracing-perfetto/api/restricted_1.0.0-beta01.txt
@@ -0,0 +1,19 @@
+// Signature format: 4.0
+package androidx.tracing.perfetto {
+
+  public final class PerfettoSdkTrace {
+    method public void beginSection(String sectionName);
+    method public void endSection();
+    method public boolean isEnabled();
+    property public final boolean isEnabled;
+    field public static final androidx.tracing.perfetto.PerfettoSdkTrace INSTANCE;
+  }
+
+  public final class StartupTracingInitializer implements androidx.startup.Initializer<kotlin.Unit> {
+    ctor public StartupTracingInitializer();
+    method public void create(android.content.Context context);
+    method public java.util.List<java.lang.Class<? extends androidx.startup.Initializer<?>>> dependencies();
+  }
+
+}
+
diff --git a/tracing/tracing-perfetto/build.gradle b/tracing/tracing-perfetto/build.gradle
index ba5c591..e4946a2 100644
--- a/tracing/tracing-perfetto/build.gradle
+++ b/tracing/tracing-perfetto/build.gradle
@@ -60,7 +60,6 @@
 androidx {
     name = "Tracing Perfetto"
     publish = Publish.SNAPSHOT_AND_RELEASE
-    mavenVersion = LibraryVersions.TRACING_PERFETTO
     inceptionYear = "2022"
     description = "AndroidX Tracing: Perfetto SDK"
 }
diff --git a/tracing/tracing-perfetto/src/androidTest/java/androidx/tracing/perfetto/jni/test/PerfettoNativeTest.kt b/tracing/tracing-perfetto/src/androidTest/java/androidx/tracing/perfetto/jni/test/PerfettoNativeTest.kt
index 5a70f00..2884d0a 100644
--- a/tracing/tracing-perfetto/src/androidTest/java/androidx/tracing/perfetto/jni/test/PerfettoNativeTest.kt
+++ b/tracing/tracing-perfetto/src/androidTest/java/androidx/tracing/perfetto/jni/test/PerfettoNativeTest.kt
@@ -30,7 +30,7 @@
         init {
             PerfettoNative.loadLib()
         }
-        const val libraryVersion = "1.0.0-alpha17" // TODO: get using reflection
+        const val libraryVersion = "1.0.0-beta01" // TODO: get using reflection
     }
 
     @Test
diff --git a/tracing/tracing-perfetto/src/main/AndroidManifest.xml b/tracing/tracing-perfetto/src/main/AndroidManifest.xml
index 1eebae8..7402b3a 100644
--- a/tracing/tracing-perfetto/src/main/AndroidManifest.xml
+++ b/tracing/tracing-perfetto/src/main/AndroidManifest.xml
@@ -32,7 +32,7 @@
             </intent-filter>
         </receiver>
 
-        <!-- TODO(245426369): enable when feature complete (i.e. after measuring perf impact) -->
+        <!-- TODO(283953019): enable when feature complete (i.e. after measuring perf impact) -->
         <!--
         <provider
             android:name="androidx.startup.InitializationProvider"
diff --git a/tracing/tracing-perfetto/src/main/java/androidx/tracing/perfetto/jni/PerfettoNative.kt b/tracing/tracing-perfetto/src/main/java/androidx/tracing/perfetto/jni/PerfettoNative.kt
index 31f40a1..a613038 100644
--- a/tracing/tracing-perfetto/src/main/java/androidx/tracing/perfetto/jni/PerfettoNative.kt
+++ b/tracing/tracing-perfetto/src/main/java/androidx/tracing/perfetto/jni/PerfettoNative.kt
@@ -25,12 +25,12 @@
 
     // TODO(224510255): load from a file produced at build time
     object Metadata {
-        const val version = "1.0.0-alpha17"
+        const val version = "1.0.0-beta01"
         val checksums = mapOf(
-            "arm64-v8a" to "50caf24da1716eb52974a4554da0d22e5c14ebf0deb40746a84011746ad28c5c",
-            "armeabi-v7a" to "e2c8fd9910635c37e0e59d758cb0418fd14e306d8a4f3962c7ea4f045e820ac2",
-            "x86" to "b8bca8f0792bc4e53db3e13c084f8573bd62c821d994fe92273586107dd5e16c",
-            "x86_64" to "0709dde1c246b249bc80bcbd3ff6ee28c70acf6ff6917b2753f40936056854e0",
+            "arm64-v8a" to "4d5a1d9006571fb14919a867599895bbd4433c7f6b7695fdbce73315489aa6e2",
+            "armeabi-v7a" to "74dc8c7e1423f4b3a43813acd0d02e46fb80424d09fa3ac7fbd04656699a00fc",
+            "x86" to "0aec446a0ffceaf290347a6bae07edb4e4456326687a720ce84ff50d7d2d88da",
+            "x86_64" to "bcea1f5fc4587692a4ee7f318ebeb356172da839a9d4cf8c7b1d45d285613814",
         )
     }
 
diff --git a/transition/transition/api/current.txt b/transition/transition/api/current.txt
index 672d6db..b6bf3ae 100644
--- a/transition/transition/api/current.txt
+++ b/transition/transition/api/current.txt
@@ -209,6 +209,7 @@
     method public static void beginDelayedTransition(android.view.ViewGroup);
     method public static void beginDelayedTransition(android.view.ViewGroup, androidx.transition.Transition?);
     method public static androidx.transition.TransitionSeekController? controlDelayedTransition(android.view.ViewGroup, androidx.transition.Transition);
+    method public static androidx.transition.TransitionSeekController? createSeekController(androidx.transition.Scene, androidx.transition.Transition);
     method public static void endTransitions(android.view.ViewGroup?);
     method public static void go(androidx.transition.Scene);
     method public static void go(androidx.transition.Scene, androidx.transition.Transition?);
@@ -225,14 +226,18 @@
   }
 
   public interface TransitionSeekController {
+    method public void addOnProgressChangedListener(androidx.core.util.Consumer<androidx.transition.TransitionSeekController!>);
     method public void addOnReadyListener(androidx.core.util.Consumer<androidx.transition.TransitionSeekController!>);
     method public void animateToEnd();
     method public void animateToStart();
-    method public long getCurrentPlayTimeMillis();
-    method public long getDurationMillis();
+    method @FloatRange(from=0.0, to=1.0) public float getCurrentFraction();
+    method @IntRange(from=0) public long getCurrentPlayTimeMillis();
+    method @IntRange(from=0) public long getDurationMillis();
     method public boolean isReady();
+    method public void removeOnProgressChangedListener(androidx.core.util.Consumer<androidx.transition.TransitionSeekController!>);
     method public void removeOnReadyListener(androidx.core.util.Consumer<androidx.transition.TransitionSeekController!>);
-    method public void setCurrentPlayTimeMillis(long);
+    method public void setCurrentFraction(@FloatRange(from=0.0, to=1.0) float);
+    method public void setCurrentPlayTimeMillis(@IntRange(from=0) long);
   }
 
   public class TransitionSet extends androidx.transition.Transition {
diff --git a/transition/transition/api/restricted_current.txt b/transition/transition/api/restricted_current.txt
index 187290f..3f44ab2 100644
--- a/transition/transition/api/restricted_current.txt
+++ b/transition/transition/api/restricted_current.txt
@@ -242,6 +242,7 @@
     method public static void beginDelayedTransition(android.view.ViewGroup);
     method public static void beginDelayedTransition(android.view.ViewGroup, androidx.transition.Transition?);
     method public static androidx.transition.TransitionSeekController? controlDelayedTransition(android.view.ViewGroup, androidx.transition.Transition);
+    method public static androidx.transition.TransitionSeekController? createSeekController(androidx.transition.Scene, androidx.transition.Transition);
     method public static void endTransitions(android.view.ViewGroup?);
     method public static void go(androidx.transition.Scene);
     method public static void go(androidx.transition.Scene, androidx.transition.Transition?);
@@ -258,14 +259,18 @@
   }
 
   public interface TransitionSeekController {
+    method public void addOnProgressChangedListener(androidx.core.util.Consumer<androidx.transition.TransitionSeekController!>);
     method public void addOnReadyListener(androidx.core.util.Consumer<androidx.transition.TransitionSeekController!>);
     method public void animateToEnd();
     method public void animateToStart();
-    method public long getCurrentPlayTimeMillis();
-    method public long getDurationMillis();
+    method @FloatRange(from=0.0, to=1.0) public float getCurrentFraction();
+    method @IntRange(from=0) public long getCurrentPlayTimeMillis();
+    method @IntRange(from=0) public long getDurationMillis();
     method public boolean isReady();
+    method public void removeOnProgressChangedListener(androidx.core.util.Consumer<androidx.transition.TransitionSeekController!>);
     method public void removeOnReadyListener(androidx.core.util.Consumer<androidx.transition.TransitionSeekController!>);
-    method public void setCurrentPlayTimeMillis(long);
+    method public void setCurrentFraction(@FloatRange(from=0.0, to=1.0) float);
+    method public void setCurrentPlayTimeMillis(@IntRange(from=0) long);
   }
 
   public class TransitionSet extends androidx.transition.Transition {
diff --git a/transition/transition/build.gradle b/transition/transition/build.gradle
index b899cd4..8ed8a8e 100644
--- a/transition/transition/build.gradle
+++ b/transition/transition/build.gradle
@@ -12,6 +12,7 @@
     implementation("androidx.collection:collection:1.1.0")
     compileOnly("androidx.fragment:fragment:1.2.5")
     compileOnly("androidx.appcompat:appcompat:1.0.1")
+    implementation("androidx.dynamicanimation:dynamicanimation:1.0.0")
 
     androidTestImplementation(libs.kotlinStdlib)
     androidTestImplementation(libs.testExtJunit)
diff --git a/transition/transition/src/androidTest/java/androidx/transition/SeekTransitionTest.kt b/transition/transition/src/androidTest/java/androidx/transition/SeekTransitionTest.kt
index 1700300..fd55d1a 100644
--- a/transition/transition/src/androidTest/java/androidx/transition/SeekTransitionTest.kt
+++ b/transition/transition/src/androidTest/java/androidx/transition/SeekTransitionTest.kt
@@ -27,6 +27,7 @@
 import androidx.test.filters.SdkSuppress
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.testutils.AnimationDurationScaleRule.Companion.createForAllTests
+import androidx.testutils.PollingCheck
 import androidx.transition.Transition.TransitionListener
 import androidx.transition.test.R
 import com.google.common.truth.Truth.assertThat
@@ -43,7 +44,7 @@
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 
-@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
 @MediumTest
 class SeekTransitionTest : BaseTest() {
     @get:Rule
@@ -69,7 +70,6 @@
     @Test(expected = IllegalArgumentException::class)
     @UiThreadTest
     fun onlySeekingTransitions() {
-        if (Build.VERSION.SDK_INT < 34) throw IllegalArgumentException()
         transition = object : Visibility() {}
         TransitionManager.controlDelayedTransition(root, transition)
         fail("Expected IllegalArgumentException")
@@ -77,7 +77,6 @@
 
     @Test
     fun waitForReady() {
-        if (Build.VERSION.SDK_INT < 34) return
         lateinit var seekController: TransitionSeekController
 
         @Suppress("UNCHECKED_CAST")
@@ -99,7 +98,6 @@
 
     @Test
     fun waitForReadyNoChange() {
-        if (Build.VERSION.SDK_INT < 34) return
         lateinit var seekController: TransitionSeekController
 
         @Suppress("UNCHECKED_CAST")
@@ -120,7 +118,6 @@
 
     @Test
     fun addListenerAfterReady() {
-        if (Build.VERSION.SDK_INT < 34) return
         lateinit var seekController: TransitionSeekController
 
         @Suppress("UNCHECKED_CAST")
@@ -148,7 +145,6 @@
 
     @Test
     fun seekTransition() {
-        if (Build.VERSION.SDK_INT < 34) return
         lateinit var seekController: TransitionSeekController
 
         val listener = spy(TransitionListenerAdapter())
@@ -170,15 +166,60 @@
 
             assertThat(seekController.durationMillis).isEqualTo(300)
             assertThat(seekController.currentPlayTimeMillis).isEqualTo(0)
+            assertThat(seekController.currentFraction).isEqualTo(0f)
 
             assertThat(view.transitionAlpha).isEqualTo(1f)
 
             seekController.currentPlayTimeMillis = 150
+            assertThat(seekController.currentFraction).isEqualTo(0.5f)
             assertThat(view.transitionAlpha).isEqualTo(0.5f)
             seekController.currentPlayTimeMillis = 299
+            assertThat(seekController.currentFraction).isWithin(0.001f).of(299f / 300f)
             assertThat(view.transitionAlpha).isWithin(0.001f).of(1f / 300f)
             seekController.currentPlayTimeMillis = 300
+            assertThat(seekController.currentFraction).isEqualTo(1f)
+            verify(listener, times(1)).onTransitionEnd(any())
 
+            assertThat(view.transitionAlpha).isEqualTo(1f)
+            assertThat(view.visibility).isEqualTo(View.GONE)
+        }
+    }
+
+    @Test
+    fun seekTransitionWithFraction() {
+        lateinit var seekController: TransitionSeekController
+
+        val listener = spy(TransitionListenerAdapter())
+        transition.addListener(listener)
+
+        rule.runOnUiThread {
+            val controller = TransitionManager.controlDelayedTransition(root, transition)
+            assertThat(controller).isNotNull()
+            seekController = controller!!
+            assertThat(seekController.isReady).isFalse()
+            view.visibility = View.GONE
+        }
+
+        verify(listener, timeout(1000)).onTransitionStart(any())
+        verify(listener, times(0)).onTransitionEnd(any())
+
+        rule.runOnUiThread {
+            assertThat(view.visibility).isEqualTo(View.VISIBLE)
+
+            assertThat(seekController.durationMillis).isEqualTo(300)
+            assertThat(seekController.currentPlayTimeMillis).isEqualTo(0)
+            assertThat(seekController.currentFraction).isEqualTo(0f)
+
+            assertThat(view.transitionAlpha).isEqualTo(1f)
+
+            seekController.currentFraction = 0.5f
+            assertThat(seekController.currentPlayTimeMillis).isEqualTo(150)
+            assertThat(view.transitionAlpha).isEqualTo(0.5f)
+            seekController.currentFraction = 299f / 300f
+            assertThat(seekController.currentPlayTimeMillis).isEqualTo(299)
+            assertThat(view.transitionAlpha).isWithin(0.001f).of(1f / 300f)
+            seekController.currentFraction = 1f
+            assertThat(seekController.currentPlayTimeMillis).isEqualTo(300)
             verify(listener, times(1)).onTransitionEnd(any())
 
             assertThat(view.transitionAlpha).isEqualTo(1f)
@@ -188,7 +229,6 @@
 
     @Test
     fun animationDoesNotTakeOverSeek() {
-        if (Build.VERSION.SDK_INT < 34) return
         lateinit var seekController: TransitionSeekController
 
         val stateListener1 = spy(TransitionListenerAdapter())
@@ -231,7 +271,6 @@
 
     @Test
     fun seekCannotTakeOverAnimation() {
-        if (Build.VERSION.SDK_INT < 34) return
         lateinit var seekController: TransitionSeekController
 
         val stateListener1 = spy(TransitionListenerAdapter())
@@ -271,7 +310,6 @@
 
     @Test
     fun seekCannotTakeOverSeek() {
-        if (Build.VERSION.SDK_INT < 34) return
         lateinit var seekController1: TransitionSeekController
 
         val stateListener1 = spy(TransitionListenerAdapter())
@@ -317,7 +355,6 @@
 
     @Test
     fun seekReplacesSeek() {
-        if (Build.VERSION.SDK_INT < 34) return
         lateinit var seekController1: TransitionSeekController
 
         val stateListener1 = spy(TransitionListenerAdapter())
@@ -360,7 +397,6 @@
 
     @Test
     fun animateToEnd() {
-        if (Build.VERSION.SDK_INT < 34) return
         lateinit var seekController: TransitionSeekController
 
         val listener = spy(TransitionListenerAdapter())
@@ -387,7 +423,6 @@
 
     @Test
     fun animateToStart() {
-        if (Build.VERSION.SDK_INT < 34) return
         lateinit var seekController: TransitionSeekController
 
         val listener = spy(TransitionListenerAdapter())
@@ -428,7 +463,6 @@
 
     @Test
     fun animateToStartAfterAnimateToEnd() {
-        if (Build.VERSION.SDK_INT < 34) return
         lateinit var seekController: TransitionSeekController
 
         val listener = spy(TransitionListenerAdapter())
@@ -448,7 +482,7 @@
             seekController.animateToStart()
         }
 
-        verify(listener, timeout(3000)).onTransitionEnd(any())
+        verify(listener, timeout(3000)).onTransitionEnd(any(), eq(true))
 
         rule.runOnUiThread {
             assertThat(view.visibility).isEqualTo(View.VISIBLE)
@@ -458,7 +492,6 @@
 
     @Test
     fun animateToEndAfterAnimateToStart() {
-        if (Build.VERSION.SDK_INT < 34) return
         lateinit var seekController: TransitionSeekController
 
         val listener = spy(TransitionListenerAdapter())
@@ -488,7 +521,6 @@
 
     @Test(expected = IllegalStateException::class)
     fun seekAfterAnimate() {
-        if (Build.VERSION.SDK_INT < 34) throw IllegalStateException("Not supported before U")
         lateinit var seekController: TransitionSeekController
         transition.duration = 5000
 
@@ -507,9 +539,28 @@
         }
     }
 
+    @Test(expected = IllegalStateException::class)
+    fun seekFractionAfterAnimate() {
+        lateinit var seekController: TransitionSeekController
+        transition.duration = 5000
+
+        rule.runOnUiThread {
+            seekController = TransitionManager.controlDelayedTransition(root, transition)!!
+            view.visibility = View.GONE
+        }
+
+        rule.runOnUiThread {
+            seekController.currentFraction = 0.5f
+            seekController.animateToEnd()
+        }
+
+        rule.runOnUiThread {
+            seekController.currentFraction = 0.2f
+        }
+    }
+
     @Test
     fun seekTransitionSet() {
-        if (Build.VERSION.SDK_INT < 34) return
         transition = TransitionSet().also {
             it.addTransition(Fade(Fade.MODE_OUT))
                 .addTransition(Fade(Fade.MODE_IN))
@@ -579,7 +630,6 @@
 
     @Test
     fun animateToEndTransitionSet() {
-        if (Build.VERSION.SDK_INT < 34) return
         transition = TransitionSet().also {
             it.addTransition(Fade(Fade.MODE_OUT))
                 .addTransition(Fade(Fade.MODE_IN))
@@ -629,7 +679,6 @@
 
     @Test
     fun animateToStartTransitionSet() {
-        if (Build.VERSION.SDK_INT < 34) return
         transition = TransitionSet().also {
             it.addTransition(Fade(Fade.MODE_OUT))
                 .addTransition(Fade(Fade.MODE_IN))
@@ -695,7 +744,6 @@
 
     @Test
     fun cancelPartOfTransitionSet() {
-        if (Build.VERSION.SDK_INT < 34) return
         transition = TransitionSet().also {
             it.addTransition(Fade(Fade.MODE_OUT))
                 .addTransition(Fade(Fade.MODE_IN))
@@ -763,7 +811,6 @@
 
     @Test
     fun onTransitionCallsForwardAndReversed() {
-        if (Build.VERSION.SDK_INT < 34) return
         val listener = spy(TransitionListenerAdapter())
         transition = Fade()
         transition.addListener(listener)
@@ -792,7 +839,6 @@
 
     @Test
     fun onTransitionCallsForwardAndReversedTransitionSet() {
-        if (Build.VERSION.SDK_INT < 34) return
         val fadeOut = Fade(Fade.MODE_OUT)
         val outListener = spy(TransitionListenerAdapter())
         fadeOut.addListener(outListener)
@@ -881,7 +927,6 @@
 
     @Test
     fun pauseResumeOnSeek() {
-        if (Build.VERSION.SDK_INT < 34) return
         var pauseCount = 0
         var resumeCount = 0
         var setPauseCount = 0
@@ -942,4 +987,131 @@
             assertThat(setResumeCount).isEqualTo(1)
         }
     }
+
+    @Test
+    fun animationListener() {
+        lateinit var seekController: TransitionSeekController
+        var animatedFraction = -1f
+        var animatedMillis = -1L
+        rule.runOnUiThread {
+            seekController = TransitionManager.controlDelayedTransition(root, Fade())!!
+            view.visibility = View.GONE
+
+            seekController.addOnProgressChangedListener {
+                animatedFraction = it.currentFraction
+                animatedMillis = it.currentPlayTimeMillis
+            }
+        }
+
+        rule.runOnUiThread {
+            assertThat(animatedFraction).isEqualTo(0f)
+            assertThat(animatedMillis).isEqualTo(0)
+            seekController.currentFraction = 0.25f
+            assertThat(animatedFraction).isEqualTo(0.25f)
+            assertThat(animatedMillis).isEqualTo(75)
+            seekController.animateToEnd()
+        }
+
+        PollingCheck.waitFor {
+            animatedFraction == 1f
+        }
+    }
+
+    @Test
+    fun animationListenerRemoval() {
+        lateinit var seekController: TransitionSeekController
+        rule.runOnUiThread {
+            seekController = TransitionManager.controlDelayedTransition(root, Fade())!!
+            view.visibility = View.GONE
+        }
+
+        var animatedFraction = -1f
+        var animatedMillis = -1L
+        val removeListener = object : Consumer<TransitionSeekController> {
+            override fun accept(t: TransitionSeekController?) {
+                seekController.removeOnProgressChangedListener(this)
+            }
+        }
+        seekController.addOnProgressChangedListener(removeListener)
+        val changeListener = Consumer<TransitionSeekController> {
+            animatedFraction = it.currentFraction
+            animatedMillis = it.currentPlayTimeMillis
+        }
+        seekController.addOnProgressChangedListener(changeListener)
+
+        rule.runOnUiThread {
+            assertThat(animatedFraction).isEqualTo(0f)
+            assertThat(animatedMillis).isEqualTo(0)
+            seekController.removeOnProgressChangedListener(changeListener)
+            seekController.currentFraction = 0.25f
+            assertThat(animatedFraction).isEqualTo(0)
+            assertThat(animatedMillis).isEqualTo(0)
+        }
+    }
+
+    @Test
+    fun seekToScene() {
+        lateinit var seekController: TransitionSeekController
+        val scene1 = Scene(root, view)
+        val view2 = View(view.context)
+        val scene2 = Scene(root, view2)
+        rule.runOnUiThread {
+            TransitionManager.go(scene1)
+        }
+
+        rule.runOnUiThread {
+            val controller = TransitionManager.createSeekController(scene2, Fade())
+            assertThat(controller).isNotNull()
+            seekController = controller!!
+        }
+
+        rule.runOnUiThread {
+            assertThat(seekController.currentFraction).isEqualTo(0f)
+            assertThat(view.visibility).isEqualTo(View.VISIBLE)
+            assertThat(view.transitionAlpha).isEqualTo(1f)
+            assertThat(view.isAttachedToWindow).isTrue()
+            assertThat(view2.visibility).isEqualTo(View.VISIBLE)
+            assertThat(view2.transitionAlpha).isEqualTo(0f)
+            assertThat(view2.isAttachedToWindow).isTrue()
+            seekController.currentFraction = 1f
+            assertThat(view.visibility).isEqualTo(View.VISIBLE)
+            assertThat(view.transitionAlpha).isEqualTo(1f)
+            assertThat(view.isAttachedToWindow).isFalse()
+            assertThat(view2.visibility).isEqualTo(View.VISIBLE)
+            assertThat(view2.transitionAlpha).isEqualTo(1f)
+            assertThat(view2.isAttachedToWindow).isTrue()
+        }
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun seekToScene_notSupportedTransition() {
+        class NoSeekingTransition : Fade() {
+            override fun isSeekingSupported(): Boolean = false
+        }
+        val scene1 = Scene(root, view)
+        val view2 = View(view.context)
+        val scene2 = Scene(root, view2)
+        rule.runOnUiThread {
+            TransitionManager.go(scene1)
+        }
+
+        rule.runOnUiThread {
+            TransitionManager.createSeekController(scene2, NoSeekingTransition())
+        }
+    }
+
+    @Test
+    fun seekToScene_alreadyRunningTransition() {
+        val scene1 = Scene(root, view)
+        val view2 = View(view.context)
+        val scene2 = Scene(root, view2)
+        rule.runOnUiThread {
+            TransitionManager.go(scene1)
+        }
+
+        rule.runOnUiThread {
+            TransitionManager.go(scene2, Fade())
+            assertThat(TransitionManager.createSeekController(scene1, Fade())).isNull()
+        }
+    }
 }
diff --git a/transition/transition/src/androidTest/java/androidx/transition/VelocityTracker1DTest.kt b/transition/transition/src/androidTest/java/androidx/transition/VelocityTracker1DTest.kt
new file mode 100644
index 0000000..d74c93e
--- /dev/null
+++ b/transition/transition/src/androidTest/java/androidx/transition/VelocityTracker1DTest.kt
@@ -0,0 +1,430 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.transition
+
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
+import kotlin.math.abs
+import org.junit.Test
+
+// Velocities between (1-Tolerance)*RV and (1+Tolerance)*RV are accepted
+// where RV is the "Real Velocity"
+private const val Tolerance: Float = 0.2f
+
+@SmallTest
+class VelocityTracker1DTest : BaseTest() {
+    @Test
+    fun twoPoints_nonDifferentialValues() {
+        checkTestCase(
+            VelocityTrackingTestCase(
+                dataPoints = listOf(1 to 5f, 2 to 15f),
+                expectedVelocity = 10000f
+            )
+        )
+    }
+
+    @Test
+    fun threePoints_pointerStoppedMoving_nonDifferentialValues() {
+        checkTestCase(
+            VelocityTrackingTestCase(
+                dataPoints = listOf(
+                    25 to 25f,
+                    50 to 50f,
+                    100 to 100f,
+                ),
+                // Expect 0 velocities, as the pointer will be considered to have stopped moving,
+                // due to the (100-50)=40ms gap from the last data point (i.e. it's effectively
+                // a data set with only 1 data point).
+                expectedVelocity = 0f,
+            )
+        )
+    }
+
+    /** Impulse strategy specific test cases. */
+    @Test
+    fun threePoints_zeroVelocity_nonDifferentialValues() {
+        checkTestCase(
+            VelocityTrackingTestCase(
+                dataPoints = listOf(
+                    0 to 273f,
+                    1 to 273f,
+                    2 to 273f,
+                ),
+                expectedVelocity = 0f
+            ),
+        )
+    }
+
+    @Test
+    fun resetTracking_defaultConstructor() {
+        // Fixed velocity at 5 points per 10 milliseconds
+        val tracker = VelocityTracker1D()
+        tracker.addDataPoint(0, 0f)
+        tracker.addDataPoint(10, 5f)
+        tracker.addDataPoint(20, 10f)
+        tracker.addDataPoint(30, 15f)
+        tracker.addDataPoint(40, 30f)
+
+        tracker.resetTracking()
+
+        assertThat(tracker.calculateVelocity()).isZero()
+    }
+
+    @Test
+    fun resetTracking_nonDifferentialValues_impulse() {
+        // Fixed velocity at 5 points per 10 milliseconds
+        val tracker = VelocityTracker1D()
+        tracker.addDataPoint(0, 0f)
+        tracker.addDataPoint(10, 5f)
+        tracker.addDataPoint(20, 10f)
+        tracker.addDataPoint(30, 15f)
+        tracker.addDataPoint(40, 30f)
+
+        tracker.resetTracking()
+
+        assertThat(tracker.calculateVelocity()).isZero()
+    }
+
+    @Test
+    fun linearMotion_positiveVelocity_positiveDataPoints_nonDifferentialValues() {
+        // Fixed velocity at 5 points per 10 milliseconds
+        checkTestCase(
+            VelocityTrackingTestCase(
+                dataPoints = listOf(
+                    0 to 0f,
+                    10 to 5f,
+                    20 to 10f,
+                    30 to 15f,
+                ),
+                expectedVelocity = 500f,
+            )
+        )
+    }
+
+    @Test
+    fun linearMotion_positiveVelocity_negativeDataPoints_nonDifferentialValues() {
+        // Fixed velocity at 5 points per 10 milliseconds
+        checkTestCase(
+            VelocityTrackingTestCase(
+                dataPoints = listOf(
+                    0 to -20f,
+                    10 to -15f,
+                    20 to -10f,
+                ),
+                expectedVelocity = 500f,
+            )
+        )
+    }
+
+    @Test
+    fun linearMotion_positiveVelocity_mixedSignDataPoints_nonDifferentialValues() {
+        // Fixed velocity at 5 points per 10 milliseconds
+        checkTestCase(
+            VelocityTrackingTestCase(
+                dataPoints = listOf(
+                    0 to -5f,
+                    10 to 0f,
+                    20 to 5f,
+                ),
+                expectedVelocity = 500f,
+            )
+        )
+    }
+
+    @Test
+    fun linearMotion_negativeVelocity_negativeDataPoints_nonDifferentialValues() {
+        // Fixed velocity at 5 points per 10 milliseconds
+        checkTestCase(
+            VelocityTrackingTestCase(
+                dataPoints = listOf(
+                    0 to 0f,
+                    10 to -5f,
+                    20 to -10f,
+                ),
+                expectedVelocity = -500f,
+            )
+        )
+    }
+
+    @Test
+    fun linearMotion_negativeVelocity_postiveDataPoints_nonDifferentialValues() {
+        // Fixed velocity at 5 points per 10 milliseconds
+        checkTestCase(
+            VelocityTrackingTestCase(
+                dataPoints = listOf(
+                    0 to 20f,
+                    10 to 15f,
+                    20 to 10f,
+                ),
+                expectedVelocity = -500f,
+            )
+        )
+    }
+
+    @Test
+    fun linearMotion_negativeVelocity_mixedSignDataPoints_nonDifferentialValues() {
+        // Fixed velocity at 5 points per 10 milliseconds
+        checkTestCase(
+            VelocityTrackingTestCase(
+                dataPoints = listOf(
+                    0 to 5f,
+                    10 to 0f,
+                    20 to -5f,
+                ),
+                expectedVelocity = -500f,
+            )
+        )
+    }
+
+    @Test
+    fun linearHalfMotion() {
+        // Stay still for 50 ms, and then move 100 points in the final 50 ms.
+        // The final line is sloped at 2 units/ms.
+        // This can be visualized as 2 lines: flat line (50ms), and line with slope of 2 units/ms.
+        checkTestCase(
+            VelocityTrackingTestCase(
+                dataPoints = listOf(
+                    0 to 0f,
+                    10 to 0f,
+                    20 to 0f,
+                    30 to 0f,
+                    40 to 0f,
+                    50 to 0f,
+                    60 to 20f,
+                    70 to 40f,
+                    80 to 60f,
+                    90 to 80f,
+                    100 to 100f,
+                ),
+                expectedVelocity = 2000f
+            ),
+        )
+    }
+
+    @Test
+    fun linearHalfMotionSampled() {
+        // Linear half motion, but sampled much less frequently. The resulting velocity is higher
+        // than the previous test, because the path looks significantly different now if you
+        // were to just plot these points.
+        checkTestCase(
+            VelocityTrackingTestCase(
+                dataPoints = listOf(
+                    0 to 0f,
+                    30 to 0f,
+                    40 to 0f,
+                    70 to 40f,
+                    100 to 100f,
+                ),
+                expectedVelocity = 2018.2f
+            )
+        )
+    }
+
+    @Test
+    fun linearMotionFollowedByFlatLine() {
+        // Fixed velocity at first, but flat line afterwards.
+        checkTestCase(
+            VelocityTrackingTestCase(
+                dataPoints = listOf(
+                    0 to 0f,
+                    10 to 10f,
+                    20 to 20f,
+                    30 to 30f,
+                    40 to 40f,
+                    50 to 50f,
+                    60 to 50f,
+                    70 to 50f,
+                    80 to 50f,
+                    90 to 50f,
+                    100 to 50f,
+                ),
+                expectedVelocity = 1000f
+            )
+        )
+    }
+
+    @Test
+    fun linearMotionFollowedByFlatLineWithoutIntermediatePoints() {
+        // Fixed velocity at first, but flat line afterwards
+        checkTestCase(
+            VelocityTrackingTestCase(
+                dataPoints = listOf(
+                    0 to 0f,
+                    50 to 50f,
+                    100 to 50f,
+                ),
+                expectedVelocity = 0f
+            ),
+        )
+    }
+
+    @Test
+    fun swordfishFlingDown_xValues() {
+        checkTestCase(
+            VelocityTrackingTestCase(
+                dataPoints = listOf(
+                    0 to 271f,
+                    16 to 269.786346f,
+                    35 to 267.983063f,
+                    52 to 262.638397f,
+                    68 to 266.138824f,
+                    85 to 274.79245f,
+                    96 to 274.79245f,
+                ),
+                expectedVelocity = 623.57f
+            )
+        )
+    }
+
+    @Test
+    fun swordfishFlingDown_yValues() {
+        checkTestCase(
+            VelocityTrackingTestCase(
+                dataPoints = listOf(
+                    0 to 96f,
+                    16 to 106.922775f,
+                    35 to 156.660034f,
+                    52 to 220.339081f,
+                    68 to 331.581116f,
+                    85 to 428.113159f,
+                    96 to 428.113159f,
+                ),
+                expectedVelocity = 5970.73f
+            )
+        )
+    }
+
+    @Test
+    fun sailfishFlingUpSlow_xValues() {
+        checkTestCase(
+            VelocityTrackingTestCase(
+                dataPoints = listOf(
+                    235089067 to 528.0f,
+                    235089084 to 527.0f,
+                    235089093 to 527.0f,
+                    235089095 to 527.0f,
+                    235089101 to 527.0f,
+                    235089110 to 528.0f,
+                    235089112 to 528.25f,
+                    235089118 to 531.0f,
+                    235089126 to 535.0f,
+                    235089129 to 536.33f,
+                    235089135 to 540.0f,
+                    235089144 to 546.0f,
+                    235089146 to 547.21f,
+                    235089152 to 553.0f,
+                    235089160 to 559.0f,
+                    235089162 to 560.66f,
+                ),
+                expectedVelocity = 764.34f,
+            )
+        )
+    }
+
+    @Test
+    fun sailfishFlingUpSlow_yValues() {
+        checkTestCase(
+            VelocityTrackingTestCase(
+                dataPoints = listOf(
+                    235089067 to 983.0f,
+                    235089084 to 981.0f,
+                    235089093 to 977.0f,
+                    235089095 to 975.93f,
+                    235089101 to 970.0f,
+                    235089110 to 960.0f,
+                    235089112 to 957.51f,
+                    235089118 to 946.0f,
+                    235089126 to 931.0f,
+                    235089129 to 926.02f,
+                    235089135 to 914.0f,
+                    235089144 to 896.0f,
+                    235089146 to 892.36f,
+                    235089152 to 877.0f,
+                    235089160 to 851.0f,
+                    235089162 to 843.82f,
+                ),
+                expectedVelocity = -3604.82f,
+            )
+        )
+    }
+
+    @Test
+    fun sailfishFlingUpFast_xValues() {
+        // Some "repeated" data points are removed, since the conversion from ns to ms made some
+        // data ponits "repeated"
+        checkTestCase(
+            VelocityTrackingTestCase(
+                dataPoints = listOf(
+                    920922 to 561.0f,
+                    920930 to 559.0f,
+                    920938 to 559.0f,
+                    920947 to 562.91f,
+                    920955 to 577.0f,
+                    920963 to 596.87f,
+                    920972 to 631.0f,
+                    920980 to 671.31f,
+                    920989 to 715.0f,
+                ),
+                expectedVelocity = 5670.32f,
+            )
+        )
+    }
+
+    @Test
+    fun sailfishFlingUpFast_yValues() {
+        // Some "repeated" data points are removed, since the conversion from ns to ms made some
+        // data ponits "repeated"
+        checkTestCase(
+            VelocityTrackingTestCase(
+                dataPoints = listOf(
+                    920922 to 1412.0f,
+                    920930 to 1377.0f,
+                    920938 to 1371.0f,
+                    920947 to 1342.68f,
+                    920955 to 1272.0f,
+                    920963 to 1190.54f,
+                    920972 to 1093.0f,
+                    920980 to 994.68f,
+                    920989 to 903.0f,
+                ),
+                expectedVelocity = -13021.10f,
+            )
+        )
+    }
+
+    private fun checkTestCase(testCase: VelocityTrackingTestCase) {
+        val expectedVelocity = testCase.expectedVelocity
+        val tracker = VelocityTracker1D()
+        testCase.dataPoints.forEach {
+            tracker.addDataPoint(it.first.toLong(), it.second)
+        }
+
+        Truth.assertWithMessage(
+            "Wrong velocity for data points: ${testCase.dataPoints}" +
+                "\nExpected velocity: {$expectedVelocity}"
+        )
+            .that(tracker.calculateVelocity())
+            .isWithin(abs(expectedVelocity) * Tolerance)
+            .of(expectedVelocity)
+    }
+}
+
+/** Holds configs for a velocity tracking test case, for convenience. */
+private data class VelocityTrackingTestCase(
+    val dataPoints: List<Pair<Int, Float>>,
+    val expectedVelocity: Float
+)
diff --git a/transition/transition/src/main/java/androidx/transition/Transition.java b/transition/transition/src/main/java/androidx/transition/Transition.java
index c2c0986..9cdb387 100644
--- a/transition/transition/src/main/java/androidx/transition/Transition.java
+++ b/transition/transition/src/main/java/androidx/transition/Transition.java
@@ -22,7 +22,6 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.TypedArray;
@@ -55,6 +54,10 @@
 import androidx.core.content.res.TypedArrayUtils;
 import androidx.core.util.Consumer;
 import androidx.core.view.ViewCompat;
+import androidx.dynamicanimation.animation.DynamicAnimation;
+import androidx.dynamicanimation.animation.FloatValueHolder;
+import androidx.dynamicanimation.animation.SpringAnimation;
+import androidx.dynamicanimation.animation.SpringForce;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -1920,7 +1923,7 @@
             runAnimators();
         } else if (Build.VERSION.SDK_INT >= 34) {
             prepareAnimatorsForSeeking();
-            mSeekController.setCurrentPlayTimeMillis(0);
+            mSeekController.initPlayTime();
             mSeekController.ready();
         }
     }
@@ -2690,14 +2693,17 @@
      */
     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
     class SeekController extends TransitionListenerAdapter implements TransitionSeekController,
-            ValueAnimator.AnimatorUpdateListener {
+            DynamicAnimation.OnAnimationUpdateListener {
+        // Animation calculations appear to work better with numbers that range greater than 1
         private long mCurrentPlayTime = -1;
         private ArrayList<Consumer<TransitionSeekController>> mOnReadyListeners = null;
+        private ArrayList<Consumer<TransitionSeekController>> mOnProgressListeners = null;
         private boolean mIsReady;
         private boolean mIsCanceled;
 
-        private ValueAnimator mAnimator;
-        private boolean mIsAnimatingReversed;
+        private SpringAnimation mSpringAnimation;
+        private Consumer<TransitionSeekController>[] mListenerCache = null;
+        private final VelocityTracker1D mVelocityTracker = new VelocityTracker1D();
 
         @Override
         public long getDurationMillis() {
@@ -2710,6 +2716,11 @@
         }
 
         @Override
+        public float getCurrentFraction() {
+            return ((float) getCurrentPlayTimeMillis()) / ((float) getDurationMillis());
+        }
+
+        @Override
         public boolean isReady() {
             return mIsReady;
         }
@@ -2723,34 +2734,54 @@
                     onReadyListeners.get(i).accept(this);
                 }
             }
+            callProgressListeners();
         }
 
         @Override
         public void setCurrentPlayTimeMillis(long playTimeMillis) {
-            if (mAnimator != null) {
+            if (mSpringAnimation != null) {
                 throw new IllegalStateException("setCurrentPlayTimeMillis() called after animation "
                         + "has been started");
             }
-            if (playTimeMillis == mCurrentPlayTime) {
+            if (playTimeMillis == mCurrentPlayTime || !isReady()) {
                 return; // no change
             }
 
+            long targetPlayTime = playTimeMillis;
             if (!mIsCanceled) {
-                if (playTimeMillis == 0 && mCurrentPlayTime > 0) {
+                if (targetPlayTime == 0 && mCurrentPlayTime > 0) {
                     // Force the transition to end
-                    playTimeMillis = -1;
+                    targetPlayTime = -1;
                 } else {
                     long duration = getDurationMillis();
                     // Force the transition to the end
-                    if (playTimeMillis == duration && mCurrentPlayTime < duration) {
-                        playTimeMillis = duration + 1;
+                    if (targetPlayTime == duration && mCurrentPlayTime < duration) {
+                        targetPlayTime = duration + 1;
                     }
                 }
-                if (playTimeMillis != mCurrentPlayTime) {
-                    Transition.this.setCurrentPlayTimeMillis(playTimeMillis, mCurrentPlayTime);
-                    mCurrentPlayTime = playTimeMillis;
+                if (targetPlayTime != mCurrentPlayTime) {
+                    Transition.this.setCurrentPlayTimeMillis(targetPlayTime, mCurrentPlayTime);
+                    mCurrentPlayTime = targetPlayTime;
                 }
             }
+            callProgressListeners();
+            mVelocityTracker.addDataPoint(AnimationUtils.currentAnimationTimeMillis(),
+                    (float) targetPlayTime);
+        }
+
+        void initPlayTime() {
+            long playTime = (getDurationMillis() == 0) ? 1 : 0;
+            Transition.this.setCurrentPlayTimeMillis(playTime, mCurrentPlayTime);
+            mCurrentPlayTime = playTime;
+        }
+
+        @Override
+        public void setCurrentFraction(float fraction) {
+            if (mSpringAnimation != null) {
+                throw new IllegalStateException("setCurrentFraction() called after animation "
+                        + "has been started");
+            }
+            setCurrentPlayTimeMillis((long) (fraction * getDurationMillis()));
         }
 
         @Override
@@ -2785,55 +2816,85 @@
         }
 
         @Override
-        public void onAnimationUpdate(@NonNull ValueAnimator valueAnimator) {
-            long time = Math.max(-1,
-                    Math.min(getDurationMillis() + 1, mAnimator.getCurrentPlayTime())
-            );
-            if (mIsAnimatingReversed) {
-                time = getDurationMillis() - time;
-            }
+        public void onAnimationUpdate(DynamicAnimation animation, float value, float velocity) {
+            long time = Math.max(-1, Math.min(getDurationMillis() + 1, Math.round((double) value)));
             Transition.this.setCurrentPlayTimeMillis(time, mCurrentPlayTime);
             mCurrentPlayTime = time;
+            callProgressListeners();
         }
 
-        private void createAnimator() {
-            long duration = getDurationMillis() + 1;
-            mAnimator = ValueAnimator.ofInt((int) duration);
-            mAnimator.setInterpolator(null);
-            mAnimator.setDuration(duration);
-            mAnimator.addUpdateListener(this);
+        private void ensureAnimation() {
+            if (mSpringAnimation != null) {
+                return;
+            }
+            mVelocityTracker.addDataPoint(AnimationUtils.currentAnimationTimeMillis(),
+                    (float) mCurrentPlayTime);
+            mSpringAnimation = new SpringAnimation(new FloatValueHolder());
+            SpringForce springForce = new SpringForce();
+            springForce.setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY);
+            springForce.setStiffness(SpringForce.STIFFNESS_LOW);
+            mSpringAnimation.setSpring(springForce);
+            mSpringAnimation.setStartValue((float) mCurrentPlayTime);
+            mSpringAnimation.addUpdateListener(this);
+            mSpringAnimation.setStartVelocity(mVelocityTracker.calculateVelocity());
+            mSpringAnimation.setMaxValue((float) (getDurationMillis() + 1));
+            mSpringAnimation.setMinValue(-1f);
+            mSpringAnimation.setMinimumVisibleChange(4f); // 4 milliseconds ~ 1/2 frame @ 120Hz
+            mSpringAnimation.addEndListener((anim, canceled, value, velocity) -> {
+                if (!canceled) {
+                    boolean isReversed = value < 1f;
+                    notifyListeners(TransitionNotification.ON_END, isReversed);
+                }
+                mSpringAnimation = null;
+            });
         }
 
         @Override
         public void animateToEnd() {
-            if (mAnimator != null) {
-                mAnimator.cancel();
-            }
-            final long duration = getDurationMillis();
-            if (mCurrentPlayTime > duration) {
-                return; // we're already at the end
-            }
-            createAnimator();
-            mIsAnimatingReversed = false;
-            mAnimator.setCurrentPlayTime(mCurrentPlayTime);
-            mAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    notifyListeners(TransitionNotification.ON_END, false);
-                }
-            });
-            mAnimator.start();
+            ensureAnimation();
+            mSpringAnimation.animateToFinalPosition((float) (getDurationMillis() + 1));
         }
 
         @Override
         public void animateToStart() {
-            if (mAnimator != null) {
-                mAnimator.cancel();
+            ensureAnimation();
+            mSpringAnimation.animateToFinalPosition(-1);
+        }
+
+        @Override
+        public void addOnProgressChangedListener(
+                @NonNull Consumer<TransitionSeekController> consumer) {
+            if (mOnProgressListeners == null) {
+                mOnProgressListeners = new ArrayList<>();
             }
-            createAnimator();
-            mAnimator.setCurrentPlayTime(getDurationMillis() - mCurrentPlayTime);
-            mIsAnimatingReversed = true;
-            mAnimator.start();
+            mOnProgressListeners.add(consumer);
+        }
+
+        @Override
+        public void removeOnProgressChangedListener(
+                @NonNull Consumer<TransitionSeekController> consumer) {
+            if (mOnProgressListeners != null) {
+                mOnProgressListeners.remove(consumer);
+            }
+        }
+
+        @SuppressWarnings("unchecked")
+        private void callProgressListeners() {
+            if (mOnProgressListeners == null || mOnProgressListeners.isEmpty()) {
+                return;
+            }
+            int size = mOnProgressListeners.size();
+            if (mListenerCache == null) {
+                mListenerCache = new Consumer[size];
+            }
+            Consumer<TransitionSeekController>[] cache =
+                    mOnProgressListeners.toArray(mListenerCache);
+            mListenerCache = null;
+            for (int i = 0; i < size; i++) {
+                cache[i].accept(this);
+                cache[i] = null;
+            }
+            mListenerCache = cache;
         }
     }
 }
diff --git a/transition/transition/src/main/java/androidx/transition/TransitionManager.java b/transition/transition/src/main/java/androidx/transition/TransitionManager.java
index 62ee14c..261738f4 100644
--- a/transition/transition/src/main/java/androidx/transition/TransitionManager.java
+++ b/transition/transition/src/main/java/androidx/transition/TransitionManager.java
@@ -350,6 +350,59 @@
     }
 
     /**
+     * Convenience method to seek to the given scene using the given transition. If seeking
+     * is not supported because the device is {@link Build.VERSION_CODES.TIRAMISU} or earlier,
+     * the scene transition is immediate and {@code null} is returned.
+     *
+     * @param scene      The Scene to change to
+     * @param transition The transition to use for this scene change.
+     * @return a {@link TransitionSeekController} that can be used control the animation to the
+     * destination scene. {@code null} is returned when seeking is not supported on the scene,
+     * either because it is running on {@link android.os.Build.VERSION_CODES.TIRAMISU} or earlier,
+     * another Transition is being captured for {@code sceneRoot}, or {@code sceneRoot} hasn't
+     * had a layout yet.
+     * @throws IllegalArgumentException if {@code transition} returns {@code false} from
+     * {@link Transition#isSeekingSupported()}.
+     */
+    @Nullable
+    public static TransitionSeekController createSeekController(
+            @NonNull Scene scene,
+            @NonNull Transition transition
+    ) {
+        final ViewGroup sceneRoot = scene.getSceneRoot();
+
+        if (!transition.isSeekingSupported()) {
+            throw new IllegalArgumentException("The Transition must support seeking.");
+        }
+        if (sPendingTransitions.contains(sceneRoot)) {
+            return null; // Already in the process of transitioning
+        }
+        Scene oldScene = Scene.getCurrentScene(sceneRoot);
+        if (!ViewCompat.isLaidOut(sceneRoot)
+                || Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE
+        ) {
+            // Can't control it, so just change the scene immediately
+            if (oldScene != null) {
+                oldScene.exit();
+            }
+            scene.enter();
+            return null;
+        }
+        sPendingTransitions.add(sceneRoot);
+        final Transition transitionClone = transition.clone();
+        final TransitionSet set = new TransitionSet();
+        set.addTransition(transitionClone);
+        if (oldScene != null && oldScene.isCreatedFromLayoutResource()) {
+            set.setCanRemoveViews(true);
+        }
+        sceneChangeSetup(sceneRoot, set);
+        scene.enter();
+
+        sceneChangeRunTransition(sceneRoot, set);
+        return set.createSeekController();
+    }
+
+    /**
      * Convenience method to simply change to the given scene using
      * the given transition.
      *
diff --git a/transition/transition/src/main/java/androidx/transition/TransitionSeekController.java b/transition/transition/src/main/java/androidx/transition/TransitionSeekController.java
index bcede11..9e4f228 100644
--- a/transition/transition/src/main/java/androidx/transition/TransitionSeekController.java
+++ b/transition/transition/src/main/java/androidx/transition/TransitionSeekController.java
@@ -18,6 +18,8 @@
 
 import android.view.ViewGroup;
 
+import androidx.annotation.FloatRange;
+import androidx.annotation.IntRange;
 import androidx.annotation.NonNull;
 import androidx.core.util.Consumer;
 
@@ -31,15 +33,24 @@
     /**
      * @return The total duration, in milliseconds, of the Transition's animations.
      */
+    @IntRange(from = 0)
     long getDurationMillis();
 
     /**
      * @return The time, in milliseconds, of the animation. This will be between 0
      * and {@link #getDurationMillis()}.
      */
+    @IntRange(from = 0)
     long getCurrentPlayTimeMillis();
 
     /**
+     * @return The fraction, between 0 and 1, inclusive, of the progress of the transition.
+     * @see #getCurrentPlayTimeMillis()
+     */
+    @FloatRange(from = 0.0, to = 1.0)
+    float getCurrentFraction();
+
+    /**
      * Returns {@code true} when the Transition is ready to seek or {@code false}
      * when the Transition's animations have yet to be built.
      */
@@ -73,14 +84,26 @@
     void animateToEnd();
 
     /**
+     * Sets the position of the Transition's animation. {@code fraction} should be
+     * between 0 and 1, inclusive, where 0 indicates that the transition hasn't progressed and 1
+     * indicates that the transition is completed. Calling this before {@link #isReady()} is
+     * {@code true} will do nothing.
+     *
+     * @param fraction The fraction, between 0 and 1, inclusive, of the progress of the transition.
+     * @see #setCurrentPlayTimeMillis(long)
+     */
+    void setCurrentFraction(@FloatRange(from = 0.0, to = 1.0) float fraction);
+
+    /**
      * Sets the position of the Transition's animation. {@code playTimeMillis} should be
-     * between 0 and {@link #getDurationMillis()}. This should not be called when
-     * {@link #isReady()} is {@code false}.
+     * between 0 and {@link #getDurationMillis()}. Calling this before {@link #isReady()} is
+     * {@code true} will do nothing.
      *
      * @param playTimeMillis The time, between 0 and {@link #getDurationMillis()} that the
      *                       animation should play.
+     * @see #setCurrentFraction(float)
      */
-    void setCurrentPlayTimeMillis(long playTimeMillis);
+    void setCurrentPlayTimeMillis(@IntRange(from = 0) long playTimeMillis);
 
     /**
      * Adds a listener to know when {@link #isReady()} is {@code true}. The listener will
@@ -98,5 +121,20 @@
      * @param onReadyListener The listener to be removed so that it won't be notified when ready.
      */
     void removeOnReadyListener(@NonNull Consumer<TransitionSeekController> onReadyListener);
+
+    /**
+     * Add a listener for whenever the progress of the transition is changed. This will be called
+     * when {@link #setCurrentPlayTimeMillis(long)} or {@link #setCurrentFraction(float)} are
+     * called as well as when the animation from {@link #animateToEnd()} or
+     * {@link #animateToStart()} changes the progress.
+     * @param consumer A method that accepts this TransitionSeekController.
+     */
+    void addOnProgressChangedListener(@NonNull Consumer<TransitionSeekController> consumer);
+
+    /**
+     * Remove a listener previously added in {@link #addOnProgressChangedListener(Consumer)}
+     * @param consumer The listener to be removed.
+     */
+    void removeOnProgressChangedListener(@NonNull Consumer<TransitionSeekController> consumer);
 }
 
diff --git a/transition/transition/src/main/java/androidx/transition/VelocityTracker1D.java b/transition/transition/src/main/java/androidx/transition/VelocityTracker1D.java
new file mode 100644
index 0000000..e4e7ccb
--- /dev/null
+++ b/transition/transition/src/main/java/androidx/transition/VelocityTracker1D.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.transition;
+
+import java.util.Arrays;
+
+/**
+ * Velocity Tracker, simplified from compose's VelocityTracker1D.
+ */
+class VelocityTracker1D {
+    private static final int HISTORY_SIZE = 20;
+    private static final int ASSUME_POINTER_MOVE_STOPPED_MILLIS = 40;
+    private static final int HORIZON_MILLIS = 100;
+
+    // Circular buffer; current sample at index.
+    private long[] mTimeSamples = new long[HISTORY_SIZE];
+    private float[] mDataSamples = new float[HISTORY_SIZE];
+    private int mIndex = 0;
+
+    VelocityTracker1D() {
+        Arrays.fill(mTimeSamples, Long.MIN_VALUE);
+    }
+
+    /**
+     * Adds a data point for velocity calculation at a given time, {@code timeMillis}. The data
+     * point represents an absolute position.
+     * <p>
+     * Use the same units for the data points provided. For example, having some data points in `cm`
+     * and some in `m` will result in incorrect velocity calculations, as this method (and the
+     * tracker) has no knowledge of the units used.
+     */
+    public void addDataPoint(long timeMillis, float data) {
+        mIndex = (mIndex + 1) % HISTORY_SIZE;
+        mTimeSamples[mIndex] = timeMillis;
+        mDataSamples[mIndex] = data;
+    }
+
+    public void resetTracking() {
+        mIndex = 0;
+        Arrays.fill(mTimeSamples, Long.MIN_VALUE);
+        Arrays.fill(mDataSamples, 0f);
+    }
+
+    /**
+     * Computes the estimated velocity at the time of the last provided data point. The units of
+     * velocity will be `units/second`, where `units` is the units of the data points provided via
+     * [addDataPoint].
+     *
+     * This can be expensive. Only call this when you need the velocity.
+     */
+    float calculateVelocity() {
+        int sampleCount = 0;
+        int index = mIndex;
+
+        if (index == 0 && mTimeSamples[index] == Long.MIN_VALUE) {
+            return 0f; // We haven't received any data
+        }
+
+        // The sample at index is our newest sample.  If it is null, we have no samples so return.
+        long newestTime = mTimeSamples[index];
+
+        long previousTime = newestTime;
+
+        // Starting with the most recent sample, iterate backwards while
+        // the samples represent continuous motion.
+        do {
+            long sampleTime = mTimeSamples[index];
+            if (sampleTime == Long.MIN_VALUE) {
+                break; // no point here
+            }
+            float age = newestTime - sampleTime;
+            float delta = Math.abs(sampleTime - previousTime);
+            previousTime = sampleTime;
+
+            if (age > HORIZON_MILLIS || delta > ASSUME_POINTER_MOVE_STOPPED_MILLIS) {
+                break;
+            }
+
+            index = (index == 0 ? HISTORY_SIZE : index) - 1;
+            sampleCount++;
+        } while (sampleCount < HISTORY_SIZE);
+
+        if (sampleCount < 2) {
+            return 0f; // Not enough data to have a velocity
+        }
+
+        if (sampleCount == 2) {
+            // Simple diff in time
+            int prevIndex = mIndex == 0 ? HISTORY_SIZE - 1 : mIndex - 1;
+            float timeDiff = mTimeSamples[mIndex] - mTimeSamples[prevIndex];
+            if (timeDiff == 0f) {
+                return 0f;
+            }
+            float dataDiff = mDataSamples[mIndex] - mDataSamples[prevIndex];
+            return dataDiff / timeDiff * 1000;
+        }
+
+        float work = 0f;
+        int startIndex = (mIndex - sampleCount + HISTORY_SIZE + 1) % HISTORY_SIZE;
+        int endIndex = (mIndex + 1 + HISTORY_SIZE) % HISTORY_SIZE;
+        previousTime = mTimeSamples[startIndex];
+        float previousData = mDataSamples[startIndex];
+        for (int i = (startIndex + 1) % HISTORY_SIZE; i != endIndex; i = (i + 1) % HISTORY_SIZE) {
+            long time = mTimeSamples[i];
+            long timeDelta = time - previousTime;
+            if (timeDelta == 0f) {
+                continue;
+            }
+            float data = mDataSamples[i];
+            float vPrev = kineticEnergyToVelocity(work);
+            float dataPointsDelta = data - previousData;
+
+            float vCurr = dataPointsDelta / timeDelta;
+            work += (vCurr - vPrev) * Math.abs(vCurr);
+            if (i == startIndex + 1) {
+                work = (work * 0.5f);
+            }
+            previousTime = time;
+            previousData = data;
+        }
+        return kineticEnergyToVelocity(work) * 1000;
+    }
+
+    private float kineticEnergyToVelocity(float kineticEnergy) {
+        return (float) (Math.signum(kineticEnergy) * Math.sqrt(2 * Math.abs(kineticEnergy)));
+    }
+}
diff --git a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/TopNavigation.kt b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/TopNavigation.kt
index 6e69a24..d4b97c2 100644
--- a/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/TopNavigation.kt
+++ b/tv/integration-tests/playground/src/main/java/androidx/tv/integration/playground/TopNavigation.kt
@@ -127,9 +127,10 @@
     TabRow(
         selectedTabIndex = selectedTabIndex,
         separator = { Spacer(modifier = Modifier.width(12.dp)) },
-        indicator = { tabPositions ->
+        indicator = { tabPositions, isActivated ->
             TabRowDefaults.UnderlinedIndicator(
-                currentTabPosition = tabPositions[selectedTabIndex]
+                currentTabPosition = tabPositions[selectedTabIndex],
+                isActivated = isActivated,
             )
         },
         modifier = Modifier
diff --git a/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/AppButton.kt b/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/AppButton.kt
index 9857021..a42fa91 100644
--- a/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/AppButton.kt
+++ b/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/AppButton.kt
@@ -37,8 +37,10 @@
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
+import androidx.tv.material3.ExperimentalTvMaterial3Api
 import androidx.tv.material3.Text
 
+@OptIn(ExperimentalTvMaterial3Api::class)
 @Composable
 fun AppButton(
     text: String,
diff --git a/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/AppLazyRow.kt b/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/AppLazyRow.kt
index 902258f..26e58f6 100644
--- a/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/AppLazyRow.kt
+++ b/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/AppLazyRow.kt
@@ -31,8 +31,10 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import androidx.tv.foundation.lazy.list.TvLazyRow
+import androidx.tv.material3.ExperimentalTvMaterial3Api
 import androidx.tv.material3.Text
 
+@OptIn(ExperimentalTvMaterial3Api::class)
 @Composable
 fun AppLazyRow(
     title: String,
diff --git a/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/AppTabRow.kt b/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/AppTabRow.kt
index a571a11c..ea59506 100644
--- a/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/AppTabRow.kt
+++ b/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/AppTabRow.kt
@@ -63,7 +63,7 @@
                         selected = selectedTabIndex == index,
                         onFocus = { onSelectedTabIndexChange(index) },
                         colors = TabDefaults.pillIndicatorTabColors(
-                            contentColor = LocalContentColor.current,
+                            inactiveContentColor = LocalContentColor.current,
 //                            selectedContentColor = Color(0xFF313033),
                         ),
                         modifier = Modifier
diff --git a/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/FeaturedCarousel.kt b/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/FeaturedCarousel.kt
index 94df400..e5fa951 100644
--- a/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/FeaturedCarousel.kt
+++ b/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/FeaturedCarousel.kt
@@ -92,7 +92,7 @@
     }
 }
 
-@OptIn(ExperimentalAnimationApi::class)
+@OptIn(ExperimentalAnimationApi::class, ExperimentalTvMaterial3Api::class)
 @Composable
 private fun AnimatedContentScope.CarouselSlide(
     title: String,
diff --git a/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/ImageCard.kt b/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/ImageCard.kt
index 450a9a0..1e97c370 100644
--- a/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/ImageCard.kt
+++ b/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/ImageCard.kt
@@ -41,8 +41,10 @@
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
+import androidx.tv.material3.ExperimentalTvMaterial3Api
 import androidx.tv.material3.Text
 
+@OptIn(ExperimentalTvMaterial3Api::class)
 @Composable
 fun ImageCard(
     movie: Movie,
diff --git a/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/ShowsGrid.kt b/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/ShowsGrid.kt
index eecb500..38bef2b 100644
--- a/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/ShowsGrid.kt
+++ b/tv/integration-tests/presentation/src/main/java/androidx/tv/integration/presentation/ShowsGrid.kt
@@ -37,8 +37,10 @@
 import androidx.compose.ui.unit.dp
 import androidx.tv.foundation.lazy.grid.TvGridCells
 import androidx.tv.foundation.lazy.grid.TvLazyHorizontalGrid
+import androidx.tv.material3.ExperimentalTvMaterial3Api
 import androidx.tv.material3.Text
 
+@OptIn(ExperimentalTvMaterial3Api::class)
 @Composable
 fun ShowsGrid(modifier: Modifier = Modifier) {
     var keyword by remember { mutableStateOf("") }
diff --git a/tv/samples/src/main/java/androidx/tv/samples/TabRowSamples.kt b/tv/samples/src/main/java/androidx/tv/samples/TabRowSamples.kt
index 96c9535..bf47504 100644
--- a/tv/samples/src/main/java/androidx/tv/samples/TabRowSamples.kt
+++ b/tv/samples/src/main/java/androidx/tv/samples/TabRowSamples.kt
@@ -90,9 +90,10 @@
   TabRow(
     selectedTabIndex = selectedTabIndex,
     separator = { Spacer(modifier = Modifier.width(12.dp)) },
-    indicator = { tabPositions ->
+    indicator = { tabPositions, isActivated ->
       TabRowDefaults.UnderlinedIndicator(
-        currentTabPosition = tabPositions[selectedTabIndex]
+        currentTabPosition = tabPositions[selectedTabIndex],
+        isActivated = isActivated,
       )
     },
     modifier = Modifier.focusRestorer()
@@ -180,17 +181,19 @@
   ) {
     TabRow(
       selectedTabIndex = focusedTabIndex,
-      indicator = { tabPositions ->
+      indicator = { tabPositions, isActivated ->
         // FocusedTab's indicator
         TabRowDefaults.PillIndicator(
           currentTabPosition = tabPositions[focusedTabIndex],
           activeColor = Color.Blue.copy(alpha = 0.4f),
           inactiveColor = Color.Transparent,
+          isActivated = isActivated,
         )
 
         // SelectedTab's indicator
         TabRowDefaults.PillIndicator(
-          currentTabPosition = tabPositions[activeTabIndex]
+          currentTabPosition = tabPositions[activeTabIndex],
+          isActivated = isActivated,
         )
       },
       modifier = Modifier.focusRestorer()
diff --git a/tv/tv-material/api/current.txt b/tv/tv-material/api/current.txt
index ddd94d5..1c964b9 100644
--- a/tv/tv-material/api/current.txt
+++ b/tv/tv-material/api/current.txt
@@ -134,7 +134,7 @@
   }
 
   @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public final class CarouselDefaults {
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public void IndicatorRow(int itemCount, int activeItemIndex, optional androidx.compose.ui.Modifier modifier, optional float spacing, optional kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> indicator);
+    method @androidx.compose.runtime.Composable public void IndicatorRow(int itemCount, int activeItemIndex, optional androidx.compose.ui.Modifier modifier, optional float spacing, optional kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> indicator);
     method @androidx.compose.runtime.Composable public androidx.compose.animation.ContentTransform getContentTransform();
     property @androidx.compose.runtime.Composable public final androidx.compose.animation.ContentTransform contentTransform;
     field public static final androidx.tv.material3.CarouselDefaults INSTANCE;
@@ -295,8 +295,8 @@
   }
 
   public final class ContentColorKt {
-    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> getLocalContentColor();
-    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> LocalContentColor;
+    method @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> getLocalContentColor();
+    property @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> LocalContentColor;
   }
 
   @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public final class DrawerState {
@@ -434,11 +434,13 @@
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.ListItemBorder border(optional androidx.tv.material3.Border border, optional androidx.tv.material3.Border focusedBorder, optional androidx.tv.material3.Border pressedBorder, optional androidx.tv.material3.Border selectedBorder, optional androidx.tv.material3.Border disabledBorder, optional androidx.tv.material3.Border focusedSelectedBorder, optional androidx.tv.material3.Border focusedDisabledBorder, optional androidx.tv.material3.Border pressedSelectedBorder);
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.ListItemColors colors(optional long containerColor, optional long contentColor, optional long focusedContainerColor, optional long focusedContentColor, optional long pressedContainerColor, optional long pressedContentColor, optional long selectedContainerColor, optional long selectedContentColor, optional long disabledContainerColor, optional long disabledContentColor, optional long focusedSelectedContainerColor, optional long focusedSelectedContentColor, optional long pressedSelectedContainerColor, optional long pressedSelectedContentColor);
     method public float getIconSize();
+    method public float getIconSizeDense();
     method public float getListItemElevation();
     method public androidx.tv.material3.ListItemGlow glow(optional androidx.tv.material3.Glow glow, optional androidx.tv.material3.Glow focusedGlow, optional androidx.tv.material3.Glow pressedGlow, optional androidx.tv.material3.Glow selectedGlow, optional androidx.tv.material3.Glow focusedSelectedGlow, optional androidx.tv.material3.Glow pressedSelectedGlow);
     method public androidx.tv.material3.ListItemScale scale(optional @FloatRange(from=0.0) float scale, optional @FloatRange(from=0.0) float focusedScale, optional @FloatRange(from=0.0) float pressedScale, optional @FloatRange(from=0.0) float selectedScale, optional @FloatRange(from=0.0) float disabledScale, optional @FloatRange(from=0.0) float focusedSelectedScale, optional @FloatRange(from=0.0) float focusedDisabledScale, optional @FloatRange(from=0.0) float pressedSelectedScale);
     method public androidx.tv.material3.ListItemShape shape(optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.ui.graphics.Shape focusedShape, optional androidx.compose.ui.graphics.Shape pressedShape, optional androidx.compose.ui.graphics.Shape selectedShape, optional androidx.compose.ui.graphics.Shape disabledShape, optional androidx.compose.ui.graphics.Shape focusedSelectedShape, optional androidx.compose.ui.graphics.Shape focusedDisabledShape, optional androidx.compose.ui.graphics.Shape pressedSelectedShape);
     property public final float IconSize;
+    property public final float IconSizeDense;
     property public final float ListItemElevation;
     field public static final androidx.tv.material3.ListItemDefaults INSTANCE;
   }
@@ -447,6 +449,7 @@
   }
 
   public final class ListItemKt {
+    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void DenseListItem(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> headlineContent, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional kotlin.jvm.functions.Function0<kotlin.Unit>? overlineContent, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? leadingContent, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingContent, optional float tonalElevation, optional androidx.tv.material3.ListItemShape shape, optional androidx.tv.material3.ListItemColors colors, optional androidx.tv.material3.ListItemScale scale, optional androidx.tv.material3.ListItemBorder border, optional androidx.tv.material3.ListItemGlow glow, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
     method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void ListItem(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> headlineContent, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional kotlin.jvm.functions.Function0<kotlin.Unit>? overlineContent, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? leadingContent, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingContent, optional float tonalElevation, optional androidx.tv.material3.ListItemShape shape, optional androidx.tv.material3.ListItemColors colors, optional androidx.tv.material3.ListItemScale scale, optional androidx.tv.material3.ListItemBorder border, optional androidx.tv.material3.ListItemGlow glow, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
   }
 
@@ -462,11 +465,11 @@
   @SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class ListItemShape {
   }
 
-  public final class MaterialTheme {
+  @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public final class MaterialTheme {
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.ColorScheme getColorScheme();
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.Shapes getShapes();
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.Typography getTypography();
-    property @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable @androidx.tv.material3.ExperimentalTvMaterial3Api public final androidx.tv.material3.ColorScheme colorScheme;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.tv.material3.ColorScheme colorScheme;
     property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.tv.material3.Shapes shapes;
     property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.tv.material3.Typography typography;
     field public static final androidx.tv.material3.MaterialTheme INSTANCE;
@@ -567,7 +570,7 @@
   @SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class SelectableChipShape {
   }
 
-  public final class ShapeDefaults {
+  @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public final class ShapeDefaults {
     method public androidx.compose.foundation.shape.CornerBasedShape getExtraLarge();
     method public androidx.compose.foundation.shape.CornerBasedShape getExtraSmall();
     method public androidx.compose.foundation.shape.CornerBasedShape getLarge();
@@ -581,7 +584,7 @@
     field public static final androidx.tv.material3.ShapeDefaults INSTANCE;
   }
 
-  @androidx.compose.runtime.Immutable public final class Shapes {
+  @SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api 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.tv.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);
     method public androidx.compose.foundation.shape.CornerBasedShape getExtraLarge();
@@ -613,8 +616,8 @@
     method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Surface(optional androidx.compose.ui.Modifier modifier, optional float tonalElevation, optional androidx.compose.ui.graphics.Shape shape, optional androidx.tv.material3.NonInteractiveSurfaceColors colors, optional androidx.tv.material3.Border border, optional androidx.tv.material3.Glow glow, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
     method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Surface(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional float tonalElevation, optional androidx.tv.material3.ToggleableSurfaceShape shape, optional androidx.tv.material3.ToggleableSurfaceColors colors, optional androidx.tv.material3.ToggleableSurfaceScale scale, optional androidx.tv.material3.ToggleableSurfaceBorder border, optional androidx.tv.material3.ToggleableSurfaceGlow glow, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
     method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Surface(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional boolean enabled, optional float tonalElevation, optional androidx.tv.material3.ClickableSurfaceShape shape, optional androidx.tv.material3.ClickableSurfaceColors colors, optional androidx.tv.material3.ClickableSurfaceScale scale, optional androidx.tv.material3.ClickableSurfaceBorder border, optional androidx.tv.material3.ClickableSurfaceGlow glow, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
-    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Dp> getLocalAbsoluteTonalElevation();
-    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Dp> LocalAbsoluteTonalElevation;
+    method @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Dp> getLocalAbsoluteTonalElevation();
+    property @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Dp> LocalAbsoluteTonalElevation;
   }
 
   @SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class SwitchColors {
@@ -635,19 +638,19 @@
   }
 
   @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public final class TabDefaults {
-    method @androidx.compose.runtime.Composable public androidx.tv.material3.TabColors pillIndicatorTabColors(optional long activeContentColor, optional long contentColor, optional long selectedContentColor, optional long focusedContentColor, optional long focusedSelectedContentColor, optional long disabledActiveContentColor, optional long disabledContentColor, optional long disabledSelectedContentColor);
-    method @androidx.compose.runtime.Composable public androidx.tv.material3.TabColors underlinedIndicatorTabColors(optional long activeContentColor, optional long contentColor, optional long selectedContentColor, optional long focusedContentColor, optional long focusedSelectedContentColor, optional long disabledActiveContentColor, optional long disabledContentColor, optional long disabledSelectedContentColor);
+    method @androidx.compose.runtime.Composable public androidx.tv.material3.TabColors pillIndicatorTabColors(optional long contentColor, optional long inactiveContentColor, optional long selectedContentColor, optional long focusedContentColor, optional long focusedSelectedContentColor, optional long disabledContentColor, optional long disabledInactiveContentColor, optional long disabledSelectedContentColor);
+    method @androidx.compose.runtime.Composable public androidx.tv.material3.TabColors underlinedIndicatorTabColors(optional long contentColor, optional long inactiveContentColor, optional long selectedContentColor, optional long focusedContentColor, optional long focusedSelectedContentColor, optional long disabledContentColor, optional long disabledInactiveContentColor, optional long disabledSelectedContentColor);
     field public static final androidx.tv.material3.TabDefaults INSTANCE;
   }
 
   public final class TabKt {
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Tab(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onFocus, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional boolean enabled, optional androidx.tv.material3.TabColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Tab(androidx.tv.material3.TabRowScope, boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onFocus, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional boolean enabled, optional androidx.tv.material3.TabColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
   }
 
   @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public final class TabRowDefaults {
-    method @androidx.compose.runtime.Composable public void PillIndicator(androidx.compose.ui.unit.DpRect currentTabPosition, optional androidx.compose.ui.Modifier modifier, optional long activeColor, optional long inactiveColor);
+    method @androidx.compose.runtime.Composable public void PillIndicator(androidx.compose.ui.unit.DpRect currentTabPosition, boolean isActivated, optional androidx.compose.ui.Modifier modifier, optional long activeColor, optional long inactiveColor);
     method @androidx.compose.runtime.Composable public void TabSeparator();
-    method @androidx.compose.runtime.Composable public void UnderlinedIndicator(androidx.compose.ui.unit.DpRect currentTabPosition, optional androidx.compose.ui.Modifier modifier, optional long activeColor, optional long inactiveColor);
+    method @androidx.compose.runtime.Composable public void UnderlinedIndicator(androidx.compose.ui.unit.DpRect currentTabPosition, boolean isActivated, optional androidx.compose.ui.Modifier modifier, optional long activeColor, optional long inactiveColor);
     method @androidx.compose.runtime.Composable public long contentColor();
     method public long getContainerColor();
     property public final long ContainerColor;
@@ -655,15 +658,20 @@
   }
 
   public final class TabRowKt {
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void TabRow(int selectedTabIndex, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional kotlin.jvm.functions.Function0<kotlin.Unit> separator, optional kotlin.jvm.functions.Function1<? super java.util.List<androidx.compose.ui.unit.DpRect>,kotlin.Unit> indicator, kotlin.jvm.functions.Function0<kotlin.Unit> tabs);
+    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void TabRow(int selectedTabIndex, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional kotlin.jvm.functions.Function0<kotlin.Unit> separator, optional kotlin.jvm.functions.Function2<? super java.util.List<androidx.compose.ui.unit.DpRect>,? super java.lang.Boolean,kotlin.Unit> indicator, kotlin.jvm.functions.Function1<? super androidx.tv.material3.TabRowScope,kotlin.Unit> tabs);
+  }
+
+  @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public interface TabRowScope {
+    method public boolean isActivated();
+    property public abstract boolean isActivated;
   }
 
   public final class TextKt {
-    method @androidx.compose.runtime.Composable public static void ProvideTextStyle(androidx.compose.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
-    method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
-    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> getLocalTextStyle();
-    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> LocalTextStyle;
+    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void ProvideTextStyle(androidx.compose.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+    method @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> getLocalTextStyle();
+    property @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> LocalTextStyle;
   }
 
   @SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class ToggleableSurfaceBorder {
@@ -696,7 +704,7 @@
   @SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class ToggleableSurfaceShape {
   }
 
-  @androidx.compose.runtime.Immutable public final class Typography {
+  @SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class Typography {
     ctor public Typography(optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle headlineLarge, optional androidx.compose.ui.text.TextStyle headlineMedium, optional androidx.compose.ui.text.TextStyle headlineSmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle labelLarge, optional androidx.compose.ui.text.TextStyle labelMedium, optional androidx.compose.ui.text.TextStyle labelSmall);
     method public androidx.tv.material3.Typography copy(optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle headlineLarge, optional androidx.compose.ui.text.TextStyle headlineMedium, optional androidx.compose.ui.text.TextStyle headlineSmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle labelLarge, optional androidx.compose.ui.text.TextStyle labelMedium, optional androidx.compose.ui.text.TextStyle labelSmall);
     method public androidx.compose.ui.text.TextStyle getBodyLarge();
diff --git a/tv/tv-material/api/restricted_current.txt b/tv/tv-material/api/restricted_current.txt
index ddd94d5..1c964b9 100644
--- a/tv/tv-material/api/restricted_current.txt
+++ b/tv/tv-material/api/restricted_current.txt
@@ -134,7 +134,7 @@
   }
 
   @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public final class CarouselDefaults {
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public void IndicatorRow(int itemCount, int activeItemIndex, optional androidx.compose.ui.Modifier modifier, optional float spacing, optional kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> indicator);
+    method @androidx.compose.runtime.Composable public void IndicatorRow(int itemCount, int activeItemIndex, optional androidx.compose.ui.Modifier modifier, optional float spacing, optional kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> indicator);
     method @androidx.compose.runtime.Composable public androidx.compose.animation.ContentTransform getContentTransform();
     property @androidx.compose.runtime.Composable public final androidx.compose.animation.ContentTransform contentTransform;
     field public static final androidx.tv.material3.CarouselDefaults INSTANCE;
@@ -295,8 +295,8 @@
   }
 
   public final class ContentColorKt {
-    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> getLocalContentColor();
-    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> LocalContentColor;
+    method @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> getLocalContentColor();
+    property @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> LocalContentColor;
   }
 
   @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public final class DrawerState {
@@ -434,11 +434,13 @@
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.ListItemBorder border(optional androidx.tv.material3.Border border, optional androidx.tv.material3.Border focusedBorder, optional androidx.tv.material3.Border pressedBorder, optional androidx.tv.material3.Border selectedBorder, optional androidx.tv.material3.Border disabledBorder, optional androidx.tv.material3.Border focusedSelectedBorder, optional androidx.tv.material3.Border focusedDisabledBorder, optional androidx.tv.material3.Border pressedSelectedBorder);
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.ListItemColors colors(optional long containerColor, optional long contentColor, optional long focusedContainerColor, optional long focusedContentColor, optional long pressedContainerColor, optional long pressedContentColor, optional long selectedContainerColor, optional long selectedContentColor, optional long disabledContainerColor, optional long disabledContentColor, optional long focusedSelectedContainerColor, optional long focusedSelectedContentColor, optional long pressedSelectedContainerColor, optional long pressedSelectedContentColor);
     method public float getIconSize();
+    method public float getIconSizeDense();
     method public float getListItemElevation();
     method public androidx.tv.material3.ListItemGlow glow(optional androidx.tv.material3.Glow glow, optional androidx.tv.material3.Glow focusedGlow, optional androidx.tv.material3.Glow pressedGlow, optional androidx.tv.material3.Glow selectedGlow, optional androidx.tv.material3.Glow focusedSelectedGlow, optional androidx.tv.material3.Glow pressedSelectedGlow);
     method public androidx.tv.material3.ListItemScale scale(optional @FloatRange(from=0.0) float scale, optional @FloatRange(from=0.0) float focusedScale, optional @FloatRange(from=0.0) float pressedScale, optional @FloatRange(from=0.0) float selectedScale, optional @FloatRange(from=0.0) float disabledScale, optional @FloatRange(from=0.0) float focusedSelectedScale, optional @FloatRange(from=0.0) float focusedDisabledScale, optional @FloatRange(from=0.0) float pressedSelectedScale);
     method public androidx.tv.material3.ListItemShape shape(optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.ui.graphics.Shape focusedShape, optional androidx.compose.ui.graphics.Shape pressedShape, optional androidx.compose.ui.graphics.Shape selectedShape, optional androidx.compose.ui.graphics.Shape disabledShape, optional androidx.compose.ui.graphics.Shape focusedSelectedShape, optional androidx.compose.ui.graphics.Shape focusedDisabledShape, optional androidx.compose.ui.graphics.Shape pressedSelectedShape);
     property public final float IconSize;
+    property public final float IconSizeDense;
     property public final float ListItemElevation;
     field public static final androidx.tv.material3.ListItemDefaults INSTANCE;
   }
@@ -447,6 +449,7 @@
   }
 
   public final class ListItemKt {
+    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void DenseListItem(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> headlineContent, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional kotlin.jvm.functions.Function0<kotlin.Unit>? overlineContent, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? leadingContent, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingContent, optional float tonalElevation, optional androidx.tv.material3.ListItemShape shape, optional androidx.tv.material3.ListItemColors colors, optional androidx.tv.material3.ListItemScale scale, optional androidx.tv.material3.ListItemBorder border, optional androidx.tv.material3.ListItemGlow glow, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
     method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void ListItem(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> headlineContent, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional kotlin.jvm.functions.Function0<kotlin.Unit>? overlineContent, optional kotlin.jvm.functions.Function0<kotlin.Unit>? supportingContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? leadingContent, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingContent, optional float tonalElevation, optional androidx.tv.material3.ListItemShape shape, optional androidx.tv.material3.ListItemColors colors, optional androidx.tv.material3.ListItemScale scale, optional androidx.tv.material3.ListItemBorder border, optional androidx.tv.material3.ListItemGlow glow, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
   }
 
@@ -462,11 +465,11 @@
   @SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class ListItemShape {
   }
 
-  public final class MaterialTheme {
+  @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public final class MaterialTheme {
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.ColorScheme getColorScheme();
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.Shapes getShapes();
     method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public androidx.tv.material3.Typography getTypography();
-    property @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable @androidx.tv.material3.ExperimentalTvMaterial3Api public final androidx.tv.material3.ColorScheme colorScheme;
+    property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.tv.material3.ColorScheme colorScheme;
     property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.tv.material3.Shapes shapes;
     property @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public final androidx.tv.material3.Typography typography;
     field public static final androidx.tv.material3.MaterialTheme INSTANCE;
@@ -567,7 +570,7 @@
   @SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class SelectableChipShape {
   }
 
-  public final class ShapeDefaults {
+  @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public final class ShapeDefaults {
     method public androidx.compose.foundation.shape.CornerBasedShape getExtraLarge();
     method public androidx.compose.foundation.shape.CornerBasedShape getExtraSmall();
     method public androidx.compose.foundation.shape.CornerBasedShape getLarge();
@@ -581,7 +584,7 @@
     field public static final androidx.tv.material3.ShapeDefaults INSTANCE;
   }
 
-  @androidx.compose.runtime.Immutable public final class Shapes {
+  @SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api 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.tv.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);
     method public androidx.compose.foundation.shape.CornerBasedShape getExtraLarge();
@@ -613,8 +616,8 @@
     method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.compose.runtime.NonRestartableComposable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Surface(optional androidx.compose.ui.Modifier modifier, optional float tonalElevation, optional androidx.compose.ui.graphics.Shape shape, optional androidx.tv.material3.NonInteractiveSurfaceColors colors, optional androidx.tv.material3.Border border, optional androidx.tv.material3.Glow glow, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
     method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Surface(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional float tonalElevation, optional androidx.tv.material3.ToggleableSurfaceShape shape, optional androidx.tv.material3.ToggleableSurfaceColors colors, optional androidx.tv.material3.ToggleableSurfaceScale scale, optional androidx.tv.material3.ToggleableSurfaceBorder border, optional androidx.tv.material3.ToggleableSurfaceGlow glow, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
     method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Surface(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional boolean enabled, optional float tonalElevation, optional androidx.tv.material3.ClickableSurfaceShape shape, optional androidx.tv.material3.ClickableSurfaceColors colors, optional androidx.tv.material3.ClickableSurfaceScale scale, optional androidx.tv.material3.ClickableSurfaceBorder border, optional androidx.tv.material3.ClickableSurfaceGlow glow, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
-    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Dp> getLocalAbsoluteTonalElevation();
-    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Dp> LocalAbsoluteTonalElevation;
+    method @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Dp> getLocalAbsoluteTonalElevation();
+    property @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.unit.Dp> LocalAbsoluteTonalElevation;
   }
 
   @SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class SwitchColors {
@@ -635,19 +638,19 @@
   }
 
   @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public final class TabDefaults {
-    method @androidx.compose.runtime.Composable public androidx.tv.material3.TabColors pillIndicatorTabColors(optional long activeContentColor, optional long contentColor, optional long selectedContentColor, optional long focusedContentColor, optional long focusedSelectedContentColor, optional long disabledActiveContentColor, optional long disabledContentColor, optional long disabledSelectedContentColor);
-    method @androidx.compose.runtime.Composable public androidx.tv.material3.TabColors underlinedIndicatorTabColors(optional long activeContentColor, optional long contentColor, optional long selectedContentColor, optional long focusedContentColor, optional long focusedSelectedContentColor, optional long disabledActiveContentColor, optional long disabledContentColor, optional long disabledSelectedContentColor);
+    method @androidx.compose.runtime.Composable public androidx.tv.material3.TabColors pillIndicatorTabColors(optional long contentColor, optional long inactiveContentColor, optional long selectedContentColor, optional long focusedContentColor, optional long focusedSelectedContentColor, optional long disabledContentColor, optional long disabledInactiveContentColor, optional long disabledSelectedContentColor);
+    method @androidx.compose.runtime.Composable public androidx.tv.material3.TabColors underlinedIndicatorTabColors(optional long contentColor, optional long inactiveContentColor, optional long selectedContentColor, optional long focusedContentColor, optional long focusedSelectedContentColor, optional long disabledContentColor, optional long disabledInactiveContentColor, optional long disabledSelectedContentColor);
     field public static final androidx.tv.material3.TabDefaults INSTANCE;
   }
 
   public final class TabKt {
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Tab(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onFocus, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional boolean enabled, optional androidx.tv.material3.TabColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Tab(androidx.tv.material3.TabRowScope, boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onFocus, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional boolean enabled, optional androidx.tv.material3.TabColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
   }
 
   @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public final class TabRowDefaults {
-    method @androidx.compose.runtime.Composable public void PillIndicator(androidx.compose.ui.unit.DpRect currentTabPosition, optional androidx.compose.ui.Modifier modifier, optional long activeColor, optional long inactiveColor);
+    method @androidx.compose.runtime.Composable public void PillIndicator(androidx.compose.ui.unit.DpRect currentTabPosition, boolean isActivated, optional androidx.compose.ui.Modifier modifier, optional long activeColor, optional long inactiveColor);
     method @androidx.compose.runtime.Composable public void TabSeparator();
-    method @androidx.compose.runtime.Composable public void UnderlinedIndicator(androidx.compose.ui.unit.DpRect currentTabPosition, optional androidx.compose.ui.Modifier modifier, optional long activeColor, optional long inactiveColor);
+    method @androidx.compose.runtime.Composable public void UnderlinedIndicator(androidx.compose.ui.unit.DpRect currentTabPosition, boolean isActivated, optional androidx.compose.ui.Modifier modifier, optional long activeColor, optional long inactiveColor);
     method @androidx.compose.runtime.Composable public long contentColor();
     method public long getContainerColor();
     property public final long ContainerColor;
@@ -655,15 +658,20 @@
   }
 
   public final class TabRowKt {
-    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void TabRow(int selectedTabIndex, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional kotlin.jvm.functions.Function0<kotlin.Unit> separator, optional kotlin.jvm.functions.Function1<? super java.util.List<androidx.compose.ui.unit.DpRect>,kotlin.Unit> indicator, kotlin.jvm.functions.Function0<kotlin.Unit> tabs);
+    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void TabRow(int selectedTabIndex, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional kotlin.jvm.functions.Function0<kotlin.Unit> separator, optional kotlin.jvm.functions.Function2<? super java.util.List<androidx.compose.ui.unit.DpRect>,? super java.lang.Boolean,kotlin.Unit> indicator, kotlin.jvm.functions.Function1<? super androidx.tv.material3.TabRowScope,kotlin.Unit> tabs);
+  }
+
+  @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public interface TabRowScope {
+    method public boolean isActivated();
+    property public abstract boolean isActivated;
   }
 
   public final class TextKt {
-    method @androidx.compose.runtime.Composable public static void ProvideTextStyle(androidx.compose.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> content);
-    method @androidx.compose.runtime.Composable public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
-    method @androidx.compose.runtime.Composable public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
-    method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> getLocalTextStyle();
-    property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> LocalTextStyle;
+    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void ProvideTextStyle(androidx.compose.ui.text.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Text(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+    method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void Text(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional long fontSize, optional androidx.compose.ui.text.font.FontStyle? fontStyle, optional androidx.compose.ui.text.font.FontWeight? fontWeight, optional androidx.compose.ui.text.font.FontFamily? fontFamily, optional long letterSpacing, optional androidx.compose.ui.text.style.TextDecoration? textDecoration, optional androidx.compose.ui.text.style.TextAlign? textAlign, optional long lineHeight, optional int overflow, optional boolean softWrap, optional int maxLines, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.text.TextLayoutResult,kotlin.Unit> onTextLayout, optional androidx.compose.ui.text.TextStyle style);
+    method @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> getLocalTextStyle();
+    property @SuppressCompatibility @androidx.tv.material3.ExperimentalTvMaterial3Api public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> LocalTextStyle;
   }
 
   @SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class ToggleableSurfaceBorder {
@@ -696,7 +704,7 @@
   @SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class ToggleableSurfaceShape {
   }
 
-  @androidx.compose.runtime.Immutable public final class Typography {
+  @SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.tv.material3.ExperimentalTvMaterial3Api public final class Typography {
     ctor public Typography(optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle headlineLarge, optional androidx.compose.ui.text.TextStyle headlineMedium, optional androidx.compose.ui.text.TextStyle headlineSmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle labelLarge, optional androidx.compose.ui.text.TextStyle labelMedium, optional androidx.compose.ui.text.TextStyle labelSmall);
     method public androidx.tv.material3.Typography copy(optional androidx.compose.ui.text.TextStyle displayLarge, optional androidx.compose.ui.text.TextStyle displayMedium, optional androidx.compose.ui.text.TextStyle displaySmall, optional androidx.compose.ui.text.TextStyle headlineLarge, optional androidx.compose.ui.text.TextStyle headlineMedium, optional androidx.compose.ui.text.TextStyle headlineSmall, optional androidx.compose.ui.text.TextStyle titleLarge, optional androidx.compose.ui.text.TextStyle titleMedium, optional androidx.compose.ui.text.TextStyle titleSmall, optional androidx.compose.ui.text.TextStyle bodyLarge, optional androidx.compose.ui.text.TextStyle bodyMedium, optional androidx.compose.ui.text.TextStyle bodySmall, optional androidx.compose.ui.text.TextStyle labelLarge, optional androidx.compose.ui.text.TextStyle labelMedium, optional androidx.compose.ui.text.TextStyle labelSmall);
     method public androidx.compose.ui.text.TextStyle getBodyLarge();
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/DenseListItemScreenshotTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/DenseListItemScreenshotTest.kt
new file mode 100644
index 0000000..cb55ab8
--- /dev/null
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/DenseListItemScreenshotTest.kt
@@ -0,0 +1,414 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.tv.material3
+
+import android.os.Build
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
+import androidx.compose.material.icons.filled.KeyboardArrowRight
+import androidx.compose.testutils.assertAgainstGolden
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.SemanticsActions
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onChild
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performSemanticsAction
+import androidx.compose.ui.unit.dp
+import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.screenshot.AndroidXScreenshotTestRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@LargeTest
+@RunWith(Parameterized::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+@OptIn(ExperimentalTvMaterial3Api::class)
+class DenseListItemScreenshotTest(private val scheme: ColorSchemeWrapper) {
+    @get:Rule
+    val rule = createComposeRule()
+
+    @get:Rule
+    val screenshotRule = AndroidXScreenshotTestRule(TV_GOLDEN_MATERIAL3)
+
+    val wrapperModifier = Modifier
+        .testTag(DenseListItemWrapperTag)
+        .background(scheme.colorScheme.surface)
+        .padding(20.dp)
+
+    @Test
+    fun denseListItem_customColor() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            Box(modifier = wrapperModifier) {
+                DenseListItem(
+                    selected = false,
+                    onClick = {},
+                    headlineContent = { Text("Dense list item") },
+                    leadingContent = {
+                        Icon(
+                            imageVector = Icons.Filled.Favorite,
+                            contentDescription = null,
+                            modifier = Modifier.size(ListItemDefaults.IconSizeDense)
+                        )
+                    },
+                    colors = ListItemDefaults.colors(containerColor = Color.Red)
+                )
+            }
+        }
+
+        assertAgainstGolden("denseListItem_${scheme.name}_customColor")
+    }
+
+    @Test
+    fun denseListItem_oneLine() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            Box(modifier = wrapperModifier) {
+                DenseListItem(
+                    selected = false,
+                    onClick = {},
+                    headlineContent = { Text("One line dense list item") }
+                )
+            }
+        }
+
+        assertAgainstGolden("denseListItem_${scheme.name}_oneLine")
+    }
+
+    @Test
+    fun denseListItem_oneLine_withIcon() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            Box(modifier = wrapperModifier) {
+                DenseListItem(
+                    selected = false,
+                    onClick = {},
+                    headlineContent = { Text("One line dense list item") },
+                    leadingContent = {
+                        Icon(
+                            imageVector = Icons.Filled.Favorite,
+                            contentDescription = null,
+                            modifier = Modifier.size(ListItemDefaults.IconSizeDense)
+                        )
+                    }
+                )
+            }
+        }
+
+        assertAgainstGolden("denseListItem_${scheme.name}_oneLine_withIcon")
+    }
+
+    @Test
+    fun denseListItem_twoLine() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            Column(
+                modifier = wrapperModifier,
+                verticalArrangement = Arrangement.spacedBy(20.dp)
+            ) {
+                DenseListItem(
+                    selected = false,
+                    onClick = {},
+                    headlineContent = { Text("Two line dense list item") },
+                    supportingContent = { Text("Secondary text") }
+                )
+                DenseListItem(
+                    selected = false,
+                    onClick = {},
+                    headlineContent = { Text("Two line dense list item") },
+                    overlineContent = { Text("OVERLINE") }
+                )
+            }
+        }
+
+        assertAgainstGolden("denseListItem_${scheme.name}_twoLine")
+    }
+
+    @Test
+    fun denseListItem_twoLine_withIcon() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            Column(
+                modifier = wrapperModifier,
+                verticalArrangement = Arrangement.spacedBy(20.dp)
+            ) {
+                DenseListItem(
+                    selected = false,
+                    onClick = {},
+                    headlineContent = { Text("Two line dense list item") },
+                    supportingContent = { Text("Secondary text") },
+                    leadingContent = {
+                        Icon(
+                            imageVector = Icons.Filled.Favorite,
+                            contentDescription = null,
+                            modifier = Modifier.size(ListItemDefaults.IconSizeDense)
+                        )
+                    }
+                )
+                DenseListItem(
+                    selected = false,
+                    onClick = {},
+                    headlineContent = { Text("Two line dense list item") },
+                    overlineContent = { Text("OVERLINE") },
+                    leadingContent = {
+                        Icon(
+                            imageVector = Icons.Filled.Favorite,
+                            contentDescription = null,
+                            modifier = Modifier.size(ListItemDefaults.IconSizeDense)
+                        )
+                    }
+                )
+            }
+        }
+
+        assertAgainstGolden("denseListItem_${scheme.name}_twoLine_withIcon")
+    }
+
+    @Test
+    fun denseListItem_threeLine() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            Box(modifier = wrapperModifier) {
+                DenseListItem(
+                    selected = false,
+                    onClick = {},
+                    headlineContent = { Text("Three line dense list item") },
+                    overlineContent = { Text("OVERLINE") },
+                    supportingContent = { Text("Secondary text") }
+                )
+            }
+        }
+
+        assertAgainstGolden("denseListItem_${scheme.name}_threeLine")
+    }
+
+    @Test
+    fun denseListItem_threeLine_withIcon() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            Box(modifier = wrapperModifier) {
+                DenseListItem(
+                    selected = false,
+                    onClick = {},
+                    headlineContent = { Text("Three line dense list item") },
+                    overlineContent = { Text("OVERLINE") },
+                    supportingContent = { Text("Secondary text") },
+                    leadingContent = {
+                        Icon(
+                            imageVector = Icons.Filled.Favorite,
+                            contentDescription = null,
+                            modifier = Modifier.size(ListItemDefaults.IconSizeDense)
+                        )
+                    }
+                )
+            }
+        }
+
+        assertAgainstGolden("denseListItem_${scheme.name}_threeLine_withIcon")
+    }
+
+    @Test
+    fun denseListItem_oneLine_focused() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            Box(modifier = wrapperModifier) {
+                DenseListItem(
+                    selected = false,
+                    onClick = {},
+                    headlineContent = { Text("Dense list item") },
+                    leadingContent = {
+                        Icon(
+                            imageVector = Icons.Filled.Favorite,
+                            contentDescription = null,
+                            modifier = Modifier.size(ListItemDefaults.IconSizeDense)
+                        )
+                    }
+                )
+            }
+        }
+
+        rule.onNodeWithTag(DenseListItemWrapperTag)
+            .onChild()
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+        rule.waitForIdle()
+
+        assertAgainstGolden("denseListItem_${scheme.name}_oneLine_focused")
+    }
+
+    @Test
+    fun denseListItem_oneLine_disabled() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            Box(modifier = wrapperModifier) {
+                DenseListItem(
+                    selected = false,
+                    onClick = {},
+                    enabled = false,
+                    headlineContent = { Text("Dense list item") },
+                    leadingContent = {
+                        Icon(
+                            imageVector = Icons.Filled.Favorite,
+                            contentDescription = null,
+                            modifier = Modifier.size(ListItemDefaults.IconSizeDense)
+                        )
+                    }
+                )
+            }
+        }
+
+        assertAgainstGolden("denseListItem_${scheme.name}_oneLine_disabled")
+    }
+
+    @Test
+    fun denseListItem_oneLine_focusedDisabled() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            Box(modifier = wrapperModifier) {
+                DenseListItem(
+                    selected = false,
+                    onClick = {},
+                    enabled = false,
+                    headlineContent = { Text("Dense list item") },
+                    leadingContent = {
+                        Icon(
+                            imageVector = Icons.Filled.Favorite,
+                            contentDescription = null,
+                            modifier = Modifier.size(ListItemDefaults.IconSizeDense)
+                        )
+                    }
+                )
+            }
+        }
+
+        rule.onNodeWithTag(DenseListItemWrapperTag)
+            .onChild()
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+        rule.waitForIdle()
+
+        assertAgainstGolden("denseListItem_${scheme.name}_oneLine_focusedDisabled")
+    }
+
+    @Test
+    fun denseListItem_oneLine_selected() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            Box(modifier = wrapperModifier) {
+                DenseListItem(
+                    selected = true,
+                    onClick = {},
+                    headlineContent = { Text("Dense list item") },
+                    leadingContent = {
+                        Icon(
+                            imageVector = Icons.Filled.Favorite,
+                            contentDescription = null,
+                            modifier = Modifier.size(ListItemDefaults.IconSizeDense)
+                        )
+                    }
+                )
+            }
+        }
+
+        assertAgainstGolden("denseListItem_${scheme.name}_oneLine_selected")
+    }
+
+    @Test
+    fun denseListItem_oneLine_focusedSelected() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            Box(modifier = wrapperModifier) {
+                DenseListItem(
+                    selected = true,
+                    onClick = {},
+                    headlineContent = { Text("Dense list item") },
+                    leadingContent = {
+                        Icon(
+                            imageVector = Icons.Filled.Favorite,
+                            contentDescription = null,
+                            modifier = Modifier.size(ListItemDefaults.IconSizeDense)
+                        )
+                    }
+                )
+            }
+        }
+
+        rule.onNodeWithTag(DenseListItemWrapperTag)
+            .onChild()
+            .performSemanticsAction(SemanticsActions.RequestFocus)
+        rule.waitForIdle()
+
+        assertAgainstGolden("denseListItem_${scheme.name}_oneLine_focusedSelected")
+    }
+
+    @Test
+    fun denseListItem_threeLine_withTrailingContent() {
+        rule.setMaterialContent(scheme.colorScheme) {
+            Box(modifier = wrapperModifier) {
+                DenseListItem(
+                    selected = false,
+                    onClick = {},
+                    headlineContent = { Text("Three line dense list item") },
+                    overlineContent = { Text("OVERLINE") },
+                    supportingContent = { Text("Secondary text") },
+                    leadingContent = {
+                        Icon(
+                            imageVector = Icons.Filled.Favorite,
+                            contentDescription = null,
+                            modifier = Modifier.size(ListItemDefaults.IconSizeDense)
+                        )
+                    },
+                    trailingContent = {
+                        Icon(
+                            imageVector = Icons.Filled.KeyboardArrowRight,
+                            contentDescription = null,
+                            modifier = Modifier.size(ListItemDefaults.IconSizeDense)
+                        )
+                    }
+                )
+            }
+        }
+
+        assertAgainstGolden("denseListItem_${scheme.name}_threeLine_withTrailingContent")
+    }
+
+    private fun assertAgainstGolden(goldenName: String) {
+        rule.onNodeWithTag(DenseListItemWrapperTag)
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, goldenName)
+    }
+
+    // Provide the ColorScheme and their name parameter in a ColorSchemeWrapper.
+    // This makes sure that the default method name and the initial Scuba image generated
+    // name is as expected.
+    companion object {
+        @OptIn(ExperimentalTvMaterial3Api::class)
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun parameters() = arrayOf(
+            ColorSchemeWrapper("lightTheme", lightColorScheme()),
+            ColorSchemeWrapper("darkTheme", darkColorScheme()),
+        )
+    }
+
+    @OptIn(ExperimentalTvMaterial3Api::class)
+    class ColorSchemeWrapper constructor(val name: String, val colorScheme: ColorScheme) {
+        override fun toString(): String {
+            return name
+        }
+    }
+}
+
+private const val DenseListItemWrapperTag = "denseListItem_wrapper"
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/ImmersiveListTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/ImmersiveListTest.kt
index 0a78ff0..d44e625 100644
--- a/tv/tv-material/src/androidTest/java/androidx/tv/material3/ImmersiveListTest.kt
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/ImmersiveListTest.kt
@@ -16,7 +16,6 @@
 
 package androidx.tv.material3
 
-import androidx.compose.animation.ExperimentalAnimationApi
 import androidx.compose.foundation.background
 import androidx.compose.foundation.border
 import androidx.compose.foundation.focusable
@@ -48,17 +47,18 @@
 import androidx.compose.ui.test.requestFocus
 import androidx.compose.ui.unit.dp
 import androidx.test.platform.app.InstrumentationRegistry
+import androidx.tv.foundation.ExperimentalTvFoundationApi
 import androidx.tv.foundation.lazy.list.TvLazyColumn
 import androidx.tv.foundation.lazy.list.TvLazyRow
 import com.google.common.truth.Truth.assertThat
 import org.junit.Rule
 import org.junit.Test
 
+@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalTvFoundationApi::class)
 class ImmersiveListTest {
     @get:Rule
     val rule = createComposeRule()
 
-    @OptIn(ExperimentalTvMaterial3Api::class, ExperimentalAnimationApi::class)
     @Test
     fun immersiveList_scroll_backgroundChanges() {
         rule.setContent {
@@ -255,7 +255,6 @@
         rule.runOnIdle { focusRequester.requestFocus() }
     }
 
-    @OptIn(ExperimentalTvMaterial3Api::class, ExperimentalAnimationApi::class)
     @Composable
     private fun TestImmersiveList(focusRequesterList: List<FocusRequester>) {
         val frList = remember { focusRequesterList }
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/ListItemTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/ListItemTest.kt
index 4646a03..9905170 100644
--- a/tv/tv-material/src/androidTest/java/androidx/tv/material3/ListItemTest.kt
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/ListItemTest.kt
@@ -51,7 +51,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.width
 import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.MediumTest
+import androidx.test.filters.LargeTest
 import com.google.common.truth.Truth
 import org.junit.Rule
 import org.junit.Test
@@ -61,7 +61,7 @@
     ExperimentalTestApi::class,
     ExperimentalTvMaterial3Api::class
 )
-@MediumTest
+@LargeTest
 @RunWith(AndroidJUnit4::class)
 class ListItemTest {
     @get:Rule
@@ -257,7 +257,7 @@
     }
 
     @Test
-    fun listItem_iconPadding() {
+    fun listItem_leadingContentPadding() {
         val testIconTag = "IconTag"
 
         rule.setContent {
@@ -315,7 +315,7 @@
 
         rule.setContent {
             ListItem(
-                leadingContent = {
+                trailingContent = {
                     Box(
                         modifier = Modifier
                             .size(ListItemDefaults.IconSize)
@@ -353,14 +353,14 @@
             "padding between the bottom of the trailing content and the bottom of the list item."
         )
 
-        (trailingContentBounds.left - itemBounds.left).assertIsEqualTo(
+        (itemBounds.right - trailingContentBounds.right).assertIsEqualTo(
             16.dp,
-            "padding between the start of the trailing content and the start of the list item."
+            "padding between the end of the trailing content and the end of the list item."
         )
 
-        (textBounds.left - trailingContentBounds.right).assertIsEqualTo(
+        (trailingContentBounds.left - textBounds.right).assertIsEqualTo(
             8.dp,
-            "padding between the end of the trailing content and the start of the text."
+            "padding between the start of the trailing content and the end of the text."
         )
     }
 
@@ -488,7 +488,214 @@
         rule.onNodeWithTag(ListItemTag)
             .assertWidthIsEqualTo(rule.onRoot().getUnclippedBoundsInRoot().width)
     }
+
+    @Test
+    fun denseListItem_contentPaddingHorizontal() {
+        rule.setContent {
+            DenseListItem(
+                modifier = Modifier.testTag(DenseListItemTag),
+                headlineContent = {
+                    Text(
+                        text = "Test Text",
+                        modifier = Modifier
+                            .fillMaxWidth()
+                            .testTag(DenseListItemTextTag)
+                            .semantics(mergeDescendants = true) {}
+                    )
+                },
+                onClick = {},
+                selected = false
+            )
+        }
+
+        val itemBounds = rule.onNodeWithTag(DenseListItemTag).getUnclippedBoundsInRoot()
+        val textBounds = rule.onNodeWithTag(DenseListItemTextTag).getUnclippedBoundsInRoot()
+
+        (textBounds.left - itemBounds.left).assertIsEqualTo(
+            12.dp,
+            "padding between the start of the list item and the start of the text."
+        )
+
+        (itemBounds.right - textBounds.right).assertIsEqualTo(
+            12.dp,
+            "padding between the end of the text and the end of the list item."
+        )
+    }
+
+    @Test
+    fun denseListItem_contentPaddingVertical() {
+        rule.setContent {
+            ListItem(
+                modifier = Modifier.testTag(DenseListItemTag),
+                headlineContent = {
+                    Text(
+                        text = "Test Text",
+                        modifier = Modifier
+                            .fillMaxHeight()
+                            .testTag(DenseListItemTextTag)
+                            .semantics(mergeDescendants = true) {}
+                    )
+                },
+                onClick = {},
+                selected = false
+            )
+        }
+
+        val itemBounds = rule.onNodeWithTag(DenseListItemTag).getUnclippedBoundsInRoot()
+        val textBounds = rule.onNodeWithTag(DenseListItemTextTag).getUnclippedBoundsInRoot()
+
+        (textBounds.top - itemBounds.top).assertIsEqualTo(
+            12.dp,
+            "padding between the top of the list item and the top of the text."
+        )
+
+        (itemBounds.bottom - textBounds.bottom).assertIsEqualTo(
+            12.dp,
+            "padding between the bottom of the text and the bottom of the list item."
+        )
+    }
+
+    @Test
+    fun denseListItem_leadingContentPadding() {
+        val testIconTag = "IconTag"
+
+        rule.setContent {
+            DenseListItem(
+                leadingContent = {
+                    Box(
+                        modifier = Modifier
+                            .size(ListItemDefaults.IconSizeDense)
+                            .testTag(testIconTag)
+                            .semantics(mergeDescendants = true) {}
+                    )
+                },
+                modifier = Modifier.testTag(DenseListItemTag),
+                headlineContent = {
+                    Text(
+                        text = "Test Text",
+                        modifier = Modifier
+                            .testTag(DenseListItemTextTag)
+                            .semantics(mergeDescendants = true) {}
+                    )
+                },
+                onClick = {},
+                selected = false
+            )
+        }
+
+        val itemBounds = rule.onNodeWithTag(DenseListItemTag).getUnclippedBoundsInRoot()
+        val textBounds = rule.onNodeWithTag(DenseListItemTextTag).getUnclippedBoundsInRoot()
+        val iconBounds = rule.onNodeWithTag(testIconTag).getUnclippedBoundsInRoot()
+
+        (iconBounds.top - itemBounds.top).assertIsEqualTo(
+            10.dp,
+            "padding between the top of the list item and the top of the icon."
+        )
+
+        (itemBounds.bottom - iconBounds.bottom).assertIsEqualTo(
+            10.dp,
+            "padding between the bottom of the icon and the bottom of the list item."
+        )
+
+        (iconBounds.left - itemBounds.left).assertIsEqualTo(
+            12.dp,
+            "padding between the start of the icon and the start of the list item."
+        )
+
+        (textBounds.left - iconBounds.right).assertIsEqualTo(
+            8.dp,
+            "padding between the end of the icon and the start of the text."
+        )
+    }
+
+    @Test
+    fun denseListItem_trailingContentPadding() {
+        val testTrailingContentTag = "TrailingIconTag"
+
+        rule.setContent {
+            DenseListItem(
+                trailingContent = {
+                    Box(
+                        modifier = Modifier
+                            .size(ListItemDefaults.IconSizeDense)
+                            .testTag(testTrailingContentTag)
+                            .semantics(mergeDescendants = true) {}
+                    )
+                },
+                modifier = Modifier.testTag(DenseListItemTag),
+                headlineContent = {
+                    Text(
+                        text = "Test Text",
+                        modifier = Modifier
+                            .testTag(DenseListItemTextTag)
+                            .fillMaxWidth()
+                            .semantics(mergeDescendants = true) {}
+                    )
+                },
+                onClick = {},
+                selected = false
+            )
+        }
+
+        val itemBounds = rule.onNodeWithTag(DenseListItemTag).getUnclippedBoundsInRoot()
+        val textBounds = rule.onNodeWithTag(DenseListItemTextTag).getUnclippedBoundsInRoot()
+        val trailingContentBounds =
+            rule.onNodeWithTag(testTrailingContentTag).getUnclippedBoundsInRoot()
+
+        (trailingContentBounds.top - itemBounds.top).assertIsEqualTo(
+            10.dp,
+            "padding between the top of the list item and the top of the trailing content."
+        )
+
+        (itemBounds.bottom - trailingContentBounds.bottom).assertIsEqualTo(
+            10.dp,
+            "padding between the bottom of the trailing content and the bottom of the list item."
+        )
+
+        (itemBounds.right - trailingContentBounds.right).assertIsEqualTo(
+            12.dp,
+            "padding between the end of the trailing content and the end of the list item."
+        )
+
+        (trailingContentBounds.left - textBounds.right).assertIsEqualTo(
+            8.dp,
+            "padding between the start of the trailing content and the end of the text."
+        )
+    }
+
+    @Test
+    fun denseListItem_oneLineHeight() {
+        val expectedHeightNoIcon = 40.dp
+
+        rule.setContent {
+            DenseListItem(
+                modifier = Modifier.testTag(DenseListItemTag),
+                headlineContent = { Text(text = "text") },
+                onClick = {},
+                selected = false
+            )
+        }
+
+        rule.onNodeWithTag(DenseListItemTag).assertHeightIsEqualTo(expectedHeightNoIcon)
+    }
+
+    @Test
+    fun denseListItem_width() {
+        rule.setContent {
+            DenseListItem(
+                modifier = Modifier.testTag(DenseListItemTag),
+                headlineContent = { Text(text = "text") },
+                onClick = {},
+                selected = false
+            )
+        }
+        rule.onNodeWithTag(DenseListItemTag)
+            .assertWidthIsEqualTo(rule.onRoot().getUnclippedBoundsInRoot().width)
+    }
 }
 
 private const val ListItemTag = "ListItem"
 private const val ListItemTextTag = "ListItemText"
+
+private const val DenseListItemTag = "DenseListItem"
+private const val DenseListItemTextTag = "DenseListItemText"
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/TabRowScreenshotTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/TabRowScreenshotTest.kt
index ff289b8..ab61794 100644
--- a/tv/tv-material/src/androidTest/java/androidx/tv/material3/TabRowScreenshotTest.kt
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/TabRowScreenshotTest.kt
@@ -203,9 +203,10 @@
                 TabRow(
                     selectedTabIndex = selectedTabIndex,
                     separator = { Spacer(modifier = Modifier.width(12.dp)) },
-                    indicator = { tabPositions ->
+                    indicator = { tabPositions, isisActivated ->
                         TabRowDefaults.UnderlinedIndicator(
-                            currentTabPosition = tabPositions[selectedTabIndex]
+                            currentTabPosition = tabPositions[selectedTabIndex],
+                            isActivated = isisActivated,
                         )
                     }
                 ) {
@@ -246,9 +247,10 @@
                 TabRow(
                     selectedTabIndex = selectedTabIndex,
                     separator = { Spacer(modifier = Modifier.width(12.dp)) },
-                    indicator = { tabPositions ->
+                    indicator = { tabPositions, isActivated ->
                         TabRowDefaults.UnderlinedIndicator(
-                            currentTabPosition = tabPositions[selectedTabIndex]
+                            currentTabPosition = tabPositions[selectedTabIndex],
+                            isActivated = isActivated,
                         )
                     }
                 ) {
@@ -295,9 +297,10 @@
                 TabRow(
                     selectedTabIndex = selectedTabIndex,
                     separator = { Spacer(modifier = Modifier.width(12.dp)) },
-                    indicator = { tabPositions ->
+                    indicator = { tabPositions, isActivated ->
                         TabRowDefaults.UnderlinedIndicator(
-                            currentTabPosition = tabPositions[selectedTabIndex]
+                            currentTabPosition = tabPositions[selectedTabIndex],
+                            isActivated = isActivated,
                         )
                     },
                 ) {
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/TabRowTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/TabRowTest.kt
index 1758853..f1ca6d3 100644
--- a/tv/tv-material/src/androidTest/java/androidx/tv/material3/TabRowTest.kt
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/TabRowTest.kt
@@ -169,17 +169,19 @@
                     buildTabPanel = @Composable { index, _ ->
                         BasicText(text = "Panel ${index + 1}")
                     },
-                    indicator = @Composable { tabPositions ->
+                    indicator = @Composable { tabPositions, isActivated ->
                         // FocusedTab's indicator
                         TabRowDefaults.PillIndicator(
                             currentTabPosition = tabPositions[focusedTabIndex],
+                            isActivated = isActivated,
                             activeColor = Color.Blue.copy(alpha = 0.4f),
                             inactiveColor = Color.Transparent,
                         )
 
                         // SelectedTab's indicator
                         TabRowDefaults.PillIndicator(
-                            currentTabPosition = tabPositions[activeTabIndex]
+                            currentTabPosition = tabPositions[activeTabIndex],
+                            isActivated = isActivated,
                         )
                     }
                 )
@@ -203,6 +205,7 @@
         rule.onNodeWithText(secondPanel).assertIsDisplayed()
     }
 
+    @OptIn(ExperimentalTvMaterial3Api::class)
     private fun setContent(
         tabs: List<String>,
         contentBuilder: @Composable () -> Unit = {
@@ -235,15 +238,16 @@
     modifier: Modifier = Modifier,
     onFocus: (index: Int) -> Unit = {},
     onClick: (index: Int) -> Unit = onFocus,
-    buildTab: @Composable ((index: Int, tab: String) -> Unit) = @Composable { index, tab ->
-        TabSample(
-            selected = selectedTabIndex == index,
-            onFocus = { onFocus(index) },
-            onClick = { onClick(index) },
-            modifier = Modifier.testTag(tab),
-        )
-    },
-    indicator: @Composable ((tabPositions: List<DpRect>) -> Unit)? = null,
+    buildTab: @Composable (TabRowScope.(index: Int, tab: String) -> Unit) =
+        @Composable { index, tab ->
+            TabSample(
+                selected = selectedTabIndex == index,
+                onFocus = { onFocus(index) },
+                onClick = { onClick(index) },
+                modifier = Modifier.testTag(tab),
+            )
+        },
+    indicator: @Composable ((tabPositions: List<DpRect>, isTabRowActive: Boolean) -> Unit)? = null,
     buildTabPanel: @Composable ((index: Int, tab: String) -> Unit) = @Composable { _, tab ->
         BasicText(text = tab)
     },
@@ -294,7 +298,7 @@
 
 @OptIn(ExperimentalTvMaterial3Api::class)
 @Composable
-private fun TabSample(
+private fun TabRowScope.TabSample(
     selected: Boolean,
     modifier: Modifier = Modifier,
     onFocus: () -> Unit = {},
diff --git a/tv/tv-material/src/androidTest/java/androidx/tv/material3/TextTest.kt b/tv/tv-material/src/androidTest/java/androidx/tv/material3/TextTest.kt
index 97f4fe5..bb4bb85 100644
--- a/tv/tv-material/src/androidTest/java/androidx/tv/material3/TextTest.kt
+++ b/tv/tv-material/src/androidTest/java/androidx/tv/material3/TextTest.kt
@@ -40,6 +40,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
+@OptIn(ExperimentalTvMaterial3Api::class)
 @MediumTest
 @RunWith(AndroidJUnit4::class)
 class TextTest {
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/Card.kt b/tv/tv-material/src/main/java/androidx/tv/material3/Card.kt
index 8f84169..860008d 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/Card.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/Card.kt
@@ -342,6 +342,7 @@
     }
 }
 
+@OptIn(ExperimentalTvMaterial3Api::class)
 @Composable
 internal fun CardContent(
     title: @Composable () -> Unit,
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/CardLayout.kt b/tv/tv-material/src/main/java/androidx/tv/material3/CardLayout.kt
index 29c82d3..e1cab78 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/CardLayout.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/CardLayout.kt
@@ -155,6 +155,7 @@
     }
 }
 
+@OptIn(ExperimentalTvMaterial3Api::class)
 @Composable
 internal fun CardLayoutContent(
     title: @Composable () -> Unit,
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/Carousel.kt b/tv/tv-material/src/main/java/androidx/tv/material3/Carousel.kt
index 559f3f9..2da1c2d 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/Carousel.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/Carousel.kt
@@ -475,7 +475,6 @@
      * @param spacing spacing between the indicator dots
      * @param indicator indicator dot representing each item in the carousel
      */
-    @ExperimentalTvMaterial3Api
     @Composable
     fun IndicatorRow(
         itemCount: Int,
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/Chip.kt b/tv/tv-material/src/main/java/androidx/tv/material3/Chip.kt
index 8833042..82ca1d2 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/Chip.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/Chip.kt
@@ -427,6 +427,7 @@
     }
 }
 
+@OptIn(ExperimentalTvMaterial3Api::class)
 @Composable
 private fun ChipContent(
     label: @Composable () -> Unit,
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/ContentColor.kt b/tv/tv-material/src/main/java/androidx/tv/material3/ContentColor.kt
index 261dc24..84d56da 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/ContentColor.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/ContentColor.kt
@@ -31,4 +31,7 @@
  *
  * Defaults to [Color.Black] if no color has been explicitly set.
  */
+@Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
+@ExperimentalTvMaterial3Api
+@get:ExperimentalTvMaterial3Api
 val LocalContentColor = compositionLocalOf { Color.Black }
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/ListItem.kt b/tv/tv-material/src/main/java/androidx/tv/material3/ListItem.kt
index 5ba1481..6e1a4a1 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/ListItem.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/ListItem.kt
@@ -34,6 +34,7 @@
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.semantics.selected
 import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.unit.Dp
 
 /**
@@ -114,6 +115,107 @@
         scale = scale,
         border = border,
         glow = glow,
+        minHeight = listItemMinHeight(
+            hasLeadingContent = leadingContent != null,
+            hasSupportingContent = supportingContent != null,
+            hasOverlineContent = overlineContent != null
+        ),
+        minIconSize = ListItemDefaults.IconSize,
+        headlineTextStyle = MaterialTheme.typography.titleMedium,
+        trailingTextStyle = MaterialTheme.typography.labelLarge,
+        interactionSource = interactionSource
+    )
+}
+
+/**
+ * Lists are continuous, vertical indexes of text or images.
+ *
+ * [DenseListItem] is a smaller/denser version of the Material [ListItem].
+ *
+ * This component can be used to achieve the list item templates existing in the spec. One-line list
+ * items have a singular line of headline content. Two-line list items additionally have either
+ * supporting or overline content. Three-line list items have either both supporting and overline
+ * content, or extended (two-line) supporting text.
+ *
+ * This ListItem handles click events, calling its [onClick] lambda. It also support selected state
+ * which can be toggled using the [selected] param.
+ *
+ * @param selected defines whether this ListItem is selected or not
+ * @param onClick called when this ListItem is clicked
+ * @param headlineContent the [Composable] headline content of the list item
+ * @param modifier [Modifier] to be applied to the list item
+ * @param enabled controls the enabled state of this list item. When `false`, this component will
+ * not respond to user input, and it will appear visually disabled and disabled to accessibility
+ * services.
+ * @param onLongClick called when this ListItem is long clicked (long-pressed).
+ * @param leadingContent the [Composable] leading content of the list item
+ * @param overlineContent the [Composable] content displayed above the headline content
+ * @param supportingContent the [Composable] content displayed below the headline content
+ * @param trailingContent the [Composable] trailing meta text, icon, switch or checkbox
+ * @param tonalElevation the tonal elevation of this list item
+ * @param shape [ListItemShape] defines the shape of ListItem's container in different interaction
+ * states. See [ListItemDefaults.shape].
+ * @param colors [ListItemColors] defines the background and content colors used in the list item
+ * for different interaction states. See [ListItemDefaults.colors]
+ * @param scale [ListItemScale] defines the size of the list item relative to its original size in
+ * different interaction states. See [ListItemDefaults.scale]
+ * @param border [ListItemBorder] defines a border around the list item in different interaction
+ * states. See [ListItemDefaults.border]
+ * @param glow [ListItemGlow] defines a shadow to be shown behind the list item for different
+ * interaction states. See [ListItemDefaults.glow]
+ * @param interactionSource the [MutableInteractionSource] representing the stream of
+ * [Interaction]s for this component. You can create and pass in your own [remember]ed instance
+ * to observe [Interaction]s and customize the appearance / behavior of this list item in different
+ * states.
+ */
+@ExperimentalTvMaterial3Api
+@Composable
+fun DenseListItem(
+    selected: Boolean,
+    onClick: () -> Unit,
+    headlineContent: @Composable () -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    onLongClick: (() -> Unit)? = null,
+    overlineContent: (@Composable () -> Unit)? = null,
+    supportingContent: (@Composable () -> Unit)? = null,
+    leadingContent: (@Composable BoxScope.() -> Unit)? = null,
+    trailingContent: (@Composable () -> Unit)? = null,
+    tonalElevation: Dp = ListItemDefaults.ListItemElevation,
+    shape: ListItemShape = ListItemDefaults.shape(),
+    colors: ListItemColors = ListItemDefaults.colors(),
+    scale: ListItemScale = ListItemDefaults.scale(),
+    border: ListItemBorder = ListItemDefaults.border(),
+    glow: ListItemGlow = ListItemDefaults.glow(),
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
+) {
+    BaseListItem(
+        selected = selected,
+        onClick = onClick,
+        headlineContent = headlineContent,
+        contentPadding = ListItemDefaults.ContentPaddingDense,
+        modifier = modifier,
+        enabled = enabled,
+        onLongClick = onLongClick,
+        leadingContent = leadingContent,
+        overlineContent = overlineContent,
+        supportingContent = supportingContent,
+        trailingContent = trailingContent,
+        tonalElevation = tonalElevation,
+        shape = shape,
+        colors = colors,
+        scale = scale,
+        border = border,
+        glow = glow,
+        minHeight = listItemMinHeight(
+            hasLeadingContent = leadingContent != null,
+            hasSupportingContent = supportingContent != null,
+            hasOverlineContent = overlineContent != null,
+            dense = true
+        ),
+        minIconSize = ListItemDefaults.IconSizeDense,
+        headlineTextStyle = MaterialTheme.typography.titleSmall,
+        trailingTextStyle = MaterialTheme.typography.labelSmall,
         interactionSource = interactionSource
     )
 }
@@ -145,6 +247,10 @@
  * states.
  * @param glow [ListItemGlow] defines a shadow to be shown behind the list item for different
  * interaction states.
+ * @param minHeight defines the minimum height [Dp] for the ListItem.
+ * @param minIconSize defines the minimum icon size [Dp] to be used in leading content.
+ * @param headlineTextStyle defines the [TextStyle] for the headline content.
+ * @param trailingTextStyle defines the [TextStyle] for the trailing content.
  * @param interactionSource the [MutableInteractionSource] representing the stream of
  * [Interaction]s for this component. You can create and pass in your own [remember]ed instance
  * to observe [Interaction]s and customize the appearance / behavior of this list item in different
@@ -170,6 +276,10 @@
     scale: ListItemScale,
     border: ListItemBorder,
     glow: ListItemGlow,
+    minHeight: Dp,
+    minIconSize: Dp,
+    headlineTextStyle: TextStyle,
+    trailingTextStyle: TextStyle,
     interactionSource: MutableInteractionSource
 ) {
     val semanticModifier = Modifier
@@ -177,6 +287,7 @@
             this.selected = selected
         }
         .then(modifier)
+
     Surface(
         checked = selected,
         onCheckedChange = { onClick.invoke() },
@@ -193,13 +304,7 @@
     ) {
         Row(
             modifier = Modifier
-                .defaultMinSize(
-                    minHeight = listItemMinHeight(
-                        hasLeadingContent = leadingContent != null,
-                        hasSupportingContent = supportingContent != null,
-                        hasOverlineContent = overlineContent != null
-                    )
-                )
+                .defaultMinSize(minHeight = minHeight)
                 .padding(contentPadding),
             verticalAlignment = Alignment.CenterVertically
         ) {
@@ -207,8 +312,8 @@
                 Box(
                     modifier = Modifier
                         .defaultMinSize(
-                            minWidth = ListItemDefaults.IconSize,
-                            minHeight = ListItemDefaults.IconSize
+                            minWidth = minIconSize,
+                            minHeight = minIconSize
                         )
                         .graphicsLayer {
                             alpha = ListItemDefaults.LeadingContentOpacity
@@ -241,7 +346,7 @@
                     }
 
                     ProvideTextStyle(
-                        value = MaterialTheme.typography.titleMedium,
+                        value = headlineTextStyle,
                         content = headlineContent
                     )
 
@@ -269,7 +374,7 @@
                         LocalContentColor provides LocalContentColor.current
                     ) {
                         ProvideTextStyle(
-                            value = MaterialTheme.typography.labelLarge,
+                            value = trailingTextStyle,
                             content = it
                         )
                     }
@@ -286,6 +391,7 @@
  * @param hasLeadingContent if the leading supporting visual of the list item exists.
  * @param hasSupportingContent if the supporting text composable of the list item exists.
  * @param hasOverlineContent if the text composable displayed above the headline text exists.
+ * @param dense whether the current list item is dense or not.
  *
  * @return The minimum container height for the given list item (to be used with
  * [Modifier.defaultMinSize]).
@@ -294,16 +400,29 @@
 private fun listItemMinHeight(
     hasLeadingContent: Boolean,
     hasSupportingContent: Boolean,
-    hasOverlineContent: Boolean
+    hasOverlineContent: Boolean,
+    dense: Boolean = false
 ): Dp {
     return when {
-        hasSupportingContent && hasOverlineContent -> ListItemDefaults.MinContainerHeightThreeLine
+        hasSupportingContent && hasOverlineContent -> {
+            if (dense) ListItemDefaults.MinDenseContainerHeightThreeLine
+            else ListItemDefaults.MinContainerHeightThreeLine
+        }
 
-        hasSupportingContent || hasOverlineContent -> ListItemDefaults.MinContainerHeightTwoLine
+        hasSupportingContent || hasOverlineContent -> {
+            if (dense) ListItemDefaults.MinDenseContainerHeightTwoLine
+            else ListItemDefaults.MinContainerHeightTwoLine
+        }
 
-        hasLeadingContent -> ListItemDefaults.MinContainerHeightLeadingContent
+        hasLeadingContent -> {
+            if (dense) ListItemDefaults.MinDenseContainerHeightLeadingContent
+            else ListItemDefaults.MinContainerHeightLeadingContent
+        }
 
-        else -> ListItemDefaults.MinContainerHeight
+        else -> {
+            if (dense) ListItemDefaults.MinDenseContainerHeight
+            else ListItemDefaults.MinContainerHeight
+        }
     }
 }
 
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/ListItemDefaults.kt b/tv/tv-material/src/main/java/androidx/tv/material3/ListItemDefaults.kt
index e05e944..922a83a 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/ListItemDefaults.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/ListItemDefaults.kt
@@ -39,6 +39,11 @@
     val IconSize = 32.dp
 
     /**
+     * The Icon size used by [DenseListItem].
+     */
+    val IconSizeDense = 20.dp
+
+    /**
      * The default elevation used by [ListItem].
      */
     val ListItemElevation = Elevation.Level0
@@ -51,6 +56,13 @@
         vertical = 12.dp
     )
 
+    /**
+     * The default content padding [PaddingValues] used by [DenseListItem]
+     */
+    internal val ContentPaddingDense = PaddingValues(
+        horizontal = 12.dp,
+        vertical = 10.dp
+    )
     internal const val LeadingContentOpacity = 0.8f
     internal const val OverlineContentOpacity = 0.6f
     internal const val SupportingContentOpacity = 0.8f
@@ -63,6 +75,11 @@
     internal val MinContainerHeightTwoLine = 64.dp
     internal val MinContainerHeightThreeLine = 80.dp
 
+    internal val MinDenseContainerHeight = 40.dp
+    internal val MinDenseContainerHeightLeadingContent = 40.dp
+    internal val MinDenseContainerHeightTwoLine = 56.dp
+    internal val MinDenseContainerHeightThreeLine = 72.dp
+
     private val ListItemShape = RoundedCornerShape(8.dp)
 
     private val DefaultBorder
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/MaterialTheme.kt b/tv/tv-material/src/main/java/androidx/tv/material3/MaterialTheme.kt
index 71ec11d..46147e8 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/MaterialTheme.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/MaterialTheme.kt
@@ -74,11 +74,11 @@
  * Contains functions to access the current theme values provided at the call site's position in
  * the hierarchy.
  */
+@ExperimentalTvMaterial3Api
 object MaterialTheme {
     /**
      * Retrieves the current [ColorScheme] at the call site's position in the hierarchy.
      */
-    @ExperimentalTvMaterial3Api
     val colorScheme: ColorScheme
         @Composable
         @ReadOnlyComposable
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/Shapes.kt b/tv/tv-material/src/main/java/androidx/tv/material3/Shapes.kt
index 9398128..d051b66 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/Shapes.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/Shapes.kt
@@ -65,6 +65,7 @@
  * @param extraLarge A shape style with 4 same-sized corners whose size are bigger than
  * [Shapes.large] and smaller than [CircleShape]. By default large FABs use this shape.
  */
+@ExperimentalTvMaterial3Api
 @Immutable
 class Shapes(
     // Shapes None and Full are omitted as None is a RectangleShape and Full is a CircleShape.
@@ -122,6 +123,7 @@
 /**
  * Contains the default values used by [Shapes]
  */
+@ExperimentalTvMaterial3Api
 object ShapeDefaults {
     /** Extra small sized corner shape */
     val ExtraSmall: CornerBasedShape = ShapeTokens.CornerExtraSmall
@@ -154,6 +156,7 @@
  * tokens:
  * ``MaterialTheme.shapes.fromToken(FabPrimarySmallTokens.ContainerShape)``
  */
+@OptIn(ExperimentalTvMaterial3Api::class)
 internal fun Shapes.fromToken(value: ShapeKeyTokens): Shape {
     return when (value) {
         ShapeKeyTokens.CornerExtraLarge -> extraLarge
@@ -171,10 +174,12 @@
 }
 
 /** Converts a shape token key to the local shape provided by the theme */
+@OptIn(ExperimentalTvMaterial3Api::class)
 @Composable
 internal fun ShapeKeyTokens.toShape(): Shape {
     return MaterialTheme.shapes.fromToken(this)
 }
 
 /** CompositionLocal used to specify the default shapes for the surfaces. */
+@OptIn(ExperimentalTvMaterial3Api::class)
 internal val LocalShapes = staticCompositionLocalOf { Shapes() }
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/Surface.kt b/tv/tv-material/src/main/java/androidx/tv/material3/Surface.kt
index e5897fb..2546e1c 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/Surface.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/Surface.kt
@@ -640,8 +640,12 @@
  * absolute elevation is a sum of all the previous elevations. Absolute elevation is only used for
  * calculating surface tonal colors, and is *not* used for drawing the shadow in a [SurfaceImpl].
  */
+@Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
+@ExperimentalTvMaterial3Api
+@get:ExperimentalTvMaterial3Api
 val LocalAbsoluteTonalElevation = compositionLocalOf { 0.dp }
-private val AcceptableKeys = hashSetOf(
+
+private val AcceptableKeys = intArrayOf(
     NativeKeyEvent.KEYCODE_DPAD_CENTER,
     NativeKeyEvent.KEYCODE_ENTER,
     NativeKeyEvent.KEYCODE_NUMPAD_ENTER
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/Tab.kt b/tv/tv-material/src/main/java/androidx/tv/material3/Tab.kt
index 52c353d..c791d57 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/Tab.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/Tab.kt
@@ -56,9 +56,9 @@
  * and customize the appearance / behavior of this tab in different states.
  * @param content content of the [Tab]
  */
-@ExperimentalTvMaterial3Api // TODO (b/263353219): Remove this before launching beta
+@ExperimentalTvMaterial3Api
 @Composable
-fun Tab(
+fun TabRowScope.Tab(
     selected: Boolean,
     onFocus: () -> Unit,
     modifier: Modifier = Modifier,
@@ -68,7 +68,6 @@
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
     content: @Composable RowScope.() -> Unit
 ) {
-    val isTabRowActive = LocalTabRowHasFocus.current
     Surface(
         checked = selected,
         onCheckedChange = { onClick() },
@@ -83,7 +82,7 @@
                 this.role = Role.Tab
             },
         colors = colors.toToggleableSurfaceColors(
-            isTabRowActive = isTabRowActive,
+            isActivated = isActivated,
             enabled = enabled,
         ),
         enabled = enabled,
@@ -110,39 +109,39 @@
 @ExperimentalTvMaterial3Api // TODO (b/263353219): Remove this before launching beta
 class TabColors
 internal constructor(
-    internal val activeContentColor: Color,
-    internal val contentColor: Color = activeContentColor.copy(alpha = 0.4f),
+    internal val contentColor: Color,
+    internal val inactiveContentColor: Color = contentColor.copy(alpha = 0.4f),
     internal val selectedContentColor: Color,
     internal val focusedContentColor: Color,
     internal val focusedSelectedContentColor: Color,
-    internal val disabledActiveContentColor: Color,
-    internal val disabledContentColor: Color = disabledActiveContentColor.copy(alpha = 0.4f),
+    internal val disabledContentColor: Color,
+    internal val disabledInactiveContentColor: Color = disabledContentColor.copy(alpha = 0.4f),
     internal val disabledSelectedContentColor: Color,
 ) {
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (other == null || other !is TabColors) return false
 
-        if (activeContentColor != other.activeContentColor) return false
         if (contentColor != other.contentColor) return false
+        if (inactiveContentColor != other.inactiveContentColor) return false
         if (selectedContentColor != other.selectedContentColor) return false
         if (focusedContentColor != other.focusedContentColor) return false
         if (focusedSelectedContentColor != other.focusedSelectedContentColor) return false
-        if (disabledActiveContentColor != other.disabledActiveContentColor) return false
         if (disabledContentColor != other.disabledContentColor) return false
+        if (disabledInactiveContentColor != other.disabledInactiveContentColor) return false
         if (disabledSelectedContentColor != other.disabledSelectedContentColor) return false
 
         return true
     }
 
     override fun hashCode(): Int {
-        var result = activeContentColor.hashCode()
-        result = 31 * result + contentColor.hashCode()
+        var result = contentColor.hashCode()
+        result = 31 * result + inactiveContentColor.hashCode()
         result = 31 * result + selectedContentColor.hashCode()
         result = 31 * result + focusedContentColor.hashCode()
         result = 31 * result + focusedSelectedContentColor.hashCode()
-        result = 31 * result + disabledActiveContentColor.hashCode()
         result = 31 * result + disabledContentColor.hashCode()
+        result = 31 * result + disabledInactiveContentColor.hashCode()
         result = 31 * result + disabledSelectedContentColor.hashCode()
         return result
     }
@@ -153,74 +152,74 @@
     /**
      * [Tab]'s content colors to in conjunction with underlined indicator
      *
-     * @param activeContentColor applied when the any of the other tabs is focused
-     * @param contentColor the default color of the tab's content when none of the tabs are focused
+     * @param contentColor applied when the any of the other tabs is focused
+     * @param inactiveContentColor the default color of the tab's content when none of the tabs are focused
      * @param selectedContentColor applied when the current tab is selected
      * @param focusedContentColor applied when the current tab is focused
      * @param focusedSelectedContentColor applied when the current tab is both focused and selected
-     * @param disabledActiveContentColor applied when any of the other tabs is focused and the
+     * @param disabledContentColor applied when any of the other tabs is focused and the
      * current tab is disabled
-     * @param disabledContentColor applied when the current tab is disabled and none of the tabs are
+     * @param disabledInactiveContentColor applied when the current tab is disabled and none of the tabs are
      * focused
      * @param disabledSelectedContentColor applied when the current tab is disabled and selected
      */
     @OptIn(ExperimentalTvMaterial3Api::class)
     @Composable
     fun underlinedIndicatorTabColors(
-        activeContentColor: Color = LocalContentColor.current,
-        contentColor: Color = activeContentColor.copy(alpha = 0.4f),
+        contentColor: Color = LocalContentColor.current,
+        inactiveContentColor: Color = contentColor.copy(alpha = 0.4f),
         selectedContentColor: Color = MaterialTheme.colorScheme.onPrimaryContainer,
         focusedContentColor: Color = MaterialTheme.colorScheme.primary,
         focusedSelectedContentColor: Color = focusedContentColor,
-        disabledActiveContentColor: Color = activeContentColor,
-        disabledContentColor: Color = disabledActiveContentColor.copy(alpha = 0.4f),
+        disabledContentColor: Color = contentColor,
+        disabledInactiveContentColor: Color = disabledContentColor.copy(alpha = 0.4f),
         disabledSelectedContentColor: Color = selectedContentColor,
     ): TabColors =
         TabColors(
-            activeContentColor = activeContentColor,
             contentColor = contentColor,
+            inactiveContentColor = inactiveContentColor,
             selectedContentColor = selectedContentColor,
             focusedContentColor = focusedContentColor,
             focusedSelectedContentColor = focusedSelectedContentColor,
-            disabledActiveContentColor = disabledActiveContentColor,
             disabledContentColor = disabledContentColor,
+            disabledInactiveContentColor = disabledInactiveContentColor,
             disabledSelectedContentColor = disabledSelectedContentColor,
         )
 
     /**
      * [Tab]'s content colors to in conjunction with pill indicator
      *
-     * @param activeContentColor applied when the any of the other tabs is focused
-     * @param contentColor the default color of the tab's content when none of the tabs are focused
+     * @param contentColor applied when the any of the other tabs is focused
+     * @param inactiveContentColor the default color of the tab's content when none of the tabs are focused
      * @param selectedContentColor applied when the current tab is selected
      * @param focusedContentColor applied when the current tab is focused
      * @param focusedSelectedContentColor applied when the current tab is both focused and selected
-     * @param disabledActiveContentColor applied when any of the other tabs is focused and the
+     * @param disabledContentColor applied when any of the other tabs is focused and the
      * current tab is disabled
-     * @param disabledContentColor applied when the current tab is disabled and none of the tabs are
+     * @param disabledInactiveContentColor applied when the current tab is disabled and none of the tabs are
      * focused
      * @param disabledSelectedContentColor applied when the current tab is disabled and selected
      */
     @OptIn(ExperimentalTvMaterial3Api::class)
     @Composable
     fun pillIndicatorTabColors(
-        activeContentColor: Color = LocalContentColor.current,
-        contentColor: Color = activeContentColor.copy(alpha = 0.4f),
+        contentColor: Color = LocalContentColor.current,
+        inactiveContentColor: Color = contentColor.copy(alpha = 0.4f),
         selectedContentColor: Color = MaterialTheme.colorScheme.onPrimaryContainer,
         focusedContentColor: Color = MaterialTheme.colorScheme.surfaceVariant,
         focusedSelectedContentColor: Color = focusedContentColor,
-        disabledActiveContentColor: Color = activeContentColor,
-        disabledContentColor: Color = disabledActiveContentColor.copy(alpha = 0.4f),
+        disabledContentColor: Color = contentColor,
+        disabledInactiveContentColor: Color = disabledContentColor.copy(alpha = 0.4f),
         disabledSelectedContentColor: Color = selectedContentColor,
     ): TabColors =
         TabColors(
-            activeContentColor = activeContentColor,
             contentColor = contentColor,
+            inactiveContentColor = inactiveContentColor,
             selectedContentColor = selectedContentColor,
             focusedContentColor = focusedContentColor,
             focusedSelectedContentColor = focusedSelectedContentColor,
-            disabledActiveContentColor = disabledActiveContentColor,
             disabledContentColor = disabledContentColor,
+            disabledInactiveContentColor = disabledInactiveContentColor,
             disabledSelectedContentColor = disabledSelectedContentColor,
         )
 }
@@ -228,16 +227,16 @@
 @OptIn(ExperimentalTvMaterial3Api::class)
 @Composable
 internal fun TabColors.toToggleableSurfaceColors(
-    isTabRowActive: Boolean,
+    isActivated: Boolean,
     enabled: Boolean,
 ) =
     ToggleableSurfaceDefaults.colors(
-        contentColor = if (isTabRowActive) activeContentColor else contentColor,
+        contentColor = if (isActivated) contentColor else inactiveContentColor,
         selectedContentColor = if (enabled) selectedContentColor else disabledSelectedContentColor,
         focusedContentColor = focusedContentColor,
         focusedSelectedContentColor = focusedSelectedContentColor,
         disabledContentColor =
-        if (isTabRowActive) disabledActiveContentColor else disabledContentColor,
+        if (isActivated) disabledContentColor else disabledInactiveContentColor,
         containerColor = Color.Transparent,
         focusedContainerColor = Color.Transparent,
         pressedContainerColor = Color.Transparent,
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/TabRow.kt b/tv/tv-material/src/main/java/androidx/tv/material3/TabRow.kt
index 902a974..7fb80c5 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/TabRow.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/TabRow.kt
@@ -32,7 +32,6 @@
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.runtime.compositionLocalOf
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -86,32 +85,37 @@
     containerColor: Color = TabRowDefaults.ContainerColor,
     contentColor: Color = TabRowDefaults.contentColor(),
     separator: @Composable () -> Unit = { TabRowDefaults.TabSeparator() },
-    indicator: @Composable (tabPositions: List<DpRect>) -> Unit =
-        @Composable { tabPositions ->
-            tabPositions.getOrNull(selectedTabIndex)?.let {
-                TabRowDefaults.PillIndicator(currentTabPosition = it)
+    indicator: @Composable (tabPositions: List<DpRect>, isActivated: Boolean) -> Unit =
+        @Composable { tabPositions, isActivated ->
+            tabPositions.getOrNull(selectedTabIndex)?.let { currentTabPosition ->
+                TabRowDefaults.PillIndicator(
+                    currentTabPosition = currentTabPosition,
+                    isActivated = isActivated
+                )
             }
         },
-    tabs: @Composable () -> Unit
+    tabs: @Composable TabRowScope.() -> Unit
 ) {
     val scrollState = rememberScrollState()
-    var isAnyTabFocused by remember { mutableStateOf(false) }
+    var isActivated by remember { mutableStateOf(false) }
 
-    CompositionLocalProvider(
-        LocalTabRowHasFocus provides isAnyTabFocused,
-        LocalContentColor provides contentColor
-    ) {
+    CompositionLocalProvider(LocalContentColor provides contentColor) {
+
         SubcomposeLayout(
             modifier =
             modifier
                 .background(containerColor)
                 .clipToBounds()
                 .horizontalScroll(scrollState)
-                .onFocusChanged { isAnyTabFocused = it.hasFocus }
+                .onFocusChanged { isActivated = it.hasFocus }
                 .selectableGroup()
         ) { constraints ->
             // Tab measurables
-            val tabMeasurables = subcompose(TabRowSlots.Tabs, tabs)
+            val tabMeasurables = subcompose(TabRowSlots.Tabs) {
+                TabRowScopeImpl(isActivated).apply {
+                    tabs()
+                }
+            }
 
             // Tab placeables
             val tabPlaceables =
@@ -165,7 +169,9 @@
                 }
 
                 // Place the indicator
-                subcompose(TabRowSlots.Indicator) { indicator(tabPositions) }
+                subcompose(TabRowSlots.Indicator) {
+                    indicator(tabPositions, isActivated)
+                }
                     .forEach {
                         it.measure(Constraints.fixed(layoutWidth, layoutHeight)).placeRelative(0, 0)
                     }
@@ -193,6 +199,7 @@
      * Adds a pill indicator behind the tab
      *
      * @param currentTabPosition position of the current selected tab
+     * @param isActivated whether any tab in TabRow is focused
      * @param modifier modifier to be applied to the indicator
      * @param activeColor color of indicator when [TabRow] is active
      * @param inactiveColor color of indicator when [TabRow] is inactive
@@ -200,11 +207,11 @@
     @Composable
     fun PillIndicator(
         currentTabPosition: DpRect,
+        isActivated: Boolean,
         modifier: Modifier = Modifier,
         activeColor: Color = MaterialTheme.colorScheme.onSurface,
         inactiveColor: Color = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.4f)
     ) {
-        val anyTabFocused = LocalTabRowHasFocus.current
         val width by animateDpAsState(
             targetValue = currentTabPosition.width,
             label = "PillIndicator.width",
@@ -218,7 +225,7 @@
 
         val pillColor by
         animateColorAsState(
-            targetValue = if (anyTabFocused) activeColor else inactiveColor,
+            targetValue = if (isActivated) activeColor else inactiveColor,
             label = "PillIndicator.pillColor"
         )
 
@@ -238,6 +245,7 @@
      * Adds an underlined indicator below the tab
      *
      * @param currentTabPosition position of the current selected tab
+     * @param isActivated whether any tab in TabRow is focused
      * @param modifier modifier to be applied to the indicator
      * @param activeColor color of indicator when [TabRow] is active
      * @param inactiveColor color of indicator when [TabRow] is inactive
@@ -245,22 +253,26 @@
     @Composable
     fun UnderlinedIndicator(
         currentTabPosition: DpRect,
+        isActivated: Boolean,
         modifier: Modifier = Modifier,
         activeColor: Color = MaterialTheme.colorScheme.primary,
         inactiveColor: Color = MaterialTheme.colorScheme.secondary,
     ) {
-        val anyTabFocused = LocalTabRowHasFocus.current
         val unfocusedUnderlineWidth = 10.dp
         val indicatorHeight = 2.dp
         val width by
         animateDpAsState(
-            targetValue = if (anyTabFocused) currentTabPosition.width else unfocusedUnderlineWidth,
+            targetValue =
+            if (isActivated)
+                currentTabPosition.width
+            else
+                unfocusedUnderlineWidth,
             label = "UnderlinedIndicator.width",
         )
         val leftOffset by
         animateDpAsState(
             targetValue =
-            if (anyTabFocused) {
+            if (isActivated) {
                 currentTabPosition.left
             } else {
                 val tabCenter = currentTabPosition.left + currentTabPosition.width / 2
@@ -271,7 +283,7 @@
 
         val underlineColor by
         animateColorAsState(
-            targetValue = if (anyTabFocused) activeColor else inactiveColor,
+            targetValue = if (isActivated) activeColor else inactiveColor,
             label = "UnderlinedIndicator.underlineColor",
         )
 
@@ -287,9 +299,6 @@
     }
 }
 
-/** A provider to store whether any [Tab] is focused inside the [TabRow] */
-internal val LocalTabRowHasFocus = compositionLocalOf { false }
-
 /** Slots for [TabRow]'s content */
 private enum class TabRowSlots {
     Tabs,
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/TabRowScope.kt b/tv/tv-material/src/main/java/androidx/tv/material3/TabRowScope.kt
new file mode 100644
index 0000000..9db88b3
--- /dev/null
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/TabRowScope.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.tv.material3
+
+/**
+ * [TabRowScope] is used to provide the isActivated state to the [Tab] composable
+ */
+@ExperimentalTvMaterial3Api // TODO (b/263353219): Remove this before launching beta
+interface TabRowScope {
+    /**
+     * Whether any tab within the [TabRow] is focused
+     */
+    val isActivated: Boolean
+}
+
+@OptIn(ExperimentalTvMaterial3Api::class)
+internal class TabRowScopeImpl internal constructor(
+    override val isActivated: Boolean
+) : TabRowScope
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/Text.kt b/tv/tv-material/src/main/java/androidx/tv/material3/Text.kt
index fff1438..af000b7 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/Text.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/Text.kt
@@ -86,6 +86,7 @@
  * functionality to the text. For example, to draw selection around the text.
  * @param style style configuration for the text such as color, font, line height etc.
  */
+@ExperimentalTvMaterial3Api
 @Composable
 fun Text(
     text: String,
@@ -183,6 +184,7 @@
  * functionality to the text. For example, to draw selection around the text.
  * @param style style configuration for the text such as color, font, line height etc.
  */
+@ExperimentalTvMaterial3Api
 @Composable
 fun Text(
     text: AnnotatedString,
@@ -238,6 +240,9 @@
  *
  * @see ProvideTextStyle
  */
+@Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
+@ExperimentalTvMaterial3Api
+@get:ExperimentalTvMaterial3Api
 val LocalTextStyle = compositionLocalOf(structuralEqualityPolicy()) { DefaultTextStyle }
 
 // TODO(b/156598010): remove this and replace with fold definition on the backing CompositionLocal
@@ -248,6 +253,7 @@
  *
  * @see LocalTextStyle
  */
+@ExperimentalTvMaterial3Api
 @Composable
 fun ProvideTextStyle(value: TextStyle, content: @Composable () -> Unit) {
     val mergedStyle = LocalTextStyle.current.merge(value)
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/Typography.kt b/tv/tv-material/src/main/java/androidx/tv/material3/Typography.kt
index 014694b..d1fd31f 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/Typography.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/Typography.kt
@@ -75,6 +75,7 @@
  * @property labelSmall labelSmall is one of the smallest font sizes. It is used sparingly to
  * annotate imagery or to introduce a headline.
  */
+@ExperimentalTvMaterial3Api
 @Immutable
 class Typography(
     val displayLarge: TextStyle = TypographyTokens.DisplayLarge,
@@ -185,6 +186,7 @@
 /**
  * Helper function for component typography tokens.
  */
+@OptIn(ExperimentalTvMaterial3Api::class)
 internal fun Typography.fromToken(value: TypographyKeyTokens): TextStyle {
     return when (value) {
         TypographyKeyTokens.DisplayLarge -> displayLarge
@@ -205,4 +207,5 @@
     }
 }
 
+@OptIn(ExperimentalTvMaterial3Api::class)
 internal val LocalTypography = staticCompositionLocalOf { Typography() }
diff --git a/wear/compose/compose-foundation/src/androidTest/kotlin/androidx/wear/compose/foundation/CurvedBoxTest.kt b/wear/compose/compose-foundation/src/androidTest/kotlin/androidx/wear/compose/foundation/CurvedBoxTest.kt
index cc730a6..55ac19f 100644
--- a/wear/compose/compose-foundation/src/androidTest/kotlin/androidx/wear/compose/foundation/CurvedBoxTest.kt
+++ b/wear/compose/compose-foundation/src/androidTest/kotlin/androidx/wear/compose/foundation/CurvedBoxTest.kt
@@ -21,6 +21,7 @@
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.size
+import androidx.compose.testutils.assertDoesNotContainColor
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.layout.LayoutCoordinates
diff --git a/wear/compose/compose-foundation/src/androidTest/kotlin/androidx/wear/compose/foundation/ExpandableTest.kt b/wear/compose/compose-foundation/src/androidTest/kotlin/androidx/wear/compose/foundation/ExpandableTest.kt
new file mode 100644
index 0000000..b79b17d
--- /dev/null
+++ b/wear/compose/compose-foundation/src/androidTest/kotlin/androidx/wear/compose/foundation/ExpandableTest.kt
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.foundation
+
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+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.size
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ImageBitmap
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.ComposeTestRule
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumnDefaults
+import androidx.wear.compose.foundation.lazy.ScalingLazyListState
+import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
+import org.junit.Assert.assertEquals
+import org.junit.Rule
+import org.junit.Test
+
+class ExpandableTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun initially_collapsed() =
+        verifyExpandable(
+            setupState = { rememberExpandableState(initiallyExpanded = false) },
+            bitmapAssert = {
+                assertDoesContainColor(COLLAPSED_COLOR)
+            }
+        )
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun initially_expanded() =
+        verifyExpandable(
+            setupState = { rememberExpandableState(initiallyExpanded = true) },
+            bitmapAssert = {
+                assertDoesContainColor(EXPANDED_COLOR)
+            }
+        )
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun expand() =
+        verifyExpandable(
+            setupState = { rememberExpandableState(initiallyExpanded = false) },
+            bitmapAssert = {
+                assertDoesContainColor(EXPANDED_COLOR)
+            }
+        ) { state ->
+            state.expanded = true
+            waitForIdle()
+        }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun collapse() =
+        verifyExpandable(
+            setupState = { rememberExpandableState(initiallyExpanded = true) },
+            bitmapAssert = {
+                assertDoesContainColor(COLLAPSED_COLOR)
+            }
+        ) { state ->
+            state.expanded = false
+            waitForIdle()
+        }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun collapsed_click() = verifyClick(false)
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun expanded_click() = verifyClick(true)
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    private fun verifyClick(initiallyExpanded: Boolean) {
+        val clicked = mutableListOf<Boolean>()
+        verifyExpandable(
+            setupState = { rememberExpandableState(initiallyExpanded = initiallyExpanded) },
+            bitmapAssert = {
+                assertEquals(listOf(initiallyExpanded), clicked)
+            },
+            expandableContent = { expanded ->
+                Box(modifier = Modifier.fillMaxSize().clickable {
+                    clicked.add(expanded)
+                })
+            }
+        ) { _ ->
+            onNodeWithTag(TEST_TAG).performClick()
+            waitForIdle()
+        }
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    private fun verifyExpandable(
+        setupState: @Composable () -> ExpandableState,
+        bitmapAssert: ImageBitmap.() -> Unit,
+        expandableContent: @Composable (Boolean) -> Unit = { },
+        act: ComposeTestRule.(ExpandableState) -> Unit = { }
+    ) {
+        // Arrange - set up the content for the test including expandable content
+        var slcState: ScalingLazyListState? = null
+        var state: ExpandableState? = null
+        rule.setContent {
+            state = setupState()
+            Box(
+                Modifier
+                    .testTag(TEST_TAG)
+                    .size(100.dp)) {
+                ScalingLazyColumn(
+                    state = rememberScalingLazyListState().also { slcState = it },
+                    // We can only test expandableItem inside a ScalingLazyColumn, but we can make
+                    // it behave mostly as it wasn't there.
+                    scalingParams = ScalingLazyColumnDefaults
+                        .scalingParams(edgeScale = 1f, edgeAlpha = 1f),
+                    autoCentering = null,
+                    verticalArrangement = Arrangement.spacedBy(space = 0.dp),
+                ) {
+                    expandableItem(state!!) { expanded ->
+                        Box(
+                            Modifier
+                                .fillMaxWidth()
+                                .height(100.dp)
+                                .background(
+                                    if (expanded) EXPANDED_COLOR else COLLAPSED_COLOR
+                                )
+                        ) {
+                            expandableContent(expanded)
+                        }
+                    }
+                }
+            }
+        }
+        rule.waitUntil { slcState?.initialized?.value ?: false }
+
+        // Act - exercise the expandable if required for the test.
+        with(rule) {
+            act(state!!)
+        }
+
+        // Assert - verify the object under test worked correctly
+        rule.onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .apply { bitmapAssert() }
+    }
+
+    private val EXPANDED_COLOR = Color.Red
+    private val COLLAPSED_COLOR = Color.Green
+}
diff --git a/wear/compose/compose-foundation/src/androidTest/kotlin/androidx/wear/compose/foundation/FoundationTest.kt b/wear/compose/compose-foundation/src/androidTest/kotlin/androidx/wear/compose/foundation/FoundationTest.kt
index 0da21e5..f781f3a 100644
--- a/wear/compose/compose-foundation/src/androidTest/kotlin/androidx/wear/compose/foundation/FoundationTest.kt
+++ b/wear/compose/compose-foundation/src/androidTest/kotlin/androidx/wear/compose/foundation/FoundationTest.kt
@@ -35,12 +35,25 @@
 
 /**
  * Checks whether [expectedColor] does not exist in current [ImageBitmap]
- */
+ *
 fun ImageBitmap.assertDoesNotContainColor(expectedColor: Color) {
     val histogram = histogram()
     if (histogram.containsKey(expectedColor)) {
         throw AssertionError("Expected color $expectedColor exists in current bitmap")
     }
+}*/
+
+/**
+ * Checks whether [expectedColor] exist in current [ImageBitmap], covering at least the given ratio
+ * of the image
+ */
+fun ImageBitmap.assertDoesContainColor(expectedColor: Color, expectedRatio: Float = 0.75f) {
+    val histogram = histogram()
+    val ratio = (histogram.getOrDefault(expectedColor, 0L)).toFloat() / (width * height)
+    if (ratio < expectedRatio) {
+        throw AssertionError("Expected color $expectedColor with ratio $expectedRatio." +
+            " Actual ratio = $ratio")
+    }
 }
 
 private fun ImageBitmap.histogram(): MutableMap<Color, Long> {
diff --git a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/Expandable.kt b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/Expandable.kt
index db910b1..20ebcae 100644
--- a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/Expandable.kt
+++ b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/Expandable.kt
@@ -199,8 +199,14 @@
             val off1 = (width - placeables[1].width) / 2
 
             layout(width, height) {
-                placeables[0].placeWithLayer(off0, 0) { alpha = 1 - progress }
-                placeables[1].placeWithLayer(off1, 0) { alpha = progress }
+                if (progress < 1f) {
+                    placeables[0].placeWithLayer(off0, 0, zIndex = 1 - progress) {
+                        alpha = 1 - progress
+                    }
+                }
+                if (progress > 0f) {
+                    placeables[1].placeWithLayer(off1, 0, zIndex = progress) { alpha = progress }
+                }
             }
         }
     }
diff --git a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/SwipeToReveal.kt b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/SwipeToReveal.kt
index 68cba72..da7d674 100644
--- a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/SwipeToReveal.kt
+++ b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/SwipeToReveal.kt
@@ -294,7 +294,7 @@
             ) { value, layoutSize ->
                 val swipeableWidth = layoutSize.width.toFloat()
                 // Update the total width which will be used to calculate the anchors
-                revealScope.width.value = swipeableWidth
+                revealScope.width.floatValue = swipeableWidth
                 // Multiply the anchor with -1f to get the actual swipeable anchor
                 -state.swipeAnchors[value]!! * swipeableWidth
             }
@@ -400,7 +400,7 @@
     val width = mutableFloatStateOf(0.0f)
 
     override val revealOffset: Float
-        get() = width.value * (revealState.swipeAnchors[RevealValue.Revealing] ?: 0.0f)
+        get() = width.floatValue * (revealState.swipeAnchors[RevealValue.Revealing] ?: 0.0f)
 }
 
 /**
diff --git a/wear/compose/compose-material-core/src/androidTest/kotlin/androidx/wear/compose/materialcore/RepeatableClickable.kt b/wear/compose/compose-material-core/src/androidTest/kotlin/androidx/wear/compose/materialcore/RepeatableClickable.kt
new file mode 100644
index 0000000..545a576
--- /dev/null
+++ b/wear/compose/compose-material-core/src/androidTest/kotlin/androidx/wear/compose/materialcore/RepeatableClickable.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.materialcore
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performTouchInput
+import org.junit.Assert.assertEquals
+import org.junit.Rule
+import org.junit.Test
+
+public class RepeatableClickable {
+    @get:Rule
+    public val rule = createComposeRule()
+
+    @Test
+    fun touch_hold_shorter_than_threshold() {
+        var clickCounter = 0
+
+        boxWithRepeatableClickable(rule, 300) {
+            clickCounter++
+        }
+
+        assertEquals(0, clickCounter)
+    }
+
+    @Test
+    fun touch_hold_equals_to_threshold() {
+        var clickCounter = 0
+
+        boxWithRepeatableClickable(rule, 500) {
+            clickCounter++
+        }
+
+        assertEquals(1, clickCounter)
+    }
+
+    @Test
+    fun touch_hold_longer_than_threshold() {
+        var clickCounter = 0
+
+        boxWithRepeatableClickable(rule, 620) {
+            clickCounter++
+        }
+
+        assertEquals(3, clickCounter)
+    }
+
+    @Test
+    fun touch_hold_disabled() {
+        var clickCounter = 0
+
+        boxWithRepeatableClickable(rule, 500, false) {
+            clickCounter++
+        }
+
+        assertEquals(0, clickCounter)
+    }
+
+    private fun boxWithRepeatableClickable(
+        rule: ComposeContentTestRule,
+        holdDelay: Long,
+        enabled: Boolean = true,
+        initialDelay: Long = 500L,
+        incrementalDelay: Long = 60L,
+        onClick: () -> Unit
+    ) {
+
+        rule.setContent {
+            Box(
+                modifier = Modifier
+                    .testTag(TEST_TAG)
+                    .repeatableClickable(
+                        enabled = enabled,
+                        initialDelay = initialDelay,
+                        incrementalDelay = incrementalDelay
+                    ) {
+                        onClick()
+                    }
+            ) {}
+        }
+
+        rule.onNodeWithTag(TEST_TAG).performTouchInput {
+            down(center)
+            advanceEventTime(holdDelay)
+            up()
+        }
+    }
+}
diff --git a/wear/compose/compose-material-core/src/androidTest/kotlin/androidx/wear/compose/materialcore/SelectionControlsTest.kt b/wear/compose/compose-material-core/src/androidTest/kotlin/androidx/wear/compose/materialcore/SelectionControlsTest.kt
new file mode 100644
index 0000000..3e64acd
--- /dev/null
+++ b/wear/compose/compose-material-core/src/androidTest/kotlin/androidx/wear/compose/materialcore/SelectionControlsTest.kt
@@ -0,0 +1,1114 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.materialcore
+
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.compose.animation.animateColorAsState
+import androidx.compose.animation.core.CubicBezierEasing
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.testutils.assertContainsColor
+import androidx.compose.testutils.assertDoesNotContainColor
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.test.SemanticsMatcher
+import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.assertHasClickAction
+import androidx.compose.ui.test.assertHasNoClickAction
+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.assertIsOff
+import androidx.compose.ui.test.assertIsOn
+import androidx.compose.ui.test.assertIsSelected
+import androidx.compose.ui.test.assertWidthIsEqualTo
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.isSelectable
+import androidx.compose.ui.test.isToggleable
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import org.junit.Rule
+import org.junit.Test
+
+class SelectionControlsTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    // Checkbox colors
+    private val boxColorChecked = Color.Green
+    private val boxColorUnchecked = Color.Blue
+    private val checkmarkColorChecked = Color.Red
+    private val checkmarkColorUnchecked = Color.Yellow
+    private val boxColorDisabledChecked = Color.Cyan
+    private val boxColorDisabledUnchecked = Color.Magenta
+    private val checkmarkColorDisabledChecked = Color.DarkGray
+    private val checkmarkColorDisabledUnchecked = Color.White
+
+    // Switch colors
+    private val trackColorChecked = Color.Green
+    private val trackColorUnchecked = Color.Blue
+    private val trackColorDisabledChecked = Color.Cyan
+    private val trackColorDisabledUnchecked = Color.Magenta
+    private val trackStrokeColorChecked = Color.Red
+    private val trackStrokeColorUnchecked = Color.Yellow
+    private val trackStrokeColorDisabledChecked = Color.DarkGray
+    private val trackStrokeColorDisabledUnchecked = Color.White
+    private val thumbColorChecked = Color(0xFFA020F0)
+    private val thumbColorUnchecked = Color(0xFFFFA500)
+    private val thumbColorDisabledChecked = Color(0xFFA56D61)
+    private val thumbColorDisabledUnchecked = Color(0xFF904332)
+    private val thumbIconColorChecked = Color(0xFF0000FF)
+    private val thumbIconColorUnchecked = Color(0xFF808000)
+    private val thumbIconColorDisabledChecked = Color(0xFFCCCCFF)
+    private val thumbIconColorDisabledUnchecked = Color(0xFFE3F48D)
+
+    // Radio button colors
+    private val radioRingChecked = Color.Green
+    private val radioRingUnchecked = Color.Blue
+    private val radioDotChecked = Color.Red
+    private val radioDotUnchecked = Color.Yellow
+    private val radioRingDisabledChecked = Color.Cyan
+    private val radioRingDisabledUnchecked = Color.Magenta
+    private val radioDotDisabledChecked = Color.DarkGray
+    private val radioDotDisabledUnchecked = Color.White
+
+    @Test
+    fun checkbox_supports_testtag() {
+        rule.setContent {
+            CheckboxWithDefaults(
+                checked = true, modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertExists()
+    }
+
+    @Test
+    fun checkbox_customize_canvas_size() {
+        val width = 32.dp
+        val height = 26.dp
+
+        rule.setContentForSizeAssertions {
+            CheckboxWithDefaults(
+                checked = true,
+                modifier = Modifier.testTag(TEST_TAG),
+                width = width,
+                height = height
+            )
+        }.assertHeightIsEqualTo(height).assertWidthIsEqualTo(width)
+    }
+
+    @Test
+    fun checkbox_has_role_checkbox_when_oncheckedchange_defined() {
+        rule.setContent {
+            CheckboxWithDefaults(
+                checked = true, onCheckedChange = {}, modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assert(
+            SemanticsMatcher.expectValue(
+                SemanticsProperties.Role, Role.Checkbox
+            )
+        )
+    }
+
+    @Test
+    fun checkbox_can_override_role() {
+        rule.setContent {
+            CheckboxWithDefaults(
+                checked = true,
+                onCheckedChange = {},
+                modifier = Modifier
+                    .testTag(TEST_TAG)
+                    .semantics {
+                        role = Role.Image
+                    }
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assert(
+            SemanticsMatcher.expectValue(
+                SemanticsProperties.Role, Role.Image
+            )
+        )
+    }
+
+    @Test
+    fun checkbox_has_no_clickaction_by_default() {
+        rule.setContent {
+            CheckboxWithDefaults(
+                checked = true, enabled = true, modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertHasNoClickAction()
+    }
+
+    @Test
+    fun checkbox_has_clickaction_when_oncheckedchange_defined() {
+        rule.setContent {
+            CheckboxWithDefaults(
+                checked = true,
+                enabled = true,
+                onCheckedChange = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertHasClickAction()
+    }
+
+    @Test
+    fun checkbox_is_toggleable_when_oncheckedchange_defined() {
+        rule.setContent {
+            CheckboxWithDefaults(
+                checked = true,
+                enabled = true,
+                onCheckedChange = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNode(isToggleable()).assertExists()
+    }
+
+    @Test
+    fun checkbox_is_correctly_enabled() {
+        rule.setContent {
+            CheckboxWithDefaults(
+                checked = true, enabled = true, modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsEnabled()
+    }
+
+    @Test
+    fun checkbox_is_correctly_disabled() {
+        // This test only applies when onCheckedChange is defined.
+        rule.setContent {
+            CheckboxWithDefaults(
+                checked = true,
+                enabled = false,
+                onCheckedChange = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsNotEnabled()
+    }
+
+    @Test
+    fun checkbox_is_on_when_checked() {
+        // This test only applies when onCheckedChange is defined.
+        rule.setContent {
+            CheckboxWithDefaults(
+                checked = true, onCheckedChange = {}, modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsOn()
+    }
+
+    @Test
+    fun checkbox_is_off_when_checked() {
+        // This test only applies when onCheckedChange is defined.
+        rule.setContent {
+            CheckboxWithDefaults(
+                checked = false, onCheckedChange = {}, modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsOff()
+    }
+
+    @Test
+    fun checkbox_responds_to_toggle_on() {
+        // This test only applies when onCheckedChange is defined.
+        rule.setContent {
+            val (checked, onCheckedChange) = remember { mutableStateOf(false) }
+            CheckboxWithDefaults(
+                checked = checked,
+                onCheckedChange = onCheckedChange,
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsOff().performClick()
+            .assertIsOn()
+    }
+
+    @Test
+    fun checkbox_responds_to_toggle_off() {
+        // This test only applies when onCheckedChange is defined.
+        rule.setContent {
+            val (checked, onCheckedChange) = remember { mutableStateOf(true) }
+            CheckboxWithDefaults(
+                checked = checked,
+                onCheckedChange = onCheckedChange,
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsOn().performClick()
+            .assertIsOff()
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun checkbox_enabled_checked_colors_are_customisable() {
+        setupCheckBoxWithCustomColors(enabled = true, checked = true)
+
+        val checkboxImage = rule.onNodeWithTag(TEST_TAG).captureToImage()
+        checkboxImage.assertContainsColor(boxColorChecked)
+        checkboxImage.assertContainsColor(checkmarkColorChecked)
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun checkbox_enabled_unchecked_colors_are_customisable() {
+        setupCheckBoxWithCustomColors(enabled = true, checked = false)
+
+        val checkboxImage = rule.onNodeWithTag(TEST_TAG).captureToImage()
+        checkboxImage.assertContainsColor(boxColorUnchecked)
+        checkboxImage.assertDoesNotContainColor(checkmarkColorChecked)
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun checkbox_disabled_checked_colors_are_customisable() {
+        setupCheckBoxWithCustomColors(enabled = false, checked = true)
+
+        val checkboxImage = rule.onNodeWithTag(TEST_TAG).captureToImage()
+        checkboxImage.assertContainsColor(boxColorDisabledChecked)
+        checkboxImage.assertContainsColor(checkmarkColorDisabledChecked)
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun checkbox_disabled_unchecked_colors_are_customisable() {
+        setupCheckBoxWithCustomColors(enabled = false, checked = false)
+
+        val checkboxImage = rule.onNodeWithTag(TEST_TAG).captureToImage()
+        checkboxImage.assertContainsColor(boxColorDisabledUnchecked)
+        checkboxImage.assertDoesNotContainColor(checkmarkColorDisabledChecked)
+    }
+
+    @Test
+    fun switch_supports_testtag() {
+        rule.setContent {
+            SwitchWithDefaults(
+                checked = true,
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertExists()
+    }
+
+    @Test
+    fun switch_has_role_switch_when_oncheckedchange_defined() {
+        rule.setContent {
+            SwitchWithDefaults(
+                checked = true,
+                onCheckedChange = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG)
+            .assert(
+                SemanticsMatcher.expectValue(
+                    SemanticsProperties.Role,
+                    Role.Switch
+                )
+            )
+    }
+
+    @Test
+    fun switch_can_override_role() {
+        rule.setContent {
+            SwitchWithDefaults(
+                checked = true,
+                onCheckedChange = {},
+                modifier = Modifier
+                    .testTag(TEST_TAG)
+                    .semantics {
+                        role = Role.Image
+                    }
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG)
+            .assert(
+                SemanticsMatcher.expectValue(
+                    SemanticsProperties.Role,
+                    Role.Image
+                )
+            )
+    }
+
+    @Test
+    fun switch_has_no_clickaction_by_default() {
+        rule.setContent {
+            SwitchWithDefaults(
+                checked = true,
+                enabled = true,
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertHasNoClickAction()
+    }
+
+    @Test
+    fun switch_has_clickaction_when_oncheckedchange_defined() {
+        rule.setContent {
+            SwitchWithDefaults(
+                checked = true,
+                enabled = true,
+                onCheckedChange = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertHasClickAction()
+    }
+
+    @Test
+    fun switch_is_toggleable_when_oncheckedchange_defined() {
+        rule.setContent {
+            SwitchWithDefaults(
+                checked = true,
+                enabled = true,
+                onCheckedChange = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNode(isToggleable()).assertExists()
+    }
+
+    @Test
+    fun switch_is_correctly_enabled() {
+        rule.setContent {
+            SwitchWithDefaults(
+                checked = true,
+                enabled = true,
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsEnabled()
+    }
+
+    @Test
+    fun switch_is_correctly_disabled() {
+        rule.setContent {
+            SwitchWithDefaults(
+                checked = true,
+                enabled = false,
+                onCheckedChange = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsNotEnabled()
+    }
+
+    @Test
+    fun switch_is_on_when_checked() {
+        // This test only applies when onCheckedChange is defined.
+        rule.setContent {
+            SwitchWithDefaults(
+                checked = true,
+                onCheckedChange = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsOn()
+    }
+
+    @Test
+    fun switch_is_off_when_checked() {
+        // This test only applies when onCheckedChange is defined.
+        rule.setContent {
+            SwitchWithDefaults(
+                checked = false,
+                onCheckedChange = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsOff()
+    }
+
+    @Test
+    fun switch_responds_to_toggle_on() {
+        // This test only applies when onCheckedChange is defined.
+        rule.setContent {
+            val (checked, onCheckedChange) = remember { mutableStateOf(false) }
+            SwitchWithDefaults(
+                checked = checked,
+                onCheckedChange = onCheckedChange,
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .assertIsOff()
+            .performClick()
+            .assertIsOn()
+    }
+
+    @Test
+    fun switch_responds_to_toggle_off() {
+        // This test only applies when onCheckedChange is defined.
+        rule.setContent {
+            val (checked, onCheckedChange) = remember { mutableStateOf(true) }
+            SwitchWithDefaults(
+                checked = checked,
+                onCheckedChange = onCheckedChange,
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .assertIsOn()
+            .performClick()
+            .assertIsOff()
+    }
+
+    @Test
+    fun switch_customize_canvas_size() {
+        val width = 34.dp
+        val height = 26.dp
+
+        rule.setContentForSizeAssertions {
+            CheckboxWithDefaults(
+                checked = true,
+                modifier = Modifier.testTag(TEST_TAG),
+                width = width,
+                height = height
+            )
+        }.assertHeightIsEqualTo(height).assertWidthIsEqualTo(width)
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun switch_enabled_checked_colors_are_customisable() {
+        setupSwitchWithCustomColors(enabled = true, checked = true)
+
+        val image = rule.onNodeWithTag(TEST_TAG).captureToImage()
+
+        image.assertContainsColor(trackColorChecked)
+        image.assertContainsColor(trackStrokeColorChecked)
+        image.assertContainsColor(thumbColorChecked)
+        image.assertContainsColor(thumbIconColorChecked)
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun switch_enabled_unchecked_colors_are_customisable() {
+        setupSwitchWithCustomColors(enabled = true, checked = false)
+
+        val image = rule.onNodeWithTag(TEST_TAG).captureToImage()
+
+        image.assertContainsColor(trackColorUnchecked)
+        image.assertContainsColor(trackStrokeColorUnchecked)
+        image.assertContainsColor(thumbColorUnchecked)
+        image.assertContainsColor(thumbIconColorUnchecked)
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun switch_disabled_checked_colors_are_customisable() {
+        setupSwitchWithCustomColors(enabled = false, checked = true)
+
+        val image = rule.onNodeWithTag(TEST_TAG).captureToImage()
+
+        image.assertContainsColor(trackColorDisabledChecked)
+        image.assertContainsColor(trackStrokeColorDisabledChecked)
+        image.assertContainsColor(thumbColorDisabledChecked)
+        image.assertContainsColor(thumbIconColorDisabledChecked)
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun switch_disabled_unchecked_colors_are_customisable() {
+        setupSwitchWithCustomColors(enabled = false, checked = false)
+
+        val image = rule.onNodeWithTag(TEST_TAG).captureToImage()
+
+        image.assertContainsColor(trackColorDisabledUnchecked)
+        image.assertContainsColor(trackStrokeColorDisabledUnchecked)
+        image.assertContainsColor(thumbColorDisabledUnchecked)
+        image.assertContainsColor(thumbIconColorDisabledUnchecked)
+    }
+
+    @Test
+    fun radiobutton_supports_testtag() {
+        rule.setContent {
+            RadioButtonWithDefaults(
+                modifier = Modifier.testTag(TEST_TAG),
+                selected = true
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertExists()
+    }
+
+    @Test
+    fun radiobutton_is_expected_size() {
+        val width = 30.dp
+        val height = 26.dp
+
+        rule.setContentForSizeAssertions {
+            RadioButtonWithDefaults(
+                modifier = Modifier.testTag(TEST_TAG),
+                selected = true,
+                width = width,
+                height = height
+            )
+        }.assertHeightIsEqualTo(height).assertWidthIsEqualTo(width)
+    }
+
+    @Test
+    fun radiobutton_has_role_radiobutton_when_onclick_defined() {
+        rule.setContent {
+            RadioButtonWithDefaults(
+                modifier = Modifier.testTag(TEST_TAG),
+                selected = true,
+                onClick = {}
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG)
+            .assert(
+                SemanticsMatcher.expectValue(
+                    SemanticsProperties.Role,
+                    Role.RadioButton
+                )
+            )
+    }
+
+    @Test
+    fun radiobutton_can_override_role() {
+        rule.setContent {
+            RadioButtonWithDefaults(
+                modifier = Modifier
+                    .testTag(TEST_TAG)
+                    .semantics {
+                        role = Role.Image
+                    },
+                selected = true,
+                onClick = {}
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG)
+            .assert(
+                SemanticsMatcher.expectValue(
+                    SemanticsProperties.Role,
+                    Role.Image
+                )
+            )
+    }
+
+    @Test
+    fun radiobutton_has_no_clickaction_by_default() {
+        rule.setContent {
+            RadioButtonWithDefaults(
+                modifier = Modifier.testTag(TEST_TAG),
+                selected = true,
+                enabled = true
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertHasNoClickAction()
+    }
+
+    @Test
+    fun radiobutton_has_clickaction_when_onclick_defined() {
+        rule.setContent {
+            RadioButtonWithDefaults(
+                modifier = Modifier.testTag(TEST_TAG),
+                selected = true,
+                enabled = true,
+                onClick = {},
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertHasClickAction()
+    }
+
+    @Test
+    fun radiobutton_is_selectable_when_onclick_defined() {
+        rule.setContent {
+            RadioButtonWithDefaults(
+                modifier = Modifier.testTag(TEST_TAG),
+                selected = true,
+                enabled = true,
+                onClick = {},
+            )
+        }
+
+        rule.onNode(isSelectable()).assertExists()
+    }
+
+    @Test
+    fun radiobutton_is_correctly_enabled() {
+        rule.setContent {
+            RadioButtonWithDefaults(
+                modifier = Modifier.testTag(TEST_TAG),
+                selected = true,
+                enabled = true,
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsEnabled()
+    }
+
+    @Test
+    fun radiobutton_is_correctly_disabled() {
+        // This test only applies when onClick is provided and the RadioButton itself is selectable.
+        rule.setContent {
+            RadioButtonWithDefaults(
+                modifier = Modifier.testTag(TEST_TAG),
+                selected = true,
+                enabled = false,
+                onClick = {}
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsNotEnabled()
+    }
+
+    @Test
+    fun radiobutton_is_on_when_checked() {
+        // This test only applies when onClick is provided and the RadioButton itself is selectable.
+        rule.setContent {
+            RadioButtonWithDefaults(
+                modifier = Modifier.testTag(TEST_TAG),
+                selected = true,
+                onClick = {}
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsSelected()
+    }
+
+    @Test
+    fun radiobutton_is_off_when_checked() {
+        // This test only applies when onClick is provided and the RadioButton itself is selectable.
+        rule.setContent {
+            RadioButtonWithDefaults(
+                modifier = Modifier.testTag(TEST_TAG),
+                selected = false,
+                onClick = {}
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsNotSelected()
+    }
+
+    @Test
+    fun radiobutton_responds_to_toggle_on() {
+        // This test only applies when onCheckedChange is defined.
+        rule.setContent {
+            var selected by remember { mutableStateOf(false) }
+
+            RadioButtonWithDefaults(
+                modifier = Modifier.testTag(TEST_TAG),
+                selected = selected,
+                onClick = { selected = !selected }
+            )
+        }
+
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .assertIsNotSelected()
+            .performClick()
+            .assertIsSelected()
+    }
+
+    @Test
+    fun radiobutton_responds_to_toggle_off() {
+        // This test only applies when onCheckedChange is defined.
+        rule.setContent {
+            var selected by remember { mutableStateOf(true) }
+
+            RadioButtonWithDefaults(
+                modifier = Modifier.testTag(TEST_TAG),
+                selected = selected,
+                onClick = { selected = !selected }
+            )
+        }
+
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .assertIsSelected()
+            .performClick()
+            .assertIsNotSelected()
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun radiobutton_enabled_checked_colors_are_customisable() {
+        setupRadioButtonWithCustomColors(enabled = true, selected = true)
+
+        val radioImage = rule.onNodeWithTag(TEST_TAG).captureToImage()
+        radioImage.assertContainsColor(radioRingChecked)
+        radioImage.assertContainsColor(radioDotChecked)
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun radiobutton_enabled_unchecked_colors_are_customisable() {
+        setupRadioButtonWithCustomColors(enabled = true, selected = false)
+
+        val radioImage = rule.onNodeWithTag(TEST_TAG).captureToImage()
+        radioImage.assertContainsColor(radioRingUnchecked)
+        radioImage.assertDoesNotContainColor(radioDotUnchecked)
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun radiobutton_disabled_checked_colors_are_customisable() {
+        setupRadioButtonWithCustomColors(enabled = false, selected = true)
+
+        val radioImage = rule.onNodeWithTag(TEST_TAG).captureToImage()
+        radioImage.assertContainsColor(radioRingDisabledChecked)
+        radioImage.assertContainsColor(radioDotDisabledChecked)
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun radiobutton_disabled_unchecked_colors_are_customisable() {
+        setupRadioButtonWithCustomColors(enabled = false, selected = false)
+
+        val radioImage = rule.onNodeWithTag(TEST_TAG).captureToImage()
+        radioImage.assertContainsColor(radioRingDisabledUnchecked)
+        radioImage.assertDoesNotContainColor(radioDotDisabledUnchecked)
+    }
+
+    @Composable
+    private fun CheckboxWithDefaults(
+        modifier: Modifier = Modifier,
+        checked: Boolean = true,
+        boxColor: @Composable (enabled: Boolean, checked: Boolean) -> State<Color> =
+            { isEnabled, isChecked ->
+                selectionControlColor(
+                    isEnabled, isChecked,
+                    Color.Blue, Color.Red, Color.Green, Color.Gray
+                )
+            },
+        checkmarkColor: @Composable (enabled: Boolean, checked: Boolean) -> State<Color> =
+            { isEnabled, isChecked ->
+                selectionControlColor(
+                    isEnabled, isChecked,
+                    Color.Cyan, Color.Magenta, Color.White, Color.Yellow
+                )
+            },
+        enabled: Boolean = true,
+        onCheckedChange: ((Boolean) -> Unit)? = null,
+        interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+        drawBox: FunctionDrawBox = FunctionDrawBox { _, _, _ -> },
+        width: Dp = 24.dp,
+        height: Dp = 24.dp
+    ) = Checkbox(
+        checked = checked,
+        modifier = modifier,
+        boxColor = boxColor,
+        checkmarkColor = checkmarkColor,
+        enabled = enabled,
+        onCheckedChange = onCheckedChange,
+        interactionSource = interactionSource,
+        drawBox = drawBox,
+        progressAnimationSpec =
+        tween(200, 0, CubicBezierEasing(0.0f, 0.0f, 0.2f, 1.0f)),
+        width = width,
+        height = height
+    )
+
+    @Composable
+    private fun SwitchWithDefaults(
+        modifier: Modifier = Modifier,
+        checked: Boolean = true,
+        enabled: Boolean = true,
+        onCheckedChange: ((Boolean) -> Unit)? = null,
+        interactionSource: MutableInteractionSource = remember {
+            MutableInteractionSource()
+        },
+        trackFillColor: @Composable (enabled: Boolean, checked: Boolean) -> State<Color> =
+            { isEnabled, isChecked ->
+                selectionControlColor(
+                    isEnabled, isChecked,
+                    Color.Blue, Color.Red, Color.Green, Color.Gray
+                )
+            },
+        trackStrokeColor: @Composable (enabled: Boolean, checked: Boolean) -> State<Color> =
+            { isEnabled, isChecked ->
+                selectionControlColor(
+                    isEnabled, isChecked,
+                    Color.Blue, Color.Red, Color.Green, Color.Gray
+                )
+            },
+        thumbColor: @Composable (enabled: Boolean, checked: Boolean) -> State<Color> =
+            { isEnabled, isChecked ->
+                selectionControlColor(
+                    isEnabled, isChecked,
+                    Color.Cyan, Color.Magenta, Color.White, Color.Yellow
+                )
+            },
+        thumbIconColor: @Composable (enabled: Boolean, checked: Boolean) -> State<Color> =
+            { isEnabled, isChecked ->
+                selectionControlColor(
+                    isEnabled, isChecked,
+                    Color.Cyan, Color.Magenta, Color.White, Color.Yellow
+                )
+            },
+        trackWidth: Dp = 32.dp,
+        trackHeight: Dp = 24.dp,
+        drawThumb: FunctionDrawThumb = FunctionDrawThumb { _, _, _, _, _ -> },
+        width: Dp = 32.dp,
+        height: Dp = 24.dp
+    ) = Switch(
+        checked = checked,
+        modifier = modifier,
+        enabled = enabled,
+        onCheckedChange = onCheckedChange,
+        interactionSource = interactionSource,
+        trackFillColor = trackFillColor,
+        trackStrokeColor = trackStrokeColor,
+        thumbColor = thumbColor,
+        thumbIconColor = thumbIconColor,
+        trackWidth = trackWidth,
+        trackHeight = trackHeight,
+        drawThumb = drawThumb,
+        progressAnimationSpec =
+        tween(150, 0, CubicBezierEasing(0.0f, 0.0f, 0.2f, 1.0f)),
+        width = width,
+        height = height
+    )
+
+    @Composable
+    private fun RadioButtonWithDefaults(
+        modifier: Modifier = Modifier,
+        selected: Boolean = true,
+        enabled: Boolean = true,
+        ringColor: @Composable (enabled: Boolean, checked: Boolean) -> State<Color> =
+            { isEnabled, isChecked ->
+                selectionControlColor(
+                    isEnabled, isChecked,
+                    Color.Blue, Color.Red, Color.Green, Color.Gray
+                )
+            },
+        dotColor: @Composable (enabled: Boolean, checked: Boolean) -> State<Color> =
+            { isEnabled, isChecked ->
+                selectionControlColor(
+                    isEnabled, isChecked,
+                    Color.Blue, Color.Red, Color.Green, Color.Gray
+                )
+            },
+        onClick: (() -> Unit)? = null,
+        interactionSource: MutableInteractionSource = remember {
+            MutableInteractionSource()
+        },
+        dotRadiusProgressDuration: FunctionDotRadiusProgressDuration =
+            FunctionDotRadiusProgressDuration { _ -> 200 },
+        dotAlphaProgressDuration: Int = 200,
+        dotAlphaProgressDelay: Int = 100,
+        progressAnimationEasing: CubicBezierEasing =
+            CubicBezierEasing(0.0f, 0.0f, 0.2f, 1.0f),
+        width: Dp = 32.dp,
+        height: Dp = 24.dp
+    ) = RadioButton(
+        modifier = modifier,
+        selected = selected,
+        enabled = enabled,
+        ringColor = ringColor,
+        dotColor = dotColor,
+        onClick = onClick,
+        interactionSource = interactionSource,
+        dotRadiusProgressDuration = dotRadiusProgressDuration,
+        dotAlphaProgressDuration = dotAlphaProgressDuration,
+        dotAlphaProgressDelay = dotAlphaProgressDelay,
+        easing = progressAnimationEasing,
+        width = width,
+        height = height
+    )
+
+    private fun setupCheckBoxWithCustomColors(checked: Boolean, enabled: Boolean) {
+        rule.setContent {
+            CheckboxWithDefaults(checked = checked,
+                enabled = enabled,
+                modifier = Modifier.testTag(TEST_TAG),
+                boxColor = { enabled, checked ->
+                    selectionControlColor(
+                        enabled = enabled,
+                        checked = checked,
+                        checkedColor = boxColorChecked,
+                        uncheckedColor = boxColorUnchecked,
+                        disabledCheckedColor = boxColorDisabledChecked,
+                        disabledUncheckedColor = boxColorDisabledUnchecked
+                    )
+                },
+                checkmarkColor = { enabled, checked ->
+                    selectionControlColor(
+                        enabled = enabled,
+                        checked = checked,
+                        checkedColor = checkmarkColorChecked,
+                        uncheckedColor = checkmarkColorUnchecked,
+                        disabledCheckedColor = checkmarkColorDisabledChecked,
+                        disabledUncheckedColor = checkmarkColorDisabledUnchecked
+                    )
+                },
+                drawBox = FunctionDrawBox { drawScope, color, _ ->
+                    drawScope.drawRoundRect(color)
+                })
+        }
+    }
+
+    private fun setupSwitchWithCustomColors(checked: Boolean, enabled: Boolean) {
+        rule.setContent {
+            SwitchWithDefaults(
+                checked = checked,
+                enabled = enabled,
+                modifier = Modifier.testTag(TEST_TAG),
+                trackFillColor = { enabled, checked ->
+                    selectionControlColor(
+                        enabled = enabled,
+                        checked = checked,
+                        checkedColor = trackColorChecked,
+                        uncheckedColor = trackColorUnchecked,
+                        disabledCheckedColor = trackColorDisabledChecked,
+                        disabledUncheckedColor = trackColorDisabledUnchecked
+                    )
+                },
+                trackStrokeColor = { enabled, checked ->
+                    selectionControlColor(
+                        enabled = enabled,
+                        checked = checked,
+                        checkedColor = trackStrokeColorChecked,
+                        uncheckedColor = trackStrokeColorUnchecked,
+                        disabledCheckedColor = trackStrokeColorDisabledChecked,
+                        disabledUncheckedColor = trackStrokeColorDisabledUnchecked
+                    )
+                },
+                thumbColor = { enabled, checked ->
+                    selectionControlColor(
+                        enabled = enabled,
+                        checked = checked,
+                        checkedColor = thumbColorChecked,
+                        uncheckedColor = thumbColorUnchecked,
+                        disabledCheckedColor = thumbColorDisabledChecked,
+                        disabledUncheckedColor = thumbColorDisabledUnchecked
+                    )
+                },
+                thumbIconColor = { enabled, checked ->
+                    selectionControlColor(
+                        enabled = enabled,
+                        checked = checked,
+                        checkedColor = thumbIconColorChecked,
+                        uncheckedColor = thumbIconColorUnchecked,
+                        disabledCheckedColor = thumbIconColorDisabledChecked,
+                        disabledUncheckedColor = thumbIconColorDisabledUnchecked
+                    )
+                },
+                drawThumb = FunctionDrawThumb { drawScope, thumbColor, _, thumbIconColor, _ ->
+                    // drawing
+                    drawScope.drawCircle(
+                        color = thumbColor,
+                        radius = with(drawScope) { 10.dp.toPx() }
+                    )
+                    // drawing thumb icon
+                    drawScope.drawCircle(
+                        color = thumbIconColor,
+                        radius = with(drawScope) { 5.dp.toPx() }
+                    )
+                },
+            )
+        }
+    }
+
+    private fun setupRadioButtonWithCustomColors(selected: Boolean, enabled: Boolean) {
+        rule.setContent {
+            RadioButtonWithDefaults(
+                modifier = Modifier.testTag(TEST_TAG),
+                selected = selected,
+                enabled = enabled,
+                ringColor = { enabled, checked ->
+                    selectionControlColor(
+                        enabled = enabled,
+                        checked = checked,
+                        checkedColor = radioRingChecked,
+                        uncheckedColor = radioRingUnchecked,
+                        disabledCheckedColor = radioRingDisabledChecked,
+                        disabledUncheckedColor = radioRingDisabledUnchecked
+                    )
+                },
+                dotColor = { enabled, checked ->
+                    selectionControlColor(
+                        enabled = enabled,
+                        checked = checked,
+                        checkedColor = radioDotChecked,
+                        uncheckedColor = radioDotUnchecked,
+                        disabledCheckedColor = radioDotDisabledChecked,
+                        disabledUncheckedColor = radioDotDisabledUnchecked
+                    )
+                }
+            )
+        }
+    }
+
+    @Composable
+    private fun selectionControlColor(
+        enabled: Boolean,
+        checked: Boolean,
+        checkedColor: Color,
+        uncheckedColor: Color,
+        disabledCheckedColor: Color,
+        disabledUncheckedColor: Color
+    ) = animateColorAsState(
+        if (enabled) {
+            if (checked) checkedColor else uncheckedColor
+        } else {
+            if (checked) disabledCheckedColor else disabledUncheckedColor
+        }
+    )
+}
diff --git a/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Button.kt b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Button.kt
index e389f5c..9284438 100644
--- a/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Button.kt
+++ b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Button.kt
@@ -34,6 +34,8 @@
 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.unit.Dp
 
 /**
@@ -80,14 +82,16 @@
             .clickable(
                 onClick = onClick,
                 enabled = enabled,
-                role = Role.Button,
+                role = null, // provide the role via Modifier.semantics
                 interactionSource = interactionSource,
                 indication = rememberRipple(),
             )
             .then(
                 // Make sure modifier ordering is clip > clickable > padding > size,
                 // so that the ripple applies to the entire button shape and size.
+                // Then, apply semantics to apply the default semantic role (can be overridden)
                 modifier
+                    .semantics { role = Role.Button }
             )
             .size(buttonSize)
             .clip(shape) // Clip for the painted background area after size has been applied.
diff --git a/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/RepeatableClickable.kt b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/RepeatableClickable.kt
new file mode 100644
index 0000000..da01843
--- /dev/null
+++ b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/RepeatableClickable.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.materialcore
+
+import androidx.annotation.RestrictTo
+import androidx.compose.foundation.gestures.awaitEachGesture
+import androidx.compose.foundation.gestures.awaitFirstDown
+import androidx.compose.foundation.gestures.waitForUpOrCancellation
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.composed
+import androidx.compose.ui.input.pointer.pointerInput
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+
+/**
+ * This modifier provides functionality to increment or decrement values repeatedly
+ * by holding down the composable
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public fun Modifier.repeatableClickable(
+    enabled: Boolean,
+    initialDelay: Long = 500L,
+    incrementalDelay: Long = 60L,
+    onClick: () -> Unit
+): Modifier = composed {
+
+    val currentOnClick by rememberUpdatedState(onClick)
+
+    pointerInput(enabled) {
+        coroutineScope {
+            awaitEachGesture {
+                awaitFirstDown()
+                val repeatingJob = launch {
+                    delay(initialDelay)
+                    while (enabled) {
+                        currentOnClick()
+                        delay(incrementalDelay)
+                    }
+                }
+                waitForUpOrCancellation()
+                repeatingJob.cancel()
+            }
+        }
+    }
+}
diff --git a/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Resources.kt b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Resources.kt
new file mode 100644
index 0000000..6035f10
--- /dev/null
+++ b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Resources.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.materialcore
+
+import androidx.annotation.RestrictTo
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.unit.LayoutDirection
+
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@Composable
+fun isLayoutDirectionRtl(): Boolean {
+    val layoutDirection: LayoutDirection = LocalLayoutDirection.current
+    return remember(layoutDirection) {
+        layoutDirection == LayoutDirection.Rtl
+    }
+}
diff --git a/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/SelectionControls.kt b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/SelectionControls.kt
new file mode 100644
index 0000000..3eb9cdb
--- /dev/null
+++ b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/SelectionControls.kt
@@ -0,0 +1,566 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.materialcore
+
+import androidx.annotation.RestrictTo
+import androidx.compose.animation.animateColorAsState
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.CubicBezierEasing
+import androidx.compose.animation.core.Transition
+import androidx.compose.animation.core.TweenSpec
+import androidx.compose.animation.core.animateFloat
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.core.updateTransition
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.Indication
+import androidx.compose.foundation.interaction.Interaction
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.selection.selectable
+import androidx.compose.foundation.selection.toggleable
+import androidx.compose.material.ripple.rememberRipple
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.drawscope.DrawScope
+import androidx.compose.ui.graphics.drawscope.Fill
+import androidx.compose.ui.graphics.drawscope.Stroke
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import kotlin.math.PI
+import kotlin.math.cos
+import kotlin.math.min
+import kotlin.math.sin
+
+/**
+ * [Checkbox] provides an animated checkbox for use in material APIs.
+ *
+ * @param checked Boolean flag indicating whether this checkbox is currently checked.
+ * @param modifier Modifier to be applied to the checkbox.
+ * This can be used to provide a content description for accessibility.
+ * @param boxColor Composable lambda from which the box color will be obtained.
+ * @param checkmarkColor Composable lambda from which the check mark color will be obtained.
+ * @param enabled Boolean flag indicating the enabled state of the [Checkbox] (affects
+ * the color).
+ * @param onCheckedChange Callback to be invoked when Checkbox is clicked. If null, then this is
+ * passive and relies entirely on a higher-level component to control the state.
+ * @param interactionSource When also providing [onCheckedChange], the [MutableInteractionSource]
+ * representing the stream of [Interaction]s for the "toggleable" tap area -
+ * can be used to customise the appearance / behavior of the Checkbox.
+ * @param progressAnimationSpec Animation spec to animate the progress.
+ * @param drawBox Draws the checkbox.
+ * @param width Width of the checkbox.
+ * @param height Height of the checkbox.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@Composable
+fun Checkbox(
+    checked: Boolean,
+    modifier: Modifier = Modifier,
+    boxColor: @Composable (enabled: Boolean, checked: Boolean) -> State<Color>,
+    checkmarkColor: @Composable (enabled: Boolean, checked: Boolean) -> State<Color>,
+    enabled: Boolean,
+    onCheckedChange: ((Boolean) -> Unit)?,
+    interactionSource: MutableInteractionSource,
+    progressAnimationSpec: TweenSpec<Float>,
+    drawBox: FunctionDrawBox,
+    width: Dp,
+    height: Dp
+) {
+    val targetState = if (checked) SelectionStage.Checked else SelectionStage.Unchecked
+    val transition = updateTransition(targetState, label = "checkboxTransition")
+    val progress = animateProgress(
+        transition = transition, label = "Checkbox", animationSpec = progressAnimationSpec
+    )
+
+    // For Checkbox, the color and alpha animations have the same duration and easing,
+    // so we don't need to explicitly animate alpha.
+    val boxColorState = boxColor(enabled, checked)
+    val checkmarkColorState = checkmarkColor(enabled, checked)
+
+    Canvas(
+        modifier = modifier.maybeToggleable(
+            onCheckedChange,
+            enabled,
+            checked,
+            interactionSource,
+            rememberRipple(),
+            Role.Checkbox,
+            width,
+            height
+        )
+    ) {
+        drawBox(this, boxColorState.value, progress.value)
+
+        if (targetState == SelectionStage.Checked) {
+            // Passing startXOffset as we want checkbox to be aligned to the end of the canvas.
+            drawTick(checkmarkColorState.value, progress.value, width - height)
+        } else {
+            // Passing startXOffset as we want checkbox to be aligned to the end of the canvas.
+            eraseTick(checkmarkColorState.value, progress.value, width - height)
+        }
+    }
+}
+
+/**
+ * [Switch] provides an animated switch for use in material APIs.
+ *
+ * @param modifier Modifier to be applied to the switch.
+ * This can be used to provide a content description for accessibility.
+ * @param checked Boolean flag indicating whether this switch is currently toggled on.
+ * @param enabled Boolean flag indicating the enabled state of the [Switch] (affects
+ * the color).
+ * @param onCheckedChange Callback to be invoked when Switch is clicked. If null, then this is
+ * passive and relies entirely on a higher-level component to control the state.
+ * @param interactionSource When also providing [onCheckedChange], the [MutableInteractionSource]
+ * representing the stream of [Interaction]s for the "toggleable" tap area -
+ * can be used to customise the appearance / behavior of the Switch.
+ * @param trackFillColor Composable lambda from which the fill color of the track will be obtained.
+ * @param trackStrokeColor Composable lambda from which the stroke color of the track will be obtained.
+ * @param thumbColor Composable lambda from which the thumb color will be obtained.
+ * @param thumbIconColor Composable lambda from which the icon color will be obtained.
+ * @param trackWidth Width of the track.
+ * @param trackHeight Height of the track.
+ * @param drawThumb Lambda function to draw the thumb of the switch.
+ * The lambda is invoked with trackFillColor as the icon color, along with the thumbColor,
+ * and the progress.
+ * @param progressAnimationSpec Animation spec to animate the progress.
+ * @param width Width of the switch.
+ * @param height Height of the switch.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@Composable
+fun Switch(
+    modifier: Modifier,
+    checked: Boolean,
+    enabled: Boolean,
+    onCheckedChange: ((Boolean) -> Unit)?,
+    interactionSource: MutableInteractionSource,
+    trackFillColor: @Composable (enabled: Boolean, checked: Boolean) -> State<Color>,
+    trackStrokeColor: @Composable (enabled: Boolean, checked: Boolean) -> State<Color>,
+    thumbColor: @Composable (enabled: Boolean, checked: Boolean) -> State<Color>,
+    thumbIconColor: @Composable (enabled: Boolean, checked: Boolean) -> State<Color>,
+    trackWidth: Dp,
+    trackHeight: Dp,
+    drawThumb: FunctionDrawThumb,
+    progressAnimationSpec: TweenSpec<Float>,
+    width: Dp,
+    height: Dp,
+) {
+    val targetState = if (checked) SelectionStage.Checked else SelectionStage.Unchecked
+    val transition = updateTransition(targetState, label = "switchTransition")
+    val isRtl = isLayoutDirectionRtl()
+
+    val thumbProgress = animateProgress(
+        transition,
+        "Switch",
+        progressAnimationSpec
+    )
+
+    val thumbBackgroundColor = thumbColor(enabled, checked)
+    val iconColor = thumbIconColor(enabled, checked)
+    val trackBackgroundFillColor = trackFillColor(enabled, checked)
+    val trackBackgroundStrokeColor = trackStrokeColor(enabled, checked)
+
+    Canvas(
+        modifier = modifier.maybeToggleable(
+            onCheckedChange,
+            enabled,
+            checked,
+            interactionSource,
+            rememberRipple(),
+            Role.Switch,
+            width,
+            height
+        )
+    ) {
+        drawTrack(
+            fillColor = trackBackgroundFillColor.value,
+            strokeColor = trackBackgroundStrokeColor.value,
+            trackWidthPx = trackWidth.toPx(),
+            trackHeightPx = trackHeight.toPx()
+        )
+
+        // Draw the thumb of the switch.
+        drawThumb(
+            this, thumbBackgroundColor.value, thumbProgress.value, iconColor.value, isRtl
+        )
+    }
+}
+
+/**
+ * [RadioButton] provides an animated radio button for use in material APIs.
+ *
+ * @param modifier Modifier to be applied to the radio button. This can be used to provide a
+ * content description for accessibility.
+ * @param selected Boolean flag indicating whether this radio button is currently toggled on.
+ * @param enabled Boolean flag indicating the enabled state of the [RadioButton] (affects
+ * the color).
+ * @param ringColor Composable lambda from which the ring color of the radio button will be obtained.
+ * @param dotColor Composable lambda from which the dot color of the radio button will be obtained.
+ * @param onClick Callback to be invoked when RadioButton is clicked. If null, then this is
+ * passive and relies entirely on a higher-level component to control the state.
+ * @param interactionSource When also providing [onClick], the [MutableInteractionSource]
+ * representing the stream of [Interaction]s for the "toggleable" tap area -
+ * can be used to customise the appearance / behavior of the RadioButton.
+ * @param dotRadiusProgressDuration Duration of the dot radius progress animation.
+ * @param dotAlphaProgressDuration Duration of the dot alpha progress animation.
+ * @param dotAlphaProgressDelay Delay for the dot alpha progress animation.
+ * @param easing Animation spec to animate the progress.
+ * @param width Width of the radio button.
+ * @param height Height of the radio button.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@Composable
+fun RadioButton(
+    modifier: Modifier,
+    selected: Boolean,
+    enabled: Boolean,
+    ringColor: @Composable (enabled: Boolean, checked: Boolean) -> State<Color>,
+    dotColor: @Composable (enabled: Boolean, checked: Boolean) -> State<Color>,
+    onClick: (() -> Unit)?,
+    interactionSource: MutableInteractionSource,
+    dotRadiusProgressDuration: FunctionDotRadiusProgressDuration,
+    dotAlphaProgressDuration: Int,
+    dotAlphaProgressDelay: Int,
+    easing: CubicBezierEasing,
+    width: Dp,
+    height: Dp
+) {
+    val targetState = if (selected) SelectionStage.Checked else SelectionStage.Unchecked
+    val transition = updateTransition(targetState)
+
+    val radioRingColor = ringColor(enabled, selected)
+    val radioDotColor = dotColor(enabled, selected)
+
+    val dotRadiusProgress = animateProgress(
+        transition = transition,
+        label = "dot-radius",
+        animationSpec = tween(dotRadiusProgressDuration(selected), 0, easing)
+    )
+    // Animation of the dot alpha only happens when toggling On to Off.
+    val dotAlphaProgress =
+        if (targetState == SelectionStage.Unchecked)
+            animateProgress(
+                transition = transition,
+                label = "dot-alpha",
+                animationSpec = tween(
+                    dotAlphaProgressDuration,
+                    dotAlphaProgressDelay,
+                    easing
+                )
+            )
+        else
+            null
+
+    Canvas(
+        modifier = modifier.maybeSelectable(
+            onClick, enabled, selected, interactionSource, rememberRipple(), width, height
+        )
+    ) {
+        // Aligning the radio to the right.
+        val startXOffsetPx = (width - height).toPx() / 2
+        // Outer circle has a constant radius.
+        val circleCenter = Offset(center.x + startXOffsetPx, center.y)
+        drawCircle(
+            radius = RADIO_CIRCLE_RADIUS.toPx(),
+            color = radioRingColor.value,
+            center = circleCenter,
+            style = Stroke(RADIO_CIRCLE_STROKE.toPx()),
+        )
+        // Inner dot radius expands/shrinks.
+        drawCircle(
+            radius = dotRadiusProgress.value * RADIO_DOT_RADIUS.toPx(),
+            color = radioDotColor.value.copy(
+                alpha = (dotAlphaProgress?.value ?: 1f) * radioDotColor.value.alpha
+            ),
+            center = circleCenter,
+            style = Fill,
+        )
+    }
+}
+
+/**
+ * Returns the color for the selectionControl.
+ *
+ * @param enabled Boolean flag checking if the selection control is enabled.
+ * @param checked Boolean flag checking if the selection control is checked [SelectionStage].
+ * @param checkedColor Color for selection control when [enabled] = true and [checked] = true.
+ * @param uncheckedColor Color for selection control when [enabled] = true and [checked] = false.
+ * @param disabledCheckedColor Color for selection control when [enabled] = false and [checked] = true.
+ * @param disabledUncheckedColor Color for selection control when [enabled] = false and [checked] = false.
+ * @param animationSpec AnimationSpec for the color transition animations.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@Composable
+fun animateSelectionColor(
+    enabled: Boolean,
+    checked: Boolean,
+    checkedColor: Color,
+    uncheckedColor: Color,
+    disabledCheckedColor: Color,
+    disabledUncheckedColor: Color,
+    animationSpec: AnimationSpec<Color>
+): State<Color> = animateColorAsState(
+    targetValue = if (enabled) {
+        if (checked) checkedColor else uncheckedColor
+    } else {
+        if (checked) disabledCheckedColor else disabledUncheckedColor
+    },
+    animationSpec = animationSpec
+)
+
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+enum class SelectionStage {
+    Unchecked, Checked
+}
+
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+fun interface FunctionDrawBox {
+    operator fun invoke(drawScope: DrawScope, color: Color, progress: Float)
+}
+
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+fun interface FunctionDrawThumb {
+    operator fun invoke(
+        drawScope: DrawScope,
+        thumbColor: Color,
+        progress: Float,
+        thumbIconColor: Color,
+        isRtl: Boolean
+    )
+}
+
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+fun interface FunctionDotRadiusProgressDuration {
+    operator fun invoke(selected: Boolean): Int
+}
+
+@Composable
+private fun animateProgress(
+    transition: Transition<SelectionStage>,
+    label: String,
+    animationSpec: TweenSpec<Float>,
+) = transition.animateFloat(
+    transitionSpec = {
+        animationSpec
+    }, label = label
+) {
+    when (it) {
+        SelectionStage.Unchecked -> 0f
+        SelectionStage.Checked -> 1f
+    }
+}
+
+private fun Modifier.maybeToggleable(
+    onCheckedChange: ((Boolean) -> Unit)?,
+    enabled: Boolean,
+    checked: Boolean,
+    interactionSource: MutableInteractionSource,
+    indication: Indication,
+    role: Role,
+    canvasWidth: Dp,
+    canvasHeight: Dp
+): Modifier {
+    val standardModifier = this
+        .wrapContentSize(Alignment.CenterEnd)
+        .requiredSize(canvasWidth, canvasHeight)
+
+    return if (onCheckedChange == null) {
+        standardModifier
+    } else {
+        standardModifier.then(
+            Modifier.toggleable(
+                enabled = enabled,
+                value = checked,
+                onValueChange = onCheckedChange,
+                role = role,
+                indication = indication,
+                interactionSource = interactionSource
+            )
+        )
+    }
+}
+
+private fun Modifier.maybeSelectable(
+    onClick: (() -> Unit)?,
+    enabled: Boolean,
+    selected: Boolean,
+    interactionSource: MutableInteractionSource,
+    indication: Indication,
+    canvasWidth: Dp,
+    canvasHeight: Dp
+): Modifier {
+    val standardModifier = this
+        .wrapContentSize(Alignment.Center)
+        .requiredSize(canvasWidth, canvasHeight)
+
+    return if (onClick == null) {
+        standardModifier
+    } else {
+        standardModifier.then(
+            Modifier.selectable(
+                selected = selected,
+                interactionSource = interactionSource,
+                indication = indication,
+                enabled = enabled,
+                role = Role.RadioButton,
+                onClick = onClick,
+            )
+        )
+    }
+}
+
+private fun DrawScope.drawTick(tickColor: Color, tickProgress: Float, startXOffset: Dp) {
+    // Using tickProgress animating from zero to TICK_TOTAL_LENGTH,
+    // rotate the tick as we draw from 15 degrees to zero.
+    val tickBaseLength = TICK_BASE_LENGTH.toPx()
+    val tickStickLength = TICK_STICK_LENGTH.toPx()
+    val tickTotalLength = tickBaseLength + tickStickLength
+    val tickProgressPx = tickProgress * tickTotalLength
+    val startXOffsetPx = startXOffset.toPx()
+    val center = Offset(12.dp.toPx() + startXOffsetPx, 12.dp.toPx())
+    val angle = TICK_ROTATION - TICK_ROTATION / tickTotalLength * tickProgressPx
+    val angleRadians = angle.toRadians()
+
+    // Animate the base of the tick.
+    val baseStart = Offset(6.7f.dp.toPx() + startXOffsetPx, 12.3f.dp.toPx())
+    val tickBaseProgress = min(tickProgressPx, tickBaseLength)
+
+    val path = Path()
+    path.moveTo(baseStart.rotate(angleRadians, center))
+    path.lineTo(
+        (baseStart + Offset(tickBaseProgress, tickBaseProgress)).rotate(angleRadians, center)
+    )
+
+    if (tickProgressPx > tickBaseLength) {
+        val tickStickProgress = min(tickProgressPx - tickBaseLength, tickStickLength)
+        val stickStart = Offset(9.3f.dp.toPx() + startXOffsetPx, 16.3f.dp.toPx())
+        // Move back to the start of the stick (without drawing)
+        path.moveTo(stickStart.rotate(angleRadians, center))
+        path.lineTo(
+            Offset(stickStart.x + tickStickProgress, stickStart.y - tickStickProgress).rotate(
+                angleRadians,
+                center
+            )
+        )
+    }
+    // Use StrokeCap.Butt because Square adds an extension on the end of each line.
+    drawPath(path, tickColor, style = Stroke(width = 2.dp.toPx(), cap = StrokeCap.Butt))
+}
+
+private fun DrawScope.drawTrack(
+    fillColor: Color,
+    strokeColor: Color,
+    trackWidthPx: Float,
+    trackHeightPx: Float,
+) {
+    val path = Path()
+    val strokeRadius = trackHeightPx / 2f
+    path.moveTo(Offset(strokeRadius, center.y))
+    path.lineTo(Offset(trackWidthPx - strokeRadius, center.y))
+
+    // Draws the border of the track
+    drawPath(
+        path = path,
+        color = strokeColor,
+        style = Stroke(width = trackHeightPx, cap = StrokeCap.Round),
+    )
+
+    // If strokeColor and fillColor are different, drawing another path for the fill of the track.
+    if (strokeColor != fillColor) {
+        drawPath(
+            path = path,
+            color = fillColor,
+            style = Stroke(
+                width = trackHeightPx - 2 * SWITCH_TRACK_BORDER.toPx(),
+                cap = StrokeCap.Round
+            )
+        )
+    }
+}
+
+private fun DrawScope.eraseTick(tickColor: Color, tickProgress: Float, startXOffset: Dp) {
+    val tickBaseLength = TICK_BASE_LENGTH.toPx()
+    val tickStickLength = TICK_STICK_LENGTH.toPx()
+    val tickTotalLength = tickBaseLength + tickStickLength
+    val tickProgressPx = tickProgress * tickTotalLength
+    val startXOffsetPx = startXOffset.toPx()
+
+    // Animate the stick of the tick, drawing down the stick from the top.
+    val stickStartX = 17.3f.dp.toPx() + startXOffsetPx
+    val stickStartY = 8.3f.dp.toPx()
+    val tickStickProgress = min(tickProgressPx, tickStickLength)
+
+    val path = Path()
+    path.moveTo(stickStartX, stickStartY)
+    path.lineTo(stickStartX - tickStickProgress, stickStartY + tickStickProgress)
+
+    if (tickStickProgress > tickStickLength) {
+        // Animate the base of the tick, drawing up the base from bottom of the stick.
+        val tickBaseProgress = min(tickProgressPx - tickStickLength, tickBaseLength)
+        val baseStartX = 10.7f.dp.toPx() + startXOffsetPx
+        val baseStartY = 16.3f.dp.toPx()
+        path.moveTo(baseStartX, baseStartY)
+        path.lineTo(baseStartX - tickBaseProgress, baseStartY - tickBaseProgress)
+    }
+
+    drawPath(path, tickColor, style = Stroke(width = 2.dp.toPx(), cap = StrokeCap.Butt))
+}
+
+private fun Path.moveTo(offset: Offset) {
+    moveTo(offset.x, offset.y)
+}
+
+private fun Path.lineTo(offset: Offset) {
+    lineTo(offset.x, offset.y)
+}
+
+private fun Offset.rotate(angleRadians: Float): Offset {
+    val angledDirection = directionVector(angleRadians)
+    return angledDirection * x + angledDirection.rotate90() * y
+}
+
+private fun Offset.rotate(angleRadians: Float, center: Offset): Offset =
+    (this - center).rotate(angleRadians) + center
+
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+fun directionVector(angleRadians: Float) = Offset(cos(angleRadians), sin(angleRadians))
+
+private fun Offset.rotate90() = Offset(-y, x)
+
+// This is duplicated from wear.compose.foundation/geometry.kt
+// Any changes should be replicated there.
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+fun Float.toRadians() = this * PI.toFloat() / 180f
+
+private val TICK_BASE_LENGTH = 4.dp
+private val TICK_STICK_LENGTH = 8.dp
+private const val TICK_ROTATION = 15f
+
+private val SWITCH_TRACK_BORDER = 1.dp
+
+private val RADIO_CIRCLE_RADIUS = 9.dp
+private val RADIO_CIRCLE_STROKE = 2.dp
+private val RADIO_DOT_RADIUS = 5.dp
diff --git a/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Slider.kt b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Slider.kt
index 5042b3b..065d3d0 100644
--- a/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Slider.kt
+++ b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Slider.kt
@@ -54,6 +54,7 @@
                 interactionSource = remember { MutableInteractionSource() },
                 indication = LocalIndication.current,
             )
+            .repeatableClickable(enabled = enabled, onClick = onClick)
             .then(modifier),
         contentAlignment = contentAlignment
     ) {
diff --git a/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Stepper.kt b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Stepper.kt
index a82c450..d1a25d0 100644
--- a/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Stepper.kt
+++ b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Stepper.kt
@@ -159,6 +159,7 @@
             .clickable(
                 interactionSource, null, onClick = onClick, enabled = enabled, role = Role.Button
             )
+            .repeatableClickable(enabled = enabled, onClick = onClick)
             .wrapContentWidth()
             .indication(interactionSource, rememberRipple(bounded = false))
             .padding(paddingValues),
diff --git a/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/ToggleButton.kt b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/ToggleButton.kt
new file mode 100644
index 0000000..0047502
--- /dev/null
+++ b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/ToggleButton.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.materialcore
+
+import androidx.annotation.RestrictTo
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.interaction.Interaction
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.selection.toggleable
+import androidx.compose.material.ripple.rememberRipple
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+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.unit.Dp
+
+/**
+ * Wear Material [ToggleButton] that offers a single slot to take any content
+ * (text, icon or image).
+ *
+ * [ToggleButton]s can be enabled or disabled. A disabled toggle button will not respond to click
+ * events.
+ *
+ * For more information, see the
+ * [Buttons](https://developer.android.com/training/wearables/components/buttons#toggle-button)
+ * guide.
+ *
+ * @param checked Boolean flag indicating whether this toggle button is currently checked.
+ * @param onCheckedChange Callback to be invoked when this toggle button is clicked.
+ * @param modifier Modifier to be applied to the toggle button.
+ * @param enabled Controls the enabled state of the toggle button. When `false`,
+ * this toggle button will not be clickable.
+ * @param backgroundColor Resolves the background for this toggle button in different states.
+ * @param border Resolves the border for this toggle button in different states.
+ * @param toggleButtonSize The default size of the toggle button unless overridden by
+ * [Modifier.size].
+ * @param interactionSource The [MutableInteractionSource] representing the stream of
+ * [Interaction]s for this toggle button. You can create and pass in your own remembered
+ * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
+ * appearance / behavior of this ToggleButton in different [Interaction]s.
+ * @param shape Defines the shape for this toggle button. It is strongly recommended to use the
+ * default as this shape is a key characteristic of the Wear Material Theme.
+ * @param content The icon, image or text to be drawn inside the toggle button.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@Composable
+fun ToggleButton(
+    checked: Boolean,
+    onCheckedChange: (Boolean) -> Unit,
+    modifier: Modifier,
+    enabled: Boolean,
+    backgroundColor: @Composable (enabled: Boolean, checked: Boolean) -> State<Color>,
+    border: @Composable (enabled: Boolean, checked: Boolean) -> State<BorderStroke?>?,
+    toggleButtonSize: Dp,
+    interactionSource: MutableInteractionSource,
+    shape: Shape,
+    content: @Composable BoxScope.() -> Unit,
+) {
+    val borderStroke = border(enabled, checked)?.value
+    Box(
+        contentAlignment = Alignment.Center,
+        modifier = Modifier
+            .clip(shape) // Clip for the touch area (e.g. for Ripple).
+            .toggleable(
+                value = checked,
+                onValueChange = onCheckedChange,
+                enabled = enabled,
+                role = null, // // Provide the role via Modifier.semantics
+                interactionSource = interactionSource,
+                indication = rememberRipple()
+            )
+            .then(
+                // Make sure modifier ordering is clip > toggleable > padding > size,
+                // so that the ripple applies to the entire button shape and size.
+                modifier.semantics { role = Role.Checkbox }
+            )
+            .size(toggleButtonSize)
+            .clip(shape) // Clip for the painted background area after size has been applied.
+            .then(
+                if (borderStroke != null) Modifier.border(border = borderStroke, shape = shape)
+                else Modifier
+            )
+            .background(
+                color = backgroundColor(enabled, checked).value,
+                shape = shape
+            ),
+        content = content
+    )
+}
diff --git a/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/ToggleButtonTest.kt b/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/ToggleButtonTest.kt
index 84db9176..ae4e264 100644
--- a/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/ToggleButtonTest.kt
+++ b/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/ToggleButtonTest.kt
@@ -767,11 +767,14 @@
     setContentWithTheme {
         background = MaterialTheme.colors.surface
         buttonColor = MaterialTheme.colors.primary
-        content(
-            Modifier
-                .testTag(TEST_TAG)
-                .padding(padding)
-                .background(background))
+        Box(Modifier.background(background)) {
+            content(
+                Modifier
+                    .testTag(TEST_TAG)
+                    .padding(padding)
+                    .background(background)
+            )
+        }
     }
 
     onNodeWithTag(TEST_TAG)
diff --git a/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/ToggleControlScreenshotTest.kt b/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/ToggleControlScreenshotTest.kt
index cdb3ebe..98aacd3 100644
--- a/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/ToggleControlScreenshotTest.kt
+++ b/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/ToggleControlScreenshotTest.kt
@@ -20,13 +20,16 @@
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.testutils.assertAgainstGolden
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.compositeOver
+import androidx.compose.ui.platform.LocalLayoutDirection
 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.compose.ui.unit.LayoutDirection
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
@@ -100,6 +103,38 @@
         }
 
     @Test
+    fun switch_rtl_checked_enabled() =
+        verifyScreenshot {
+            CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
+                Switch(checked = true, enabled = true, modifier = testBackgroundModifier())
+            }
+        }
+
+    @Test
+    fun switch_rtl_unchecked_enabled() =
+        verifyScreenshot {
+            CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
+                Switch(checked = false, enabled = true, modifier = testBackgroundModifier())
+            }
+        }
+
+    @Test
+    fun switch_rtl_checked_disabled() =
+        verifyScreenshot {
+            CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
+                Switch(checked = true, enabled = false, modifier = testBackgroundModifier())
+            }
+        }
+
+    @Test
+    fun switch_rtl_unchecked_disabled() =
+        verifyScreenshot {
+            CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
+                Switch(checked = false, enabled = false, modifier = testBackgroundModifier())
+            }
+        }
+
+    @Test
     fun radiobutton_checked_enabled() =
         verifyScreenshot {
             RadioButton(selected = true, enabled = true, modifier = testBackgroundModifier())
diff --git a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/Colors.kt b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/Colors.kt
index f66a294..5e0441a 100644
--- a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/Colors.kt
+++ b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/Colors.kt
@@ -208,4 +208,12 @@
     onError = other.onError
 }
 
+/**
+ * Convert given color to disabled color.
+ * @param disabledContentAlpha Alpha used to represent disabled content colors.
+ */
+@Composable
+internal fun Color.toDisabledColor(disabledContentAlpha: Float = ContentAlpha.disabled) =
+    this.copy(alpha = disabledContentAlpha)
+
 internal val LocalColors = staticCompositionLocalOf<Colors> { Colors() }
diff --git a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ListHeader.kt b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ListHeader.kt
index 46ccc4b..994e12c 100644
--- a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ListHeader.kt
+++ b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ListHeader.kt
@@ -54,7 +54,7 @@
             .wrapContentSize()
             .background(backgroundColor)
             .padding(horizontal = 14.dp)
-            .semantics { heading() }
+            .semantics(mergeDescendants = true) { heading() }
     ) {
         CompositionLocalProvider(
             LocalContentColor provides contentColor,
diff --git a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ToggleButton.kt b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ToggleButton.kt
index 812ac3d..4a208e3 100644
--- a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ToggleButton.kt
+++ b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ToggleButton.kt
@@ -15,28 +15,22 @@
  */
 package androidx.wear.compose.material
 
-import androidx.compose.foundation.background
 import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.BoxScope
-import androidx.compose.foundation.layout.defaultMinSize
-import androidx.compose.foundation.selection.toggleable
 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
 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.clip
 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.unit.dp
 
 /**
@@ -219,36 +213,24 @@
     role: Role = ToggleButtonDefaults.DefaultRole,
     content: @Composable BoxScope.() -> Unit,
 ) {
-    Box(
-        contentAlignment = Alignment.Center,
-        modifier = modifier
-            .defaultMinSize(
-                minWidth = ToggleButtonDefaults.DefaultToggleButtonSize,
-                minHeight = ToggleButtonDefaults.DefaultToggleButtonSize
-            )
-            .clip(shape)
-            .toggleable(
-                value = checked,
-                onValueChange = onCheckedChange,
-                enabled = enabled,
-                role = role,
-                interactionSource = interactionSource,
-                indication = rememberRipple()
-            )
-            .background(
-                color = colors.backgroundColor(enabled = enabled, checked = checked).value,
-                shape = shape
-            )
-    ) {
-        val contentColor = colors.contentColor(enabled = enabled, checked = checked).value
-        CompositionLocalProvider(
-            LocalContentColor provides contentColor,
-            LocalContentAlpha provides contentColor.alpha,
-            LocalTextStyle provides MaterialTheme.typography.button,
-        ) {
-            content()
-        }
-    }
+    androidx.wear.compose.materialcore.ToggleButton(
+        checked = checked,
+        onCheckedChange = onCheckedChange,
+        modifier = modifier.semantics { this.role = role },
+        enabled = enabled,
+        backgroundColor = { isEnabled, isChecked ->
+            colors.backgroundColor(enabled = isEnabled, checked = isChecked)
+        },
+        border = { _, _ -> null },
+        toggleButtonSize = ToggleButtonDefaults.DefaultToggleButtonSize,
+        interactionSource = interactionSource,
+        shape = shape,
+        content = provideScopeContent(
+            colors.contentColor(enabled = enabled, checked = checked),
+            MaterialTheme.typography.button,
+            content
+        )
+    )
 }
 /**
  * Represents the background and content colors used in a toggle button in different states.
diff --git a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ToggleControl.kt b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ToggleControl.kt
index 88c3b0de..db56184 100644
--- a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ToggleControl.kt
+++ b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ToggleControl.kt
@@ -15,46 +15,28 @@
  */
 package androidx.wear.compose.material
 
-import androidx.compose.animation.animateColorAsState
 import androidx.compose.animation.core.AnimationSpec
-import androidx.compose.animation.core.Transition
-import androidx.compose.animation.core.animateFloat
+import androidx.compose.animation.core.TweenSpec
 import androidx.compose.animation.core.tween
-import androidx.compose.animation.core.updateTransition
-import androidx.compose.foundation.Canvas
-import androidx.compose.foundation.Indication
 import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.foundation.layout.requiredSize
-import androidx.compose.foundation.layout.wrapContentSize
-import androidx.compose.foundation.selection.selectable
-import androidx.compose.foundation.selection.toggleable
-import androidx.compose.material.ripple.rememberRipple
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.State
 import androidx.compose.runtime.remember
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.CornerRadius
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.BlendMode
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.Path
-import androidx.compose.ui.graphics.StrokeCap
 import androidx.compose.ui.graphics.compositeOver
 import androidx.compose.ui.graphics.drawscope.DrawScope
-import androidx.compose.ui.graphics.drawscope.Fill
 import androidx.compose.ui.graphics.drawscope.Stroke
-import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.util.lerp
-import kotlin.math.PI
-import kotlin.math.cos
-import kotlin.math.min
-import kotlin.math.sin
+import androidx.wear.compose.materialcore.animateSelectionColor
 
 /**
  * [Checkbox] provides an animated checkbox for use as a toggle control in
@@ -84,37 +66,29 @@
     enabled: Boolean = true,
     onCheckedChange: ((Boolean) -> Unit)? = null,
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
-) {
-    val targetState = if (checked) ToggleStage.Checked else ToggleStage.Unchecked
-    val transition = updateTransition(targetState)
-    val tickProgress = animateProgress(transition, "Checkbox")
-
-    // For Checkbox, the color and alpha animations have the same duration and easing,
-    // so we don't need to explicitly animate alpha.
-    val boxColor = colors.boxColor(enabled = enabled, checked = checked)
-    val checkColor = colors.checkmarkColor(enabled = enabled, checked = checked)
-
-    Canvas(
-        modifier = modifier.maybeToggleable(
-            onCheckedChange, enabled, checked, interactionSource, rememberRipple(),
-            Role.Checkbox
+) = androidx.wear.compose.materialcore.Checkbox(
+    checked = checked,
+    modifier = modifier,
+    boxColor = { isEnabled, isChecked ->
+        colors.boxColor(
+            enabled = isEnabled,
+            checked = isChecked
         )
-    ) {
-        drawBox(color = boxColor.value)
-
-        if (targetState == ToggleStage.Checked) {
-            drawTick(
-                tickProgress = tickProgress.value,
-                tickColor = checkColor.value,
-            )
-        } else {
-            eraseTick(
-                tickProgress = tickProgress.value,
-                tickColor = checkColor.value,
-            )
-        }
-    }
-}
+    },
+    checkmarkColor = { isEnabled, isChecked ->
+        colors.checkmarkColor(
+            enabled = isEnabled,
+            checked = isChecked
+        )
+    },
+    enabled = enabled,
+    onCheckedChange = onCheckedChange,
+    interactionSource = interactionSource,
+    drawBox = { drawScope, color, _ -> drawScope.drawBox(color) },
+    progressAnimationSpec = PROGRESS_ANIMATION_SPEC,
+    width = WIDTH,
+    height = HEIGHT
+)
 
 /**
  * [Switch] provides an animated switch for use as a toggle control in
@@ -144,40 +118,45 @@
     enabled: Boolean = true,
     onCheckedChange: ((Boolean) -> Unit)? = null,
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
-) {
-    val targetState = if (checked) ToggleStage.Checked else ToggleStage.Unchecked
-    val transition = updateTransition(targetState)
-
-    // For Switch, the color and alpha animations have the same duration and easing,
-    // so we don't need to explicitly animate alpha.
-    val thumbProgress = animateProgress(transition, "Switch")
-    val thumbColor = colors.thumbColor(enabled = enabled, checked = checked)
-    val trackColor = colors.trackColor(enabled = enabled, checked = checked)
-
-    Canvas(
-        modifier = modifier.maybeToggleable(
-            onCheckedChange, enabled, checked, interactionSource, rememberRipple(), Role.Switch
+) = androidx.wear.compose.materialcore.Switch(
+    modifier = modifier,
+    checked = checked,
+    enabled = enabled,
+    onCheckedChange = onCheckedChange,
+    interactionSource = interactionSource,
+    trackFillColor = { isEnabled, isChecked ->
+        colors.trackColor(
+            enabled = isEnabled,
+            checked = isChecked
         )
-    ) {
-        val switchTrackLengthPx = SWITCH_TRACK_LENGTH.toPx()
-        val switchTrackHeightPx = SWITCH_TRACK_HEIGHT.toPx()
-        val switchThumbRadiusPx = SWITCH_THUMB_RADIUS.toPx()
-
-        val thumbProgressPx = lerp(
-            start = switchThumbRadiusPx,
-            stop = switchTrackLengthPx - switchThumbRadiusPx,
-            fraction = thumbProgress.value
+    },
+    trackStrokeColor = { isEnabled, isChecked ->
+        colors.trackColor(
+            enabled = isEnabled,
+            checked = isChecked
         )
-        drawTrack(trackColor.value, switchTrackLengthPx, switchTrackHeightPx)
-        // Use BlendMode.Src to overwrite overlapping pixels with the thumb color
-        // (by default, the track shows through any transparency).
-        drawCircle(
-            color = thumbColor.value,
-            radius = switchThumbRadiusPx,
-            center = Offset(thumbProgressPx, center.y),
-            blendMode = BlendMode.Src)
-    }
-}
+    },
+    thumbColor = { isEnabled, isChecked ->
+        colors.thumbColor(
+            enabled = isEnabled,
+            checked = isChecked
+        )
+    },
+    thumbIconColor = { isEnabled, isChecked ->
+        colors.thumbColor(
+            enabled = isEnabled,
+            checked = isChecked
+        )
+    },
+    trackWidth = SWITCH_TRACK_LENGTH,
+    trackHeight = SWITCH_TRACK_HEIGHT,
+    drawThumb = { drawScope, color, progress, _, isRtl ->
+        drawScope.drawThumb(color = color, progress = progress, isRtl = isRtl)
+    },
+    progressAnimationSpec = PROGRESS_ANIMATION_SPEC,
+    width = WIDTH,
+    height = HEIGHT
+)
 
 /**
  * [RadioButton] provides an animated radio button for use as a toggle control in
@@ -207,49 +186,31 @@
     enabled: Boolean = true,
     onClick: (() -> Unit)? = null,
     interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
-) {
-    val targetState = if (selected) ToggleStage.Checked else ToggleStage.Unchecked
-    val transition = updateTransition(targetState)
-
-    val circleColor = colors.ringColor(enabled = enabled, selected = selected)
-    val dotRadiusProgress = animateProgress(
-        transition,
-        durationMillis = if (selected) QUICK else STANDARD,
-        label = "dot-radius"
-    )
-    val dotColor = colors.dotColor(enabled = enabled, selected = selected)
-    // Animation of the dot alpha only happens when toggling On to Off.
-    val dotAlphaProgress =
-        if (targetState == ToggleStage.Unchecked)
-            animateProgress(
-                transition, durationMillis = RAPID, delayMillis = FLASH, label = "dot-alpha"
-            )
-        else
-            null
-
-    Canvas(
-        modifier = modifier.maybeSelectable(
-            onClick, enabled, selected, interactionSource, rememberRipple()
+) = androidx.wear.compose.materialcore.RadioButton(
+    modifier = modifier,
+    selected = selected,
+    enabled = enabled,
+    ringColor = { isEnabled, isSelected ->
+        colors.ringColor(
+            enabled = isEnabled,
+            selected = isSelected
         )
-    ) {
-        // Outer circle has a constant radius.
-        drawCircle(
-            radius = RADIO_CIRCLE_RADIUS.toPx(),
-            color = circleColor.value,
-            center = center,
-            style = Stroke(RADIO_CIRCLE_STROKE.toPx()),
+    },
+    dotColor = { isEnabled, isSelected ->
+        colors.dotColor(
+            enabled = isEnabled,
+            selected = isSelected
         )
-        // Inner dot radius expands/shrinks.
-        drawCircle(
-            radius = dotRadiusProgress.value * RADIO_DOT_RADIUS.toPx(),
-            color = dotColor.value.copy(
-                alpha = (dotAlphaProgress?.value ?: 1f) * dotColor.value.alpha
-            ),
-            center = center,
-            style = Fill,
-        )
-    }
-}
+    },
+    onClick = onClick,
+    interactionSource = interactionSource,
+    dotRadiusProgressDuration = { isSelected -> if (isSelected) QUICK else RAPID },
+    dotAlphaProgressDuration = RAPID,
+    dotAlphaProgressDelay = FLASH,
+    easing = STANDARD_IN,
+    width = WIDTH,
+    height = HEIGHT
+)
 
 /**
  * Represents the content colors used in [Checkbox] in different states.
@@ -359,13 +320,13 @@
             uncheckedBoxColor = uncheckedBoxColor,
             uncheckedCheckmarkColor = uncheckedCheckmarkColor,
             disabledCheckedBoxColor =
-                checkedBoxColor.copy(alpha = ContentAlpha.disabled),
+            checkedBoxColor.toDisabledColor(),
             disabledCheckedCheckmarkColor =
-                checkedCheckmarkColor.copy(alpha = ContentAlpha.disabled),
+            checkedCheckmarkColor.toDisabledColor(),
             disabledUncheckedBoxColor =
-                uncheckedBoxColor.copy(alpha = ContentAlpha.disabled),
+            uncheckedBoxColor.toDisabledColor(),
             disabledUncheckedCheckmarkColor =
-                uncheckedCheckmarkColor.copy(alpha = ContentAlpha.disabled),
+            uncheckedCheckmarkColor.toDisabledColor(),
         )
     }
 }
@@ -395,14 +356,15 @@
             checkedTrackColor = checkedTrackColor,
             uncheckedThumbColor = uncheckedThumbColor,
             uncheckedTrackColor = uncheckedTrackColor,
-            disabledCheckedThumbColor = checkedThumbColor.copy(alpha = ContentAlpha.disabled),
-            disabledCheckedTrackColor = checkedTrackColor.copy(
-                alpha = checkedTrackColor.alpha * ContentAlpha.disabled
-            ),
+            disabledCheckedThumbColor = checkedThumbColor.toDisabledColor(),
+            disabledCheckedTrackColor = checkedTrackColor.toDisabledColor(
+                disabledContentAlpha = checkedTrackColor.alpha * ContentAlpha.disabled),
             disabledUncheckedThumbColor =
-                uncheckedThumbColor.copy(alpha = uncheckedThumbColor.alpha * ContentAlpha.disabled),
+            uncheckedThumbColor.toDisabledColor(
+                disabledContentAlpha = uncheckedThumbColor.alpha * ContentAlpha.disabled),
             disabledUncheckedTrackColor =
-                uncheckedTrackColor.copy(alpha = uncheckedTrackColor.alpha * ContentAlpha.disabled),
+            uncheckedTrackColor.toDisabledColor(
+                disabledContentAlpha = uncheckedTrackColor.alpha * ContentAlpha.disabled),
         )
     }
 }
@@ -441,86 +403,10 @@
             selectedDotColor = selectedDotColor,
             unselectedRingColor = unselectedRingColor,
             unselectedDotColor = unselectedDotColor,
-            disabledSelectedRingColor = selectedRingColor.copy(alpha = ContentAlpha.disabled),
-            disabledSelectedDotColor = selectedDotColor.copy(alpha = ContentAlpha.disabled),
-            disabledUnselectedRingColor =
-                unselectedRingColor.copy(alpha = ContentAlpha.disabled),
-            disabledUnselectedDotColor = unselectedDotColor.copy(alpha = ContentAlpha.disabled),
-        )
-    }
-}
-
-@Composable
-private fun animateProgress(
-    transition: Transition<ToggleStage>,
-    label: String,
-    durationMillis: Int = QUICK,
-    delayMillis: Int = 0,
-) =
-    transition.animateFloat(
-        transitionSpec = {
-            tween(durationMillis = durationMillis, delayMillis = delayMillis, easing = STANDARD_IN)
-        },
-        label = label
-    ) {
-        // Return the tick progress as a Float in Px.
-        when (it) {
-            ToggleStage.Unchecked -> 0f
-            ToggleStage.Checked -> 1f
-        }
-    }
-
-private fun Modifier.maybeToggleable(
-    onCheckedChange: ((Boolean) -> Unit)?,
-    enabled: Boolean,
-    checked: Boolean,
-    interactionSource: MutableInteractionSource,
-    indication: Indication,
-    role: Role,
-): Modifier {
-    val standardModifier = this
-        .wrapContentSize(Alignment.Center)
-        .requiredSize(24.dp)
-
-    return if (onCheckedChange == null) {
-        standardModifier
-    } else {
-        standardModifier.then(
-            Modifier.toggleable(
-                enabled = enabled,
-                value = checked,
-                onValueChange = onCheckedChange,
-                role = role,
-                indication = indication,
-                interactionSource = interactionSource
-            )
-        )
-    }
-}
-
-private fun Modifier.maybeSelectable(
-    onClick: (() -> Unit)?,
-    enabled: Boolean,
-    selected: Boolean,
-    interactionSource: MutableInteractionSource,
-    indication: Indication,
-): Modifier {
-    val standardModifier = this
-        .wrapContentSize(Alignment.Center)
-        .requiredSize(24.dp)
-
-    return if (onClick == null) {
-        standardModifier
-    } else {
-        standardModifier.then(
-            Modifier.selectable(
-                selected = selected,
-                interactionSource = interactionSource,
-                indication = indication,
-                enabled = enabled,
-                role = Role.RadioButton,
-                onClick = onClick,
-            )
+            disabledSelectedRingColor = selectedRingColor.toDisabledColor(),
+            disabledSelectedDotColor = selectedDotColor.toDisabledColor(),
+            disabledUnselectedRingColor = unselectedRingColor.toDisabledColor(),
+            disabledUnselectedDotColor = unselectedDotColor.toDisabledColor(),
         )
     }
 }
@@ -540,79 +426,27 @@
     )
 }
 
-private fun DrawScope.drawTick(tickColor: Color, tickProgress: Float) {
-    // Using tickProgress animating from zero to TICK_TOTAL_LENGTH,
-    // rotate the tick as we draw from 15 degrees to zero.
-    val tickBaseLength = TICK_BASE_LENGTH.toPx()
-    val tickStickLength = TICK_STICK_LENGTH.toPx()
-    val tickTotalLength = tickBaseLength + tickStickLength
-    val tickProgressPx = tickProgress * tickTotalLength
-    val center = Offset(12.dp.toPx(), 12.dp.toPx())
-    val angle = TICK_ROTATION - TICK_ROTATION / tickTotalLength * tickProgressPx
-    val angleRadians = angle.toRadians()
-
-    // Animate the base of the tick.
-    val baseStart = Offset(6.7f.dp.toPx(), 12.3f.dp.toPx())
-    val tickBaseProgress = min(tickProgressPx, tickBaseLength)
-
-    val path = Path()
-    path.moveTo(baseStart.rotate(angleRadians, center))
-    path.lineTo((baseStart + Offset(tickBaseProgress, tickBaseProgress))
-        .rotate(angleRadians, center))
-
-    if (tickProgressPx > tickBaseLength) {
-        val tickStickProgress = min(tickProgressPx - tickBaseLength, tickStickLength)
-        val stickStart = Offset(9.3f.dp.toPx(), 16.3f.dp.toPx())
-        // Move back to the start of the stick (without drawing)
-        path.moveTo(stickStart.rotate(angleRadians, center))
-        path.lineTo(
-            Offset(stickStart.x + tickStickProgress, stickStart.y - tickStickProgress)
-                .rotate(angleRadians, center))
-    }
-    // Use StrokeCap.Butt because Square adds an extension on the end of each line.
-    drawPath(path, tickColor, style = Stroke(width = 2.dp.toPx(), cap = StrokeCap.Butt))
-}
-
-private fun DrawScope.eraseTick(tickColor: Color, tickProgress: Float) {
-    val tickBaseLength = TICK_BASE_LENGTH.toPx()
-    val tickStickLength = TICK_STICK_LENGTH.toPx()
-    val tickTotalLength = tickBaseLength + tickStickLength
-    val tickProgressPx = tickProgress * tickTotalLength
-
-    // Animate the stick of the tick, drawing down the stick from the top.
-    val stickStartX = 17.3f.dp.toPx()
-    val stickStartY = 8.3f.dp.toPx()
-    val tickStickProgress = min(tickProgressPx, tickStickLength)
-
-    val path = Path()
-    path.moveTo(stickStartX, stickStartY)
-    path.lineTo(stickStartX - tickStickProgress, stickStartY + tickStickProgress)
-
-    if (tickStickProgress > tickStickLength) {
-        // Animate the base of the tick, drawing up the base from bottom of the stick.
-        val tickBaseProgress = min(tickProgressPx - tickStickLength, tickBaseLength)
-        val baseStartX = 10.7f.dp.toPx()
-        val baseStartY = 16.3f.dp.toPx()
-        path.moveTo(baseStartX, baseStartY)
-        path.lineTo(baseStartX - tickBaseProgress, baseStartY - tickBaseProgress)
-    }
-
-    drawPath(path, tickColor, style = Stroke(width = 2.dp.toPx(), cap = StrokeCap.Butt))
-}
-
-private fun DrawScope.drawTrack(
+private fun DrawScope.drawThumb(
     color: Color,
-    switchTrackLengthPx: Float,
-    switchTrackHeightPx: Float,
+    progress: Float,
+    isRtl: Boolean
 ) {
-    val path = Path()
-    val strokeRadius = switchTrackHeightPx / 2f
-    path.moveTo(Offset(strokeRadius, center.y))
-    path.lineTo(Offset(switchTrackLengthPx - strokeRadius, center.y))
-    drawPath(
-        path = path,
+
+    val switchThumbRadiusPx = SWITCH_THUMB_RADIUS.toPx()
+    val switchTrackLengthPx = SWITCH_TRACK_LENGTH.toPx()
+
+    val thumbProgressPx =
+        lerp(
+            start = if (isRtl) switchTrackLengthPx - switchThumbRadiusPx else switchThumbRadiusPx,
+            stop = if (isRtl) switchThumbRadiusPx else switchTrackLengthPx - switchThumbRadiusPx,
+            fraction = progress
+        )
+
+    drawCircle(
         color = color,
-        style = Stroke(width = switchTrackHeightPx, cap = StrokeCap.Round)
+        radius = switchThumbRadiusPx,
+        center = Offset(thumbProgressPx, center.y),
+        blendMode = BlendMode.Src
     )
 }
 
@@ -631,29 +465,26 @@
     private val disabledUncheckedCheckmarkColor: Color,
 ) : CheckboxColors {
     @Composable
-    override fun boxColor(enabled: Boolean, checked: Boolean): State<Color> = animateColorAsState(
-        targetValue = toggleControlColor(
-            enabled,
-            checked,
-            checkedBoxColor,
-            uncheckedBoxColor,
-            disabledCheckedBoxColor,
-            disabledUncheckedBoxColor
-        ),
-        animationSpec = COLOR_ANIMATION_SPEC
-    )
+    override fun boxColor(enabled: Boolean, checked: Boolean): State<Color> =
+        animateSelectionColor(
+            enabled = enabled,
+            checked = checked,
+            checkedColor = checkedBoxColor,
+            uncheckedColor = uncheckedBoxColor,
+            disabledCheckedColor = disabledCheckedBoxColor,
+            disabledUncheckedColor = disabledUncheckedBoxColor,
+            animationSpec = COLOR_ANIMATION_SPEC
+        )
 
     @Composable
     override fun checkmarkColor(enabled: Boolean, checked: Boolean): State<Color> =
-        animateColorAsState(
-            targetValue = toggleControlColor(
-                enabled,
-                checked,
-                checkedCheckmarkColor,
-                uncheckedCheckmarkColor,
-                disabledCheckedCheckmarkColor,
-                disabledUncheckedCheckmarkColor
-            ),
+        animateSelectionColor(
+            enabled = enabled,
+            checked = checked,
+            checkedColor = checkedCheckmarkColor,
+            uncheckedColor = uncheckedCheckmarkColor,
+            disabledCheckedColor = disabledCheckedCheckmarkColor,
+            disabledUncheckedColor = disabledUncheckedCheckmarkColor,
             animationSpec = COLOR_ANIMATION_SPEC
         )
 
@@ -704,30 +535,28 @@
     private val disabledUncheckedTrackColor: Color,
 ) : SwitchColors {
     @Composable
-    override fun thumbColor(enabled: Boolean, checked: Boolean): State<Color> = animateColorAsState(
-        targetValue = toggleControlColor(
-            enabled,
-            checked,
-            checkedThumbColor,
-            uncheckedThumbColor,
-            disabledCheckedThumbColor,
-            disabledUncheckedThumbColor
-        ),
-        animationSpec = COLOR_ANIMATION_SPEC
-    )
+    override fun thumbColor(enabled: Boolean, checked: Boolean): State<Color> =
+        animateSelectionColor(
+            enabled = enabled,
+            checked = checked,
+            checkedColor = checkedThumbColor,
+            uncheckedColor = uncheckedThumbColor,
+            disabledCheckedColor = disabledCheckedThumbColor,
+            disabledUncheckedColor = disabledUncheckedThumbColor,
+            animationSpec = COLOR_ANIMATION_SPEC
+        )
 
     @Composable
-    override fun trackColor(enabled: Boolean, checked: Boolean): State<Color> = animateColorAsState(
-        targetValue = toggleControlColor(
-            enabled,
-            checked,
-            checkedTrackColor,
-            uncheckedTrackColor,
-            disabledCheckedTrackColor,
-            disabledUncheckedTrackColor
-        ),
-        animationSpec = COLOR_ANIMATION_SPEC
-    )
+    override fun trackColor(enabled: Boolean, checked: Boolean): State<Color> =
+        animateSelectionColor(
+            enabled = enabled,
+            checked = checked,
+            checkedColor = checkedTrackColor,
+            uncheckedColor = uncheckedTrackColor,
+            disabledCheckedColor = disabledCheckedTrackColor,
+            disabledUncheckedColor = disabledUncheckedTrackColor,
+            animationSpec = COLOR_ANIMATION_SPEC
+        )
 
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
@@ -777,29 +606,25 @@
 ) : RadioButtonColors {
     @Composable
     override fun ringColor(enabled: Boolean, selected: Boolean): State<Color> =
-        animateColorAsState(
-            targetValue = toggleControlColor(
-                enabled,
-                selected,
-                selectedRingColor,
-                unselectedRingColor,
-                disabledSelectedRingColor,
-                disabledUnselectedRingColor
-            ),
+        animateSelectionColor(
+            enabled = enabled,
+            checked = selected,
+            checkedColor = selectedRingColor,
+            uncheckedColor = unselectedRingColor,
+            disabledCheckedColor = disabledSelectedRingColor,
+            disabledUncheckedColor = disabledUnselectedRingColor,
             animationSpec = COLOR_ANIMATION_SPEC
         )
 
     @Composable
     override fun dotColor(enabled: Boolean, selected: Boolean): State<Color> =
-        animateColorAsState(
-            targetValue = toggleControlColor(
-                enabled,
-                selected,
-                selectedDotColor,
-                unselectedDotColor,
-                disabledSelectedDotColor,
-                disabledUnselectedDotColor
-            ),
+        animateSelectionColor(
+            enabled = enabled,
+            checked = selected,
+            checkedColor = selectedDotColor,
+            uncheckedColor = unselectedDotColor,
+            disabledCheckedColor = disabledSelectedDotColor,
+            disabledUncheckedColor = disabledUnselectedDotColor,
             animationSpec = COLOR_ANIMATION_SPEC
         )
 
@@ -835,63 +660,17 @@
     }
 }
 
-private fun Path.moveTo(offset: Offset) {
-    moveTo(offset.x, offset.y)
-}
-
-private fun Path.lineTo(offset: Offset) {
-    lineTo(offset.x, offset.y)
-}
-
-private fun Offset.rotate(angleRadians: Float): Offset {
-    val angledDirection = directionVector(angleRadians)
-    return angledDirection * x + angledDirection.rotate90() * y
-}
-
-private fun Offset.rotate(angleRadians: Float, center: Offset): Offset =
-    (this - center).rotate(angleRadians) + center
-
-private fun directionVector(angleRadians: Float) = Offset(cos(angleRadians), sin(angleRadians))
-
-private fun Offset.rotate90() = Offset(-y, x)
-
-// This is duplicated from wear.compose.foundation/geometry.kt
-// Any changes should be replicated there.
-private fun Float.toRadians() = this * PI.toFloat() / 180f
-
-private enum class ToggleStage {
-    Unchecked, Checked
-}
-
-private fun toggleControlColor(
-    enabled: Boolean,
-    checked: Boolean,
-    checkedColor: Color,
-    uncheckedColor: Color,
-    enabledCheckedColor: Color,
-    disabledCheckedColor: Color
-) =
-    if (enabled) {
-        if (checked) checkedColor else uncheckedColor
-    } else {
-        if (checked) enabledCheckedColor else disabledCheckedColor
-    }
-
 private val BOX_CORNER = 3.dp
 private val BOX_STROKE = 2.dp
 private val BOX_RADIUS = 2.dp
 private val BOX_SIZE = 18.dp
 
-private val TICK_BASE_LENGTH = 4.dp
-private val TICK_STICK_LENGTH = 8.dp
-private const val TICK_ROTATION = 15f
-
 private val SWITCH_TRACK_LENGTH = 24.dp
 private val SWITCH_TRACK_HEIGHT = 10.dp
 private val SWITCH_THUMB_RADIUS = 7.dp
 
-private val RADIO_CIRCLE_RADIUS = 9.dp
-private val RADIO_CIRCLE_STROKE = 2.dp
-private val RADIO_DOT_RADIUS = 5.dp
-
 private val COLOR_ANIMATION_SPEC: AnimationSpec<Color> = tween(QUICK, 0, STANDARD_IN)
+private val PROGRESS_ANIMATION_SPEC: TweenSpec<Float> = tween(QUICK, 0, STANDARD_IN)
+
+private val WIDTH = 24.dp
+private val HEIGHT = 24.dp
diff --git a/wear/compose/compose-material3/api/current.txt b/wear/compose/compose-material3/api/current.txt
index 0657516..3accfa8 100644
--- a/wear/compose/compose-material3/api/current.txt
+++ b/wear/compose/compose-material3/api/current.txt
@@ -90,6 +90,30 @@
     method @androidx.compose.runtime.Composable public static void TitleCard(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.CardColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? time, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
+  @androidx.compose.runtime.Immutable public final class CheckboxColors {
+    method public long getCheckedBoxColor();
+    method public long getCheckedCheckmarkColor();
+    method public long getDisabledCheckedBoxColor();
+    method public long getDisabledCheckedCheckmarkColor();
+    method public long getDisabledUncheckedBoxColor();
+    method public long getDisabledUncheckedCheckmarkColor();
+    method public long getUncheckedBoxColor();
+    method public long getUncheckedCheckmarkColor();
+    property public final long checkedBoxColor;
+    property public final long checkedCheckmarkColor;
+    property public final long disabledCheckedBoxColor;
+    property public final long disabledCheckedCheckmarkColor;
+    property public final long disabledUncheckedBoxColor;
+    property public final long disabledUncheckedCheckmarkColor;
+    property public final long uncheckedBoxColor;
+    property public final long uncheckedCheckmarkColor;
+  }
+
+  public final class CheckboxDefaults {
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.CheckboxColors colors(optional long checkedBoxColor, optional long checkedCheckmarkColor, optional long uncheckedBoxColor, optional long uncheckedCheckmarkColor);
+    field public static final androidx.wear.compose.material3.CheckboxDefaults INSTANCE;
+  }
+
   @androidx.compose.runtime.Stable public final class ColorScheme {
     ctor public ColorScheme(optional long primary, optional long primaryDim, optional long primaryContainer, optional long onPrimary, optional long onPrimaryContainer, optional long secondary, optional long secondaryDim, optional long secondaryContainer, optional long onSecondary, optional long onSecondaryContainer, optional long tertiary, optional long tertiaryDim, optional long tertiaryContainer, optional long onTertiary, optional long onTertiaryContainer, optional long surfaceDim, optional long surface, optional long surfaceBright, optional long onSurface, optional long onSurfaceVariant, optional long outline, optional long outlineVariant, optional long background, optional long onBackground, optional long error, optional long onError);
     method public androidx.wear.compose.material3.ColorScheme copy(optional long primary, optional long primaryDim, optional long primaryContainer, optional long onPrimary, optional long onPrimaryContainer, optional long secondary, optional long secondaryDim, optional long secondaryContainer, optional long onSecondary, optional long onSecondaryContainer, optional long tertiary, optional long tertiaryDim, optional long tertiaryContainer, optional long onTertiary, optional long onTertiaryContainer, optional long surfaceDim, optional long surface, optional long surfaceBright, optional long onSurface, optional long onSurfaceVariant, optional long outline, optional long outlineVariant, optional long background, optional long onBackground, optional long error, optional long onError);
@@ -205,6 +229,7 @@
     method public float getSmallIconSize();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors iconButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
     method public float iconSizeFor(float size);
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ToggleButtonColors iconToggleButtonColors(optional long checkedContainerColor, optional long checkedContentColor, optional long uncheckedContainerColor, optional long uncheckedContentColor, optional long disabledCheckedContainerColor, optional long disabledCheckedContentColor, optional long disabledUncheckedContainerColor, optional long disabledUncheckedContentColor);
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors outlinedIconButtonColors(optional long contentColor);
     property public final float DefaultButtonSize;
     property public final float DefaultIconSize;
@@ -221,6 +246,7 @@
     method @androidx.compose.runtime.Composable public static void FilledIconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void FilledTonalIconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void IconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void IconToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material3.ToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void OutlinedIconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
   }
 
@@ -298,45 +324,59 @@
     method @androidx.compose.runtime.Composable public static void MaterialTheme(optional androidx.wear.compose.material3.ColorScheme colorScheme, optional androidx.wear.compose.material3.Typography typography, optional androidx.wear.compose.material3.Shapes shapes, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
+  @androidx.compose.runtime.Immutable public final class RadioButtonColors {
+    method public long getDisabledSelectedColor();
+    method public long getDisabledUnselectedColor();
+    method public long getSelectedColor();
+    method public long getUnselectedColor();
+    property public final long disabledSelectedColor;
+    property public final long disabledUnselectedColor;
+    property public final long selectedColor;
+    property public final long unselectedColor;
+  }
+
+  public final class RadioButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.RadioButtonColors colors(optional long selectedColor, optional long unselectedColor);
+    field public static final androidx.wear.compose.material3.RadioButtonDefaults INSTANCE;
+  }
+
   public final class RangeSemanticsKt {
     method public static androidx.compose.ui.Modifier rangeSemantics(androidx.compose.ui.Modifier, float value, boolean enabled, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, int steps);
   }
 
+  public final class SelectionControlsKt {
+    method @androidx.compose.runtime.Composable public static void Checkbox(boolean checked, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.material3.CheckboxColors colors, optional boolean enabled, optional kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit>? onCheckedChange, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable public static void RadioButton(boolean selected, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.material3.RadioButtonColors colors, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onClick, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable public static void Switch(boolean checked, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.material3.SwitchColors colors, optional boolean enabled, optional kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit>? onCheckedChange, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+  }
+
   public final class ShapeDefaults {
     method public androidx.compose.foundation.shape.RoundedCornerShape getExtraLarge();
     method public androidx.compose.foundation.shape.RoundedCornerShape getExtraSmall();
-    method public androidx.compose.foundation.shape.RoundedCornerShape getFull();
     method public androidx.compose.foundation.shape.RoundedCornerShape getLarge();
     method public androidx.compose.foundation.shape.RoundedCornerShape getMedium();
-    method public androidx.compose.ui.graphics.Shape getNone();
     method public androidx.compose.foundation.shape.RoundedCornerShape getSmall();
     property public final androidx.compose.foundation.shape.RoundedCornerShape ExtraLarge;
     property public final androidx.compose.foundation.shape.RoundedCornerShape ExtraSmall;
-    property public final androidx.compose.foundation.shape.RoundedCornerShape Full;
     property public final androidx.compose.foundation.shape.RoundedCornerShape Large;
     property public final androidx.compose.foundation.shape.RoundedCornerShape Medium;
-    property public final androidx.compose.ui.graphics.Shape None;
     property public final androidx.compose.foundation.shape.RoundedCornerShape Small;
     field public static final androidx.wear.compose.material3.ShapeDefaults INSTANCE;
   }
 
   @androidx.compose.runtime.Immutable public final class Shapes {
-    ctor public Shapes(optional androidx.compose.ui.graphics.Shape none, optional androidx.compose.ui.graphics.Shape extraSmall, optional androidx.compose.ui.graphics.Shape small, optional androidx.compose.ui.graphics.Shape medium, optional androidx.compose.ui.graphics.Shape large, optional androidx.compose.ui.graphics.Shape extraLarge, optional androidx.compose.ui.graphics.Shape full);
-    method public androidx.wear.compose.material3.Shapes copy(optional androidx.compose.ui.graphics.Shape none, optional androidx.compose.ui.graphics.Shape extraSmall, optional androidx.compose.ui.graphics.Shape small, optional androidx.compose.ui.graphics.Shape medium, optional androidx.compose.ui.graphics.Shape large, optional androidx.compose.ui.graphics.Shape extraLarge, optional androidx.compose.ui.graphics.Shape full);
-    method public androidx.compose.ui.graphics.Shape getExtraLarge();
-    method public androidx.compose.ui.graphics.Shape getExtraSmall();
-    method public androidx.compose.ui.graphics.Shape getFull();
-    method public androidx.compose.ui.graphics.Shape getLarge();
-    method public androidx.compose.ui.graphics.Shape getMedium();
-    method public androidx.compose.ui.graphics.Shape getNone();
-    method public androidx.compose.ui.graphics.Shape getSmall();
-    property public final androidx.compose.ui.graphics.Shape extraLarge;
-    property public final androidx.compose.ui.graphics.Shape extraSmall;
-    property public final androidx.compose.ui.graphics.Shape full;
-    property public final androidx.compose.ui.graphics.Shape large;
-    property public final androidx.compose.ui.graphics.Shape medium;
-    property public final androidx.compose.ui.graphics.Shape none;
-    property public final androidx.compose.ui.graphics.Shape small;
+    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.wear.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);
+    method public androidx.compose.foundation.shape.CornerBasedShape getExtraLarge();
+    method public androidx.compose.foundation.shape.CornerBasedShape getExtraSmall();
+    method public androidx.compose.foundation.shape.CornerBasedShape getLarge();
+    method public androidx.compose.foundation.shape.CornerBasedShape getMedium();
+    method public androidx.compose.foundation.shape.CornerBasedShape getSmall();
+    property public final androidx.compose.foundation.shape.CornerBasedShape extraLarge;
+    property public final androidx.compose.foundation.shape.CornerBasedShape extraSmall;
+    property public final androidx.compose.foundation.shape.CornerBasedShape large;
+    property public final androidx.compose.foundation.shape.CornerBasedShape medium;
+    property public final androidx.compose.foundation.shape.CornerBasedShape small;
   }
 
   public final class SliderKt {
@@ -362,6 +402,46 @@
     method @androidx.compose.runtime.Composable public static void SwipeToDismissBox(kotlin.jvm.functions.Function0<kotlin.Unit> onDismissed, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.foundation.SwipeToDismissBoxState state, optional long backgroundScrimColor, optional long contentScrimColor, optional Object backgroundKey, optional Object contentKey, optional boolean userSwipeEnabled, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.layout.BoxScope,? super java.lang.Boolean,kotlin.Unit> content);
   }
 
+  @androidx.compose.runtime.Immutable public final class SwitchColors {
+    method public long getCheckedThumbColor();
+    method public long getCheckedThumbIconColor();
+    method public long getCheckedTrackBorderColor();
+    method public long getCheckedTrackColor();
+    method public long getDisabledCheckedThumbColor();
+    method public long getDisabledCheckedThumbIconColor();
+    method public long getDisabledCheckedTrackBorderColor();
+    method public long getDisabledCheckedTrackColor();
+    method public long getDisabledUncheckedThumbColor();
+    method public long getDisabledUncheckedThumbIconColor();
+    method public long getDisabledUncheckedTrackBorderColor();
+    method public long getDisabledUncheckedTrackColor();
+    method public long getUncheckedThumbColor();
+    method public long getUncheckedThumbIconColor();
+    method public long getUncheckedTrackBorderColor();
+    method public long getUncheckedTrackColor();
+    property public final long checkedThumbColor;
+    property public final long checkedThumbIconColor;
+    property public final long checkedTrackBorderColor;
+    property public final long checkedTrackColor;
+    property public final long disabledCheckedThumbColor;
+    property public final long disabledCheckedThumbIconColor;
+    property public final long disabledCheckedTrackBorderColor;
+    property public final long disabledCheckedTrackColor;
+    property public final long disabledUncheckedThumbColor;
+    property public final long disabledUncheckedThumbIconColor;
+    property public final long disabledUncheckedTrackBorderColor;
+    property public final long disabledUncheckedTrackColor;
+    property public final long uncheckedThumbColor;
+    property public final long uncheckedThumbIconColor;
+    property public final long uncheckedTrackBorderColor;
+    property public final long uncheckedTrackColor;
+  }
+
+  public final class SwitchDefaults {
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.SwitchColors colors(optional long checkedThumbColor, optional long checkedThumbIconColor, optional long checkedTrackColor, optional long checkedTrackStrokeColor, optional long uncheckedThumbColor, optional long uncheckedThumbIconColor, optional long uncheckedTrackColor, optional long uncheckedTrackStrokeColor);
+    field public static final androidx.wear.compose.material3.SwitchDefaults INSTANCE;
+  }
+
   @androidx.compose.runtime.Immutable public final class TextButtonColors {
     ctor public TextButtonColors(long containerColor, long contentColor, long disabledContainerColor, long disabledContentColor);
     method public long getContainerColor();
@@ -384,6 +464,7 @@
     method public float getSmallButtonSize();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextButtonColors outlinedTextButtonColors(optional long contentColor);
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextButtonColors textButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ToggleButtonColors textToggleButtonColors(optional long checkedContainerColor, optional long checkedContentColor, optional long uncheckedContainerColor, optional long uncheckedContentColor, optional long disabledCheckedContainerColor, optional long disabledCheckedContentColor, optional long disabledUncheckedContainerColor, optional long disabledUncheckedContentColor);
     property public final float DefaultButtonSize;
     property public final float ExtraSmallButtonSize;
     property public final float LargeButtonSize;
@@ -394,6 +475,7 @@
 
   public final class TextButtonKt {
     method @androidx.compose.runtime.Composable public static void TextButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.TextButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void TextToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material3.ToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
   }
 
   public final class TextKt {
@@ -404,6 +486,28 @@
     property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> LocalTextStyle;
   }
 
+  @androidx.compose.runtime.Immutable public final class ToggleButtonColors {
+    ctor public ToggleButtonColors(long checkedContainerColor, long checkedContentColor, long uncheckedContainerColor, long uncheckedContentColor, long disabledCheckedContainerColor, long disabledCheckedContentColor, long disabledUncheckedContainerColor, long disabledUncheckedContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> containerColor(boolean enabled, boolean checked);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> contentColor(boolean enabled, boolean checked);
+    method public long getCheckedContainerColor();
+    method public long getCheckedContentColor();
+    method public long getDisabledCheckedContainerColor();
+    method public long getDisabledCheckedContentColor();
+    method public long getDisabledUncheckedContainerColor();
+    method public long getDisabledUncheckedContentColor();
+    method public long getUncheckedContainerColor();
+    method public long getUncheckedContentColor();
+    property public final long checkedContainerColor;
+    property public final long checkedContentColor;
+    property public final long disabledCheckedContainerColor;
+    property public final long disabledCheckedContentColor;
+    property public final long disabledUncheckedContainerColor;
+    property public final long disabledUncheckedContentColor;
+    property public final long uncheckedContainerColor;
+    property public final long uncheckedContentColor;
+  }
+
   public final class TouchTargetAwareSizeKt {
     method public static androidx.compose.ui.Modifier touchTargetAwareSize(androidx.compose.ui.Modifier, float size);
   }
diff --git a/wear/compose/compose-material3/api/restricted_current.txt b/wear/compose/compose-material3/api/restricted_current.txt
index 0657516..3accfa8 100644
--- a/wear/compose/compose-material3/api/restricted_current.txt
+++ b/wear/compose/compose-material3/api/restricted_current.txt
@@ -90,6 +90,30 @@
     method @androidx.compose.runtime.Composable public static void TitleCard(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.CardColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit>? time, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
+  @androidx.compose.runtime.Immutable public final class CheckboxColors {
+    method public long getCheckedBoxColor();
+    method public long getCheckedCheckmarkColor();
+    method public long getDisabledCheckedBoxColor();
+    method public long getDisabledCheckedCheckmarkColor();
+    method public long getDisabledUncheckedBoxColor();
+    method public long getDisabledUncheckedCheckmarkColor();
+    method public long getUncheckedBoxColor();
+    method public long getUncheckedCheckmarkColor();
+    property public final long checkedBoxColor;
+    property public final long checkedCheckmarkColor;
+    property public final long disabledCheckedBoxColor;
+    property public final long disabledCheckedCheckmarkColor;
+    property public final long disabledUncheckedBoxColor;
+    property public final long disabledUncheckedCheckmarkColor;
+    property public final long uncheckedBoxColor;
+    property public final long uncheckedCheckmarkColor;
+  }
+
+  public final class CheckboxDefaults {
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.CheckboxColors colors(optional long checkedBoxColor, optional long checkedCheckmarkColor, optional long uncheckedBoxColor, optional long uncheckedCheckmarkColor);
+    field public static final androidx.wear.compose.material3.CheckboxDefaults INSTANCE;
+  }
+
   @androidx.compose.runtime.Stable public final class ColorScheme {
     ctor public ColorScheme(optional long primary, optional long primaryDim, optional long primaryContainer, optional long onPrimary, optional long onPrimaryContainer, optional long secondary, optional long secondaryDim, optional long secondaryContainer, optional long onSecondary, optional long onSecondaryContainer, optional long tertiary, optional long tertiaryDim, optional long tertiaryContainer, optional long onTertiary, optional long onTertiaryContainer, optional long surfaceDim, optional long surface, optional long surfaceBright, optional long onSurface, optional long onSurfaceVariant, optional long outline, optional long outlineVariant, optional long background, optional long onBackground, optional long error, optional long onError);
     method public androidx.wear.compose.material3.ColorScheme copy(optional long primary, optional long primaryDim, optional long primaryContainer, optional long onPrimary, optional long onPrimaryContainer, optional long secondary, optional long secondaryDim, optional long secondaryContainer, optional long onSecondary, optional long onSecondaryContainer, optional long tertiary, optional long tertiaryDim, optional long tertiaryContainer, optional long onTertiary, optional long onTertiaryContainer, optional long surfaceDim, optional long surface, optional long surfaceBright, optional long onSurface, optional long onSurfaceVariant, optional long outline, optional long outlineVariant, optional long background, optional long onBackground, optional long error, optional long onError);
@@ -205,6 +229,7 @@
     method public float getSmallIconSize();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors iconButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
     method public float iconSizeFor(float size);
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ToggleButtonColors iconToggleButtonColors(optional long checkedContainerColor, optional long checkedContentColor, optional long uncheckedContainerColor, optional long uncheckedContentColor, optional long disabledCheckedContainerColor, optional long disabledCheckedContentColor, optional long disabledUncheckedContainerColor, optional long disabledUncheckedContentColor);
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors outlinedIconButtonColors(optional long contentColor);
     property public final float DefaultButtonSize;
     property public final float DefaultIconSize;
@@ -221,6 +246,7 @@
     method @androidx.compose.runtime.Composable public static void FilledIconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void FilledTonalIconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void IconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void IconToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material3.ToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
     method @androidx.compose.runtime.Composable public static void OutlinedIconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
   }
 
@@ -298,45 +324,59 @@
     method @androidx.compose.runtime.Composable public static void MaterialTheme(optional androidx.wear.compose.material3.ColorScheme colorScheme, optional androidx.wear.compose.material3.Typography typography, optional androidx.wear.compose.material3.Shapes shapes, kotlin.jvm.functions.Function0<kotlin.Unit> content);
   }
 
+  @androidx.compose.runtime.Immutable public final class RadioButtonColors {
+    method public long getDisabledSelectedColor();
+    method public long getDisabledUnselectedColor();
+    method public long getSelectedColor();
+    method public long getUnselectedColor();
+    property public final long disabledSelectedColor;
+    property public final long disabledUnselectedColor;
+    property public final long selectedColor;
+    property public final long unselectedColor;
+  }
+
+  public final class RadioButtonDefaults {
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.RadioButtonColors colors(optional long selectedColor, optional long unselectedColor);
+    field public static final androidx.wear.compose.material3.RadioButtonDefaults INSTANCE;
+  }
+
   public final class RangeSemanticsKt {
     method public static androidx.compose.ui.Modifier rangeSemantics(androidx.compose.ui.Modifier, float value, boolean enabled, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, int steps);
   }
 
+  public final class SelectionControlsKt {
+    method @androidx.compose.runtime.Composable public static void Checkbox(boolean checked, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.material3.CheckboxColors colors, optional boolean enabled, optional kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit>? onCheckedChange, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable public static void RadioButton(boolean selected, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.material3.RadioButtonColors colors, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onClick, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+    method @androidx.compose.runtime.Composable public static void Switch(boolean checked, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.material3.SwitchColors colors, optional boolean enabled, optional kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit>? onCheckedChange, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+  }
+
   public final class ShapeDefaults {
     method public androidx.compose.foundation.shape.RoundedCornerShape getExtraLarge();
     method public androidx.compose.foundation.shape.RoundedCornerShape getExtraSmall();
-    method public androidx.compose.foundation.shape.RoundedCornerShape getFull();
     method public androidx.compose.foundation.shape.RoundedCornerShape getLarge();
     method public androidx.compose.foundation.shape.RoundedCornerShape getMedium();
-    method public androidx.compose.ui.graphics.Shape getNone();
     method public androidx.compose.foundation.shape.RoundedCornerShape getSmall();
     property public final androidx.compose.foundation.shape.RoundedCornerShape ExtraLarge;
     property public final androidx.compose.foundation.shape.RoundedCornerShape ExtraSmall;
-    property public final androidx.compose.foundation.shape.RoundedCornerShape Full;
     property public final androidx.compose.foundation.shape.RoundedCornerShape Large;
     property public final androidx.compose.foundation.shape.RoundedCornerShape Medium;
-    property public final androidx.compose.ui.graphics.Shape None;
     property public final androidx.compose.foundation.shape.RoundedCornerShape Small;
     field public static final androidx.wear.compose.material3.ShapeDefaults INSTANCE;
   }
 
   @androidx.compose.runtime.Immutable public final class Shapes {
-    ctor public Shapes(optional androidx.compose.ui.graphics.Shape none, optional androidx.compose.ui.graphics.Shape extraSmall, optional androidx.compose.ui.graphics.Shape small, optional androidx.compose.ui.graphics.Shape medium, optional androidx.compose.ui.graphics.Shape large, optional androidx.compose.ui.graphics.Shape extraLarge, optional androidx.compose.ui.graphics.Shape full);
-    method public androidx.wear.compose.material3.Shapes copy(optional androidx.compose.ui.graphics.Shape none, optional androidx.compose.ui.graphics.Shape extraSmall, optional androidx.compose.ui.graphics.Shape small, optional androidx.compose.ui.graphics.Shape medium, optional androidx.compose.ui.graphics.Shape large, optional androidx.compose.ui.graphics.Shape extraLarge, optional androidx.compose.ui.graphics.Shape full);
-    method public androidx.compose.ui.graphics.Shape getExtraLarge();
-    method public androidx.compose.ui.graphics.Shape getExtraSmall();
-    method public androidx.compose.ui.graphics.Shape getFull();
-    method public androidx.compose.ui.graphics.Shape getLarge();
-    method public androidx.compose.ui.graphics.Shape getMedium();
-    method public androidx.compose.ui.graphics.Shape getNone();
-    method public androidx.compose.ui.graphics.Shape getSmall();
-    property public final androidx.compose.ui.graphics.Shape extraLarge;
-    property public final androidx.compose.ui.graphics.Shape extraSmall;
-    property public final androidx.compose.ui.graphics.Shape full;
-    property public final androidx.compose.ui.graphics.Shape large;
-    property public final androidx.compose.ui.graphics.Shape medium;
-    property public final androidx.compose.ui.graphics.Shape none;
-    property public final androidx.compose.ui.graphics.Shape small;
+    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.wear.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);
+    method public androidx.compose.foundation.shape.CornerBasedShape getExtraLarge();
+    method public androidx.compose.foundation.shape.CornerBasedShape getExtraSmall();
+    method public androidx.compose.foundation.shape.CornerBasedShape getLarge();
+    method public androidx.compose.foundation.shape.CornerBasedShape getMedium();
+    method public androidx.compose.foundation.shape.CornerBasedShape getSmall();
+    property public final androidx.compose.foundation.shape.CornerBasedShape extraLarge;
+    property public final androidx.compose.foundation.shape.CornerBasedShape extraSmall;
+    property public final androidx.compose.foundation.shape.CornerBasedShape large;
+    property public final androidx.compose.foundation.shape.CornerBasedShape medium;
+    property public final androidx.compose.foundation.shape.CornerBasedShape small;
   }
 
   public final class SliderKt {
@@ -362,6 +402,46 @@
     method @androidx.compose.runtime.Composable public static void SwipeToDismissBox(kotlin.jvm.functions.Function0<kotlin.Unit> onDismissed, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.foundation.SwipeToDismissBoxState state, optional long backgroundScrimColor, optional long contentScrimColor, optional Object backgroundKey, optional Object contentKey, optional boolean userSwipeEnabled, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.layout.BoxScope,? super java.lang.Boolean,kotlin.Unit> content);
   }
 
+  @androidx.compose.runtime.Immutable public final class SwitchColors {
+    method public long getCheckedThumbColor();
+    method public long getCheckedThumbIconColor();
+    method public long getCheckedTrackBorderColor();
+    method public long getCheckedTrackColor();
+    method public long getDisabledCheckedThumbColor();
+    method public long getDisabledCheckedThumbIconColor();
+    method public long getDisabledCheckedTrackBorderColor();
+    method public long getDisabledCheckedTrackColor();
+    method public long getDisabledUncheckedThumbColor();
+    method public long getDisabledUncheckedThumbIconColor();
+    method public long getDisabledUncheckedTrackBorderColor();
+    method public long getDisabledUncheckedTrackColor();
+    method public long getUncheckedThumbColor();
+    method public long getUncheckedThumbIconColor();
+    method public long getUncheckedTrackBorderColor();
+    method public long getUncheckedTrackColor();
+    property public final long checkedThumbColor;
+    property public final long checkedThumbIconColor;
+    property public final long checkedTrackBorderColor;
+    property public final long checkedTrackColor;
+    property public final long disabledCheckedThumbColor;
+    property public final long disabledCheckedThumbIconColor;
+    property public final long disabledCheckedTrackBorderColor;
+    property public final long disabledCheckedTrackColor;
+    property public final long disabledUncheckedThumbColor;
+    property public final long disabledUncheckedThumbIconColor;
+    property public final long disabledUncheckedTrackBorderColor;
+    property public final long disabledUncheckedTrackColor;
+    property public final long uncheckedThumbColor;
+    property public final long uncheckedThumbIconColor;
+    property public final long uncheckedTrackBorderColor;
+    property public final long uncheckedTrackColor;
+  }
+
+  public final class SwitchDefaults {
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.SwitchColors colors(optional long checkedThumbColor, optional long checkedThumbIconColor, optional long checkedTrackColor, optional long checkedTrackStrokeColor, optional long uncheckedThumbColor, optional long uncheckedThumbIconColor, optional long uncheckedTrackColor, optional long uncheckedTrackStrokeColor);
+    field public static final androidx.wear.compose.material3.SwitchDefaults INSTANCE;
+  }
+
   @androidx.compose.runtime.Immutable public final class TextButtonColors {
     ctor public TextButtonColors(long containerColor, long contentColor, long disabledContainerColor, long disabledContentColor);
     method public long getContainerColor();
@@ -384,6 +464,7 @@
     method public float getSmallButtonSize();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextButtonColors outlinedTextButtonColors(optional long contentColor);
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextButtonColors textButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ToggleButtonColors textToggleButtonColors(optional long checkedContainerColor, optional long checkedContentColor, optional long uncheckedContainerColor, optional long uncheckedContentColor, optional long disabledCheckedContainerColor, optional long disabledCheckedContentColor, optional long disabledUncheckedContainerColor, optional long disabledUncheckedContentColor);
     property public final float DefaultButtonSize;
     property public final float ExtraSmallButtonSize;
     property public final float LargeButtonSize;
@@ -394,6 +475,7 @@
 
   public final class TextButtonKt {
     method @androidx.compose.runtime.Composable public static void TextButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.TextButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+    method @androidx.compose.runtime.Composable public static void TextToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material3.ToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
   }
 
   public final class TextKt {
@@ -404,6 +486,28 @@
     property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> LocalTextStyle;
   }
 
+  @androidx.compose.runtime.Immutable public final class ToggleButtonColors {
+    ctor public ToggleButtonColors(long checkedContainerColor, long checkedContentColor, long uncheckedContainerColor, long uncheckedContentColor, long disabledCheckedContainerColor, long disabledCheckedContentColor, long disabledUncheckedContainerColor, long disabledUncheckedContentColor);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> containerColor(boolean enabled, boolean checked);
+    method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<androidx.compose.ui.graphics.Color> contentColor(boolean enabled, boolean checked);
+    method public long getCheckedContainerColor();
+    method public long getCheckedContentColor();
+    method public long getDisabledCheckedContainerColor();
+    method public long getDisabledCheckedContentColor();
+    method public long getDisabledUncheckedContainerColor();
+    method public long getDisabledUncheckedContentColor();
+    method public long getUncheckedContainerColor();
+    method public long getUncheckedContentColor();
+    property public final long checkedContainerColor;
+    property public final long checkedContentColor;
+    property public final long disabledCheckedContainerColor;
+    property public final long disabledCheckedContentColor;
+    property public final long disabledUncheckedContainerColor;
+    property public final long disabledUncheckedContentColor;
+    property public final long uncheckedContainerColor;
+    property public final long uncheckedContentColor;
+  }
+
   public final class TouchTargetAwareSizeKt {
     method public static androidx.compose.ui.Modifier touchTargetAwareSize(androidx.compose.ui.Modifier, float size);
   }
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonDemo.kt
index 76cddf8..d2c396f 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ButtonDemo.kt
@@ -29,6 +29,7 @@
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
 import androidx.wear.compose.material3.Button
+import androidx.wear.compose.material3.ButtonColors
 import androidx.wear.compose.material3.ButtonDefaults
 import androidx.wear.compose.material3.ChildButton
 import androidx.wear.compose.material3.FilledTonalButton
@@ -310,13 +311,17 @@
 @Composable
 private fun AvatarButton(enabled: Boolean) =
     MultilineButton(
-        enabled = enabled, icon = { AvatarIcon() }, label = { Text("Primary text") }
+        enabled = enabled,
+        colors = ButtonDefaults.filledTonalButtonColors(),
+        icon = { AvatarIcon() },
+        label = { Text("Primary text") }
     )
 
 @Composable
 private fun Avatar3SlotButton(enabled: Boolean) =
     Multiline3SlotButton(
         enabled = enabled,
+        colors = ButtonDefaults.filledTonalButtonColors(),
         icon = { AvatarIcon() },
         label = { Text("Primary text") },
         secondaryLabel = { Text("Secondary label") }
@@ -325,6 +330,7 @@
 @Composable
 private fun MultilineButton(
     enabled: Boolean,
+    colors: ButtonColors = ButtonDefaults.filledButtonColors(),
     icon: (@Composable BoxScope.() -> Unit)? = null,
     label: @Composable RowScope.() -> Unit = {
         Text(
@@ -338,13 +344,15 @@
         onClick = { /* Do something */ },
         icon = icon,
         label = label,
-        enabled = enabled
+        enabled = enabled,
+        colors = colors,
     )
 }
 
 @Composable
 private fun Multiline3SlotButton(
     enabled: Boolean,
+    colors: ButtonColors = ButtonDefaults.filledButtonColors(),
     icon: (@Composable BoxScope.() -> Unit)? = null,
     label: @Composable RowScope.() -> Unit = {
         Text(
@@ -366,7 +374,8 @@
         icon = icon,
         label = label,
         secondaryLabel = secondaryLabel,
-        enabled = enabled
+        enabled = enabled,
+        colors = colors,
     )
 }
 
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/CardDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/CardDemo.kt
new file mode 100644
index 0000000..45c3ad7
--- /dev/null
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/CardDemo.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3.demos
+
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
+import androidx.wear.compose.material3.AppCard
+import androidx.wear.compose.material3.CardDefaults
+import androidx.wear.compose.material3.Icon
+import androidx.wear.compose.material3.ListHeader
+import androidx.wear.compose.material3.MaterialTheme
+import androidx.wear.compose.material3.Text
+import androidx.wear.compose.material3.samples.AppCardSample
+import androidx.wear.compose.material3.samples.AppCardWithIconSample
+import androidx.wear.compose.material3.samples.CardSample
+import androidx.wear.compose.material3.samples.OutlinedAppCardSample
+import androidx.wear.compose.material3.samples.OutlinedCardSample
+import androidx.wear.compose.material3.samples.OutlinedTitleCardSample
+import androidx.wear.compose.material3.samples.R
+import androidx.wear.compose.material3.samples.TitleCardSample
+import androidx.wear.compose.material3.samples.TitleCardWithImageSample
+
+@Composable
+fun CardDemo() {
+    ScalingLazyColumn(
+        modifier = Modifier.fillMaxSize(),
+        horizontalAlignment = Alignment.CenterHorizontally,
+    ) {
+        item { ListHeader { Text("Card") } }
+        item { CardSample() }
+        item { OutlinedCardSample() }
+
+        item { ListHeader { Text("App card") } }
+        item { AppCardSample() }
+        item { AppCardWithIconSample() }
+        item { OutlinedAppCardSample() }
+
+        item { ListHeader { Text("Title card") } }
+        item { TitleCardSample() }
+        item { OutlinedTitleCardSample() }
+
+        item { ListHeader { Text("Image card") } }
+        item {
+            AppCard(
+                onClick = { /* Do something */ },
+                appName = { Text("App name") },
+                appImage = {
+                    Icon(
+                        Icons.Filled.Favorite,
+                        contentDescription = "favourites",
+                        modifier = Modifier.size(CardDefaults.AppImageSize)
+                    )
+                },
+                title = { Text("Card title") },
+                time = { Text("now") },
+                colors = CardDefaults.imageCardColors(
+                    containerPainter = CardDefaults.imageWithScrimBackgroundPainter(
+                        backgroundImagePainter = painterResource(id = R.drawable.backgroundimage)
+                    ),
+                    contentColor = MaterialTheme.colorScheme.onSurface,
+                    titleColor = MaterialTheme.colorScheme.onSurface
+                ),
+                modifier = Modifier.semantics { contentDescription = "Background image" }
+            ) {
+                Text("Card content")
+            }
+        }
+        item { TitleCardWithImageSample() }
+    }
+}
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/IconButtonDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/IconButtonDemo.kt
index 4ce1f5d..57c962e 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/IconButtonDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/IconButtonDemo.kt
@@ -69,7 +69,7 @@
         }
         item {
             ListHeader {
-                Text("FilledTonalIconButton")
+                Text("Filled Tonal")
             }
         }
         item {
@@ -86,7 +86,7 @@
         }
         item {
             ListHeader {
-                Text("FilledIconButton")
+                Text("Filled")
             }
         }
         item {
@@ -103,7 +103,7 @@
         }
         item {
             ListHeader {
-                Text("OutlinedIconButton")
+                Text("Outlined")
             }
         }
         item {
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/IconToggleButtonDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/IconToggleButtonDemo.kt
new file mode 100644
index 0000000..18affc7
--- /dev/null
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/IconToggleButtonDemo.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3.demos
+
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.width
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
+import androidx.wear.compose.material3.Icon
+import androidx.wear.compose.material3.IconButtonDefaults
+import androidx.wear.compose.material3.IconToggleButton
+import androidx.wear.compose.material3.ListHeader
+import androidx.wear.compose.material3.Text
+import androidx.wear.compose.material3.samples.IconToggleButtonSample
+import androidx.wear.compose.material3.touchTargetAwareSize
+
+@Composable
+fun IconToggleButtonDemo() {
+    ScalingLazyColumn(
+        modifier = Modifier.fillMaxSize(),
+        horizontalAlignment = Alignment.CenterHorizontally,
+    ) {
+        item {
+            ListHeader {
+                Text("Icon Toggle Button")
+            }
+        }
+        item {
+            Row {
+                IconToggleButtonSample() // Enabled
+                Spacer(modifier = Modifier.width(5.dp))
+                IconToggleButtonsDemo(enabled = true, checked = false) // Unchecked and enabled
+            }
+        }
+        item {
+            Row {
+                IconToggleButtonsDemo(enabled = false, checked = true) // Checked and disabled
+                Spacer(modifier = Modifier.width(5.dp))
+                IconToggleButtonsDemo(enabled = false, checked = false) // Unchecked and disabled
+            }
+        }
+        item {
+            ListHeader {
+                Text("Sizes")
+            }
+        }
+        item {
+            Row(verticalAlignment = Alignment.CenterVertically) {
+                Text("${IconButtonDefaults.LargeButtonSize.value.toInt()}dp")
+                Spacer(Modifier.width(4.dp))
+                IconToggleButtonsDemo(
+                    enabled = true,
+                    checked = true,
+                    size = IconButtonDefaults.LargeButtonSize
+                )
+            }
+        }
+        item {
+            Row(verticalAlignment = Alignment.CenterVertically) {
+                Text("${IconButtonDefaults.DefaultButtonSize.value.toInt()}dp")
+                Spacer(Modifier.width(4.dp))
+                IconToggleButtonsDemo(
+                    enabled = true,
+                    checked = true,
+                    size = IconButtonDefaults.DefaultButtonSize
+                )
+            }
+        }
+        item {
+            Row(verticalAlignment = Alignment.CenterVertically) {
+                Text("${IconButtonDefaults.SmallButtonSize.value.toInt()}dp")
+                Spacer(Modifier.width(4.dp))
+                IconToggleButtonsDemo(
+                    enabled = true,
+                    checked = true,
+                    size = IconButtonDefaults.SmallButtonSize
+                )
+            }
+        }
+        item {
+            Row(verticalAlignment = Alignment.CenterVertically) {
+                Text("${IconButtonDefaults.ExtraSmallButtonSize.value.toInt()}dp")
+                Spacer(Modifier.width(4.dp))
+                IconToggleButtonsDemo(
+                    enabled = true,
+                    checked = true,
+                    size = IconButtonDefaults.ExtraSmallButtonSize
+                )
+            }
+        }
+    }
+}
+
+@Composable
+private fun IconToggleButtonsDemo(
+    enabled: Boolean,
+    checked: Boolean,
+    size: Dp = IconButtonDefaults.DefaultButtonSize
+) {
+    IconToggleButton(
+        checked = checked,
+        enabled = enabled,
+        modifier = Modifier.touchTargetAwareSize(size),
+        onCheckedChange = {} // Do not update the state
+    ) {
+        Icon(
+            imageVector = Icons.Filled.Favorite,
+            contentDescription = "Flight Mode"
+        )
+    }
+}
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SelectionControlsDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SelectionControlsDemo.kt
new file mode 100644
index 0000000..1ade790
--- /dev/null
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/SelectionControlsDemo.kt
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3.demos
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+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.platform.LocalLayoutDirection
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
+import androidx.wear.compose.integration.demos.common.Centralize
+import androidx.wear.compose.integration.demos.common.ComposableDemo
+import androidx.wear.compose.integration.demos.common.DemoCategory
+import androidx.wear.compose.material3.Checkbox
+import androidx.wear.compose.material3.ListHeader
+import androidx.wear.compose.material3.RadioButton
+import androidx.wear.compose.material3.Switch
+import androidx.wear.compose.material3.Text
+import androidx.wear.compose.material3.samples.CheckboxSample
+import androidx.wear.compose.material3.samples.RadioButtonSample
+import androidx.wear.compose.material3.samples.RtlSwitchSample
+import androidx.wear.compose.material3.samples.SwitchSample
+
+val selectionControlsDemos = listOf(
+    DemoCategory(
+        "Samples",
+        listOf(
+            ComposableDemo("Checkbox sample") {
+                Centralize(Modifier.padding(horizontal = 10.dp)) {
+                    CheckboxSample()
+                }
+            },
+            ComposableDemo("Switch sample") {
+                Centralize(Modifier.padding(horizontal = 10.dp)) {
+                    SwitchSample()
+                }
+            },
+            ComposableDemo("Rtl Switch sample") {
+                Centralize(Modifier.padding(horizontal = 10.dp)) {
+                    RtlSwitchSample()
+                }
+            },
+            ComposableDemo("RadioButton sample") {
+                Centralize(Modifier.padding(horizontal = 10.dp)) {
+                    RadioButtonSample()
+                }
+            },
+        )
+    ),
+    DemoCategory("Demos", listOf(
+        ComposableDemo("Checkbox demos") {
+            CheckboxDemos()
+        },
+        ComposableDemo("Switch demos") {
+            SwitchDemos()
+        },
+        ComposableDemo("RadioButton demos") {
+            RadioButtonDemos()
+        }
+    ))
+)
+
+@Composable
+private fun CheckboxDemos() {
+    ScalingLazyColumn(
+        modifier = Modifier
+            .fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally,
+        verticalArrangement = Arrangement.Center
+    ) {
+        item {
+            ListHeader { Text(text = "Checkbox") }
+        }
+        item {
+            Row {
+                var checked1 by remember { mutableStateOf(false) }
+                Checkbox(checked = checked1, onCheckedChange = {
+                    checked1 = it
+                })
+                Spacer(modifier = Modifier.width(10.dp))
+                var checked2 by remember { mutableStateOf(true) }
+                Checkbox(checked = checked2, onCheckedChange = {
+                    checked2 = it
+                })
+            }
+        }
+        item {
+            ListHeader { Text(text = "Disabled Checkbox") }
+        }
+        item {
+            Row {
+                Checkbox(
+                    checked = false,
+                    enabled = false
+                )
+                Spacer(modifier = Modifier.width(10.dp))
+                Checkbox(
+                    checked = true,
+                    enabled = false
+                )
+            }
+        }
+    }
+}
+
+@Composable
+private fun SwitchDemos() {
+    ScalingLazyColumn(
+        modifier = Modifier
+            .fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally,
+        verticalArrangement = Arrangement.Center
+    ) {
+        item {
+            ListHeader { Text(text = "Switch") }
+        }
+        item {
+            Row {
+                var checked1 by remember { mutableStateOf(false) }
+                Switch(checked = checked1, onCheckedChange = {
+                    checked1 = it
+                })
+                Spacer(modifier = Modifier.width(10.dp))
+                var checked2 by remember { mutableStateOf(true) }
+                Switch(checked = checked2, onCheckedChange = {
+                    checked2 = it
+                })
+            }
+        }
+        item {
+            ListHeader { Text(text = "RTL Switch") }
+        }
+        item {
+            CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
+                Row {
+                    var checked1 by remember { mutableStateOf(true) }
+                    Switch(checked = checked1, onCheckedChange = {
+                        checked1 = it
+                    })
+                    Spacer(modifier = Modifier.width(10.dp))
+                    var checked2 by remember { mutableStateOf(false) }
+                    Switch(checked = checked2, onCheckedChange = {
+                        checked2 = it
+                    })
+                }
+            }
+        }
+        item {
+            ListHeader { Text(text = "Disabled Switch") }
+        }
+        item {
+            Row {
+                Switch(
+                    checked = false,
+                    enabled = false
+                )
+                Spacer(modifier = Modifier.width(10.dp))
+                Switch(
+                    checked = true,
+                    enabled = false
+                )
+            }
+        }
+    }
+}
+
+@Composable
+private fun RadioButtonDemos() {
+    ScalingLazyColumn(
+        modifier = Modifier
+            .fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally,
+        verticalArrangement = Arrangement.Center
+    ) {
+        item {
+            ListHeader { Text(text = "RadioButton") }
+        }
+        item {
+            Row {
+                var selected1 by remember { mutableStateOf(false) }
+                RadioButton(selected = selected1, onClick = {
+                    selected1 = !selected1
+                })
+                Spacer(modifier = Modifier.width(10.dp))
+                var selected2 by remember { mutableStateOf(true) }
+                RadioButton(selected = selected2, onClick = {
+                    selected2 = !selected2
+                })
+            }
+        }
+        item {
+            ListHeader { Text(text = "Disabled Radio") }
+        }
+        item {
+            Row {
+                RadioButton(
+                    selected = false,
+                    enabled = false
+                )
+                Spacer(modifier = Modifier.width(10.dp))
+                RadioButton(
+                    selected = true,
+                    enabled = false
+                )
+            }
+        }
+    }
+}
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TextButtonDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TextButtonDemo.kt
index 30e9be8..de378c5 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TextButtonDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TextButtonDemo.kt
@@ -59,7 +59,7 @@
         }
         item {
             ListHeader {
-                Text("FilledTonalTextButton")
+                Text("Filled Tonal")
             }
         }
         item {
@@ -77,7 +77,7 @@
         }
         item {
             ListHeader {
-                Text("FilledTextButton")
+                Text("Filled")
             }
         }
         item {
@@ -95,7 +95,7 @@
         }
         item {
             ListHeader {
-                Text("OutlinedTextButton")
+                Text("Outlined")
             }
         }
         item {
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TextToggleButtonDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TextToggleButtonDemo.kt
new file mode 100644
index 0000000..c1506db
--- /dev/null
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TextToggleButtonDemo.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3.demos
+
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.width
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
+import androidx.wear.compose.material3.ListHeader
+import androidx.wear.compose.material3.Text
+import androidx.wear.compose.material3.TextButtonDefaults
+import androidx.wear.compose.material3.TextToggleButton
+import androidx.wear.compose.material3.samples.TextToggleButtonSample
+import androidx.wear.compose.material3.touchTargetAwareSize
+
+@Composable
+fun TextToggleButtonDemo() {
+    ScalingLazyColumn(
+        modifier = Modifier.fillMaxSize(),
+        horizontalAlignment = Alignment.CenterHorizontally,
+    ) {
+        item {
+            ListHeader {
+                Text("Text Toggle Button")
+            }
+        }
+        item {
+            Row {
+                TextToggleButtonSample() // Enabled
+                Spacer(modifier = Modifier.width(5.dp))
+                TextToggleButtonsDemo(enabled = true, checked = false) // Enabled and unchecked
+            }
+        }
+        item {
+            Row {
+                TextToggleButtonsDemo(enabled = false, checked = true) // Checked and disabled
+                Spacer(modifier = Modifier.width(5.dp))
+                TextToggleButtonsDemo(enabled = false, checked = false) // Unchecked and disabled
+            }
+        }
+        item {
+            ListHeader {
+                Text("Sizes")
+            }
+        }
+        item {
+            Row(verticalAlignment = Alignment.CenterVertically) {
+                Text("${TextButtonDefaults.LargeButtonSize.value.toInt()}dp")
+                Spacer(Modifier.width(4.dp))
+                TextToggleButtonsDemo(
+                    enabled = true,
+                    checked = true,
+                    size = TextButtonDefaults.LargeButtonSize
+                )
+            }
+        }
+        item {
+            Row(verticalAlignment = Alignment.CenterVertically) {
+                Text("${TextButtonDefaults.DefaultButtonSize.value.toInt()}dp")
+                Spacer(Modifier.width(4.dp))
+                TextToggleButtonsDemo(
+                    enabled = true,
+                    checked = true,
+                    size = TextButtonDefaults.DefaultButtonSize
+                )
+            }
+        }
+        item {
+            Row(verticalAlignment = Alignment.CenterVertically) {
+                Text("${TextButtonDefaults.SmallButtonSize.value.toInt()}dp")
+                Spacer(Modifier.width(4.dp))
+                TextToggleButtonsDemo(
+                    enabled = true,
+                    checked = true,
+                    size = TextButtonDefaults.SmallButtonSize
+                )
+            }
+        }
+        item {
+            Row(verticalAlignment = Alignment.CenterVertically) {
+                Text("${TextButtonDefaults.ExtraSmallButtonSize.value.toInt()}dp")
+                Spacer(Modifier.width(4.dp))
+                TextToggleButtonsDemo(
+                    enabled = true,
+                    checked = true,
+                    size = TextButtonDefaults.ExtraSmallButtonSize
+                )
+            }
+        }
+    }
+}
+
+@Composable
+private fun TextToggleButtonsDemo(
+    enabled: Boolean,
+    checked: Boolean,
+    size: Dp = TextButtonDefaults.DefaultButtonSize
+) {
+    TextToggleButton(
+        checked = checked,
+        enabled = enabled,
+        modifier = Modifier.touchTargetAwareSize(size),
+        onCheckedChange = {} // Do not update the state,
+    ) {
+        Text(
+            text = if (checked) "On" else "Off"
+        )
+    }
+}
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt
index ea6ac44..c239cfe 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt
@@ -16,24 +16,16 @@
 
 package androidx.wear.compose.material3.demos
 
-import androidx.compose.ui.Alignment
-import androidx.wear.compose.foundation.lazy.AutoCenteringParams
-import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
 import androidx.wear.compose.integration.demos.common.Centralize
 import androidx.wear.compose.integration.demos.common.ComposableDemo
 import androidx.wear.compose.integration.demos.common.DemoCategory
-import androidx.wear.compose.material3.samples.AppCardSample
-import androidx.wear.compose.material3.samples.AppCardWithIconSample
-import androidx.wear.compose.material3.samples.CardSample
+import androidx.wear.compose.material3.samples.EdgeSwipeForSwipeToDismiss
 import androidx.wear.compose.material3.samples.FixedFontSize
-import androidx.wear.compose.material3.samples.OutlinedAppCardSample
-import androidx.wear.compose.material3.samples.OutlinedCardSample
-import androidx.wear.compose.material3.samples.OutlinedTitleCardSample
+import androidx.wear.compose.material3.samples.SimpleSwipeToDismissBox
+import androidx.wear.compose.material3.samples.StatefulSwipeToDismissBox
 import androidx.wear.compose.material3.samples.StepperSample
 import androidx.wear.compose.material3.samples.StepperWithIntegerSample
 import androidx.wear.compose.material3.samples.StepperWithRangeSemanticsSample
-import androidx.wear.compose.material3.samples.TitleCardSample
-import androidx.wear.compose.material3.samples.TitleCardWithImageSample
 
 val WearMaterial3Demos = DemoCategory(
     "Material 3",
@@ -61,26 +53,9 @@
                 },
             )
         ),
-        DemoCategory(
-            "Card",
-            listOf(
-                ComposableDemo("Samples") {
-                    ScalingLazyColumn(
-                        horizontalAlignment = Alignment.CenterHorizontally,
-                        autoCentering = AutoCenteringParams(itemIndex = 0)
-                    ) {
-                        item { CardSample() }
-                        item { AppCardSample() }
-                        item { AppCardWithIconSample() }
-                        item { TitleCardSample() }
-                        item { TitleCardWithImageSample() }
-                        item { OutlinedCardSample() }
-                        item { OutlinedAppCardSample() }
-                        item { OutlinedTitleCardSample() }
-                    }
-                }
-            )
-        ),
+        ComposableDemo("Card") {
+            CardDemo()
+        },
         ComposableDemo("Text Button") {
             TextButtonDemo()
         },
@@ -110,15 +85,33 @@
             "Slider",
             SliderDemos
         ),
-        ComposableDemo("List Headers") {
+        ComposableDemo("List Header") {
             Centralize {
                 ListHeaderDemo()
             }
         },
+        ComposableDemo("Text Toggle Button") {
+            TextToggleButtonDemo()
+        },
+        ComposableDemo("Icon Toggle Button") {
+            IconToggleButtonDemo()
+        },
         ComposableDemo(
             title = "Fixed Font Size"
         ) {
             Centralize { FixedFontSize() }
-        }
+        },
+        DemoCategory(
+            title = "Selection Controls",
+            selectionControlsDemos
+        ),
+        DemoCategory(
+            title = "Swipe To Dismiss",
+            listOf(
+                ComposableDemo("Simple") { SimpleSwipeToDismissBox(it.navigateBack) },
+                ComposableDemo("Stateful") { StatefulSwipeToDismissBox() },
+                ComposableDemo("Edge swipe") { EdgeSwipeForSwipeToDismiss(it.navigateBack) },
+            )
+        ),
     )
 )
diff --git a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/CardSample.kt b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/CardSample.kt
index 4e5af0f..17424be 100644
--- a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/CardSample.kt
+++ b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/CardSample.kt
@@ -19,10 +19,14 @@
 import androidx.annotation.Sampled
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
 import androidx.wear.compose.material3.AppCard
 import androidx.wear.compose.material3.Card
 import androidx.wear.compose.material3.CardDefaults
@@ -47,8 +51,8 @@
 fun AppCardSample() {
     AppCard(
         onClick = { /* Do something */ },
-        appName = { Text("AppName") },
-        title = { Text("AppCard") },
+        appName = { Text("App name") },
+        title = { Text("Card title") },
         time = { Text("now") },
     ) {
         Text("Card content")
@@ -60,7 +64,7 @@
 fun AppCardWithIconSample() {
     AppCard(
         onClick = { /* Do something */ },
-        appName = { Text("AppName") },
+        appName = { Text("App name") },
         appImage = {
             Icon(
                 painter = painterResource(id = android.R.drawable.star_big_off),
@@ -70,7 +74,7 @@
                     .wrapContentSize(align = Alignment.Center),
             )
         },
-        title = { Text("AppCard with icon") },
+        title = { Text("Card title") },
         time = { Text("now") },
     ) {
         Text("Card content")
@@ -82,7 +86,7 @@
 fun TitleCardSample() {
     TitleCard(
         onClick = { /* Do something */ },
-        title = { Text("TitleCard") },
+        title = { Text("Title card") },
         time = { Text("now") },
     ) {
         Text("Card content")
@@ -94,14 +98,16 @@
 fun TitleCardWithImageSample() {
     TitleCard(
         onClick = { /* Do something */ },
-        title = { Text("TitleCard With an ImageBackground") },
+        title = { Text("Card title") },
+        time = { Text("now") },
         colors = CardDefaults.imageCardColors(
             containerPainter = CardDefaults.imageWithScrimBackgroundPainter(
                 backgroundImagePainter = painterResource(id = R.drawable.backgroundimage)
             ),
             contentColor = MaterialTheme.colorScheme.onSurface,
             titleColor = MaterialTheme.colorScheme.onSurface
-        )
+        ),
+        modifier = Modifier.semantics { contentDescription = "Background image" }
     ) {
         Text("Card content")
     }
@@ -113,7 +119,7 @@
     OutlinedCard(
         onClick = { /* Do something */ },
     ) {
-        Text("OutlinedCard")
+        Text("Outlined card")
     }
 }
 
@@ -122,8 +128,16 @@
 fun OutlinedAppCardSample() {
     AppCard(
         onClick = { /* Do something */ },
-        appName = { Text("AppName") },
-        title = { Text("Outlined AppCard") },
+        appName = { Text("App name") },
+        appImage = {
+            Icon(
+                Icons.Filled.Favorite,
+                contentDescription = "favourites",
+                modifier = Modifier.size(CardDefaults.AppImageSize)
+            )
+        },
+        title = { Text("App card") },
+        time = { Text("now") },
         colors = CardDefaults.outlinedCardColors(),
         border = CardDefaults.outlinedCardBorder(),
     ) {
@@ -136,7 +150,8 @@
 fun OutlinedTitleCardSample() {
     TitleCard(
         onClick = { /* Do something */ },
-        title = { Text("Outlined TitleCard") },
+        title = { Text("Title card") },
+        time = { Text("now") },
         colors = CardDefaults.outlinedCardColors(),
         border = CardDefaults.outlinedCardBorder(),
     ) {
diff --git a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/IconToggleButtonSample.kt b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/IconToggleButtonSample.kt
new file mode 100644
index 0000000..8f01225
--- /dev/null
+++ b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/IconToggleButtonSample.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
+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.wear.compose.material3.Icon
+import androidx.wear.compose.material3.IconToggleButton
+
+@Sampled
+@Composable
+fun IconToggleButtonSample() {
+    var checked by remember { mutableStateOf(true) }
+    IconToggleButton(
+        checked = checked,
+        onCheckedChange = { checked = !checked }
+    ) {
+        Icon(
+            imageVector = Icons.Filled.Favorite,
+            contentDescription = "Flight Mode"
+        )
+    }
+}
diff --git a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ListHeaderSample.kt b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ListHeaderSample.kt
index ef263e8..3b32885 100644
--- a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ListHeaderSample.kt
+++ b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ListHeaderSample.kt
@@ -29,7 +29,7 @@
 @Composable
 fun ListHeaderSample() {
     ListHeader {
-        Text("List Header")
+        Text("Header")
     }
 }
 
@@ -37,7 +37,7 @@
 @Composable
 fun ListSubheaderSample() {
     ListSubheader {
-        Text("List Subheader")
+        Text("Subheader")
     }
 }
 
@@ -45,7 +45,7 @@
 @Composable
 fun ListSubheaderWithIconSample() {
     ListSubheader(
-        label = { Text(text = "List Subheader") },
+        label = { Text(text = "Subheader") },
         icon = { Icon(imageVector = Icons.Outlined.Home, "home") }
     )
 }
diff --git a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/SelectionControlsSample.kt b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/SelectionControlsSample.kt
new file mode 100644
index 0000000..b47c4c6
--- /dev/null
+++ b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/SelectionControlsSample.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.wear.compose.material3.Checkbox
+import androidx.wear.compose.material3.RadioButton
+import androidx.wear.compose.material3.Switch
+
+@Sampled
+@Composable
+fun CheckboxSample() {
+    var checked by remember { mutableStateOf(false) }
+    Checkbox(checked = checked, onCheckedChange = { checked = it })
+}
+
+@Sampled
+@Composable
+fun SwitchSample() {
+    var checked by remember { mutableStateOf(false) }
+    Switch(checked = checked, onCheckedChange = { checked = it })
+}
+
+@Sampled
+@Composable
+fun RtlSwitchSample() {
+    var checked by remember { mutableStateOf(false) }
+    CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
+        Switch(checked = checked, onCheckedChange = { checked = it })
+    }
+}
+
+@Sampled
+@Composable
+fun RadioButtonSample() {
+    var selected by remember { mutableStateOf(false) }
+    RadioButton(selected = selected, onClick = { selected = !selected })
+}
diff --git a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/SwipeToDismissBoxSample.kt b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/SwipeToDismissBoxSample.kt
new file mode 100644
index 0000000..9f6bfcc
--- /dev/null
+++ b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/SwipeToDismissBoxSample.kt
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.horizontalScroll
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.saveable.rememberSaveableStateHolder
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.SwipeToDismissValue
+import androidx.wear.compose.foundation.edgeSwipeToDismiss
+import androidx.wear.compose.foundation.rememberSwipeToDismissBoxState
+import androidx.wear.compose.material3.Checkbox
+import androidx.wear.compose.material3.MaterialTheme
+import androidx.wear.compose.material3.SwipeToDismissBox
+import androidx.wear.compose.material3.Text
+
+@Sampled
+@Composable
+fun SimpleSwipeToDismissBox(
+    navigateBack: () -> Unit
+) {
+    SwipeToDismissBox(
+        onDismissed = navigateBack
+    ) { isBackground ->
+        if (isBackground) {
+            Box(
+                modifier = Modifier
+                    .fillMaxSize()
+                    .background(MaterialTheme.colorScheme.secondaryContainer)
+            )
+        } else {
+            Column(
+                modifier = Modifier
+                    .fillMaxSize()
+                    .background(MaterialTheme.colorScheme.primary),
+                horizontalAlignment = Alignment.CenterHorizontally,
+                verticalArrangement = Arrangement.Center,
+            ) {
+                Text("Swipe right to dismiss", color = MaterialTheme.colorScheme.onPrimary)
+            }
+        }
+    }
+}
+
+@Sampled
+@Composable
+fun StatefulSwipeToDismissBox() {
+    // State for managing a 2-level navigation hierarchy between
+    // MainScreen and ItemScreen composables.
+    // Alternatively, use SwipeDismissableNavHost from wear.compose.navigation.
+    var showMainScreen by remember { mutableStateOf(true) }
+    val saveableStateHolder = rememberSaveableStateHolder()
+
+    // Swipe gesture dismisses ItemScreen to return to MainScreen.
+    val state = rememberSwipeToDismissBoxState()
+    LaunchedEffect(state.currentValue) {
+        if (state.currentValue == SwipeToDismissValue.Dismissed) {
+            state.snapTo(SwipeToDismissValue.Default)
+            showMainScreen = !showMainScreen
+        }
+    }
+
+    // Hierarchy is ListScreen -> ItemScreen, so we show ListScreen as the background behind
+    // the ItemScreen, otherwise there's no background to show.
+    SwipeToDismissBox(
+        state = state,
+        userSwipeEnabled = !showMainScreen,
+        backgroundKey = if (!showMainScreen) "MainKey" else "Background",
+        contentKey = if (showMainScreen) "MainKey" else "ItemKey",
+    ) { isBackground ->
+
+        if (isBackground || showMainScreen) {
+            // Best practice would be to use State Hoisting and leave this composable stateless.
+            // Here, we want to support MainScreen being shown from different destinations
+            // (either in the foreground or in the background during swiping) - that can be achieved
+            // using SaveableStateHolder and rememberSaveable as shown below.
+            saveableStateHolder.SaveableStateProvider(
+                key = "MainKey",
+                content = {
+                    // Composable that maintains its own state
+                    // and can be shown in foreground or background.
+                    val checked = rememberSaveable { mutableStateOf(true) }
+                    Column(
+                        modifier = Modifier
+                            .fillMaxSize()
+                            .padding(horizontal = 8.dp, vertical = 8.dp),
+                        verticalArrangement = Arrangement.spacedBy(
+                            4.dp, Alignment.CenterVertically
+                        ),
+                    ) {
+                        Row(
+                            modifier = Modifier
+                                .height(40.dp)
+                                .background(
+                                    color = MaterialTheme.colorScheme.surface,
+                                    shape = CircleShape
+                                )
+                                .padding(horizontal = 12.dp),
+                            verticalAlignment = Alignment.CenterVertically,
+                        ) {
+                            Box(
+                                modifier = Modifier.clickable { showMainScreen = false }
+                            ) {
+                                Text("Item details")
+                            }
+                            Checkbox(
+                                checked = checked.value,
+                                onCheckedChange = { checked.value = it }
+                            )
+                        }
+                    }
+                }
+            )
+        } else {
+            Column(
+                modifier = Modifier
+                    .fillMaxSize()
+                    .background(MaterialTheme.colorScheme.primary),
+                horizontalAlignment = Alignment.CenterHorizontally,
+                verticalArrangement = Arrangement.Center,
+            ) {
+                Text("Show details here...", color = MaterialTheme.colorScheme.onPrimary)
+                Text("Swipe right to dismiss", color = MaterialTheme.colorScheme.onPrimary)
+            }
+        }
+    }
+}
+
+@Sampled
+@Composable
+fun EdgeSwipeForSwipeToDismiss(
+    navigateBack: () -> Unit
+) {
+    val state = rememberSwipeToDismissBoxState()
+
+    // When using Modifier.edgeSwipeToDismiss, it is required that the element on which the
+    // modifier applies exists within a SwipeToDismissBox which shares the same state.
+    SwipeToDismissBox(
+        state = state,
+        onDismissed = navigateBack
+    ) { isBackground ->
+        val horizontalScrollState = rememberScrollState(0)
+        if (isBackground) {
+            Box(
+                modifier = Modifier
+                    .fillMaxSize()
+                    .background(MaterialTheme.colorScheme.secondaryContainer)
+            )
+        } else {
+            Box(modifier = Modifier.fillMaxSize()) {
+                Text(
+                    modifier = Modifier
+                        .align(Alignment.Center)
+                        .edgeSwipeToDismiss(state)
+                        .horizontalScroll(horizontalScrollState),
+                    text = "This text can be scrolled horizontally - to dismiss, swipe " +
+                        "right from the left edge of the screen (called Edge Swiping)",
+                )
+            }
+        }
+    }
+}
diff --git a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/TextToggleButtonSample.kt b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/TextToggleButtonSample.kt
new file mode 100644
index 0000000..c680a7c
--- /dev/null
+++ b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/TextToggleButtonSample.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3.samples
+
+import androidx.annotation.Sampled
+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.wear.compose.material3.Text
+import androidx.wear.compose.material3.TextToggleButton
+
+@Sampled
+@Composable
+fun TextToggleButtonSample() {
+    var checked by remember { mutableStateOf(true) }
+    TextToggleButton(
+        checked = checked,
+        onCheckedChange = { checked = !checked }
+    ) {
+        Text(
+            text = if (checked) "On" else "Off"
+        )
+    }
+}
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt
index eb4b7c0..a5da87e 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonTest.kt
@@ -386,7 +386,7 @@
             status = Status.Disabled,
             colors = { ButtonDefaults.filledButtonColors() },
             expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
-                alpha = DisabledBorderAndContainerAlpha
+                alpha = DisabledContainerAlpha
             ) },
             expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
                 alpha = ContentAlpha.disabled
@@ -412,7 +412,7 @@
             status = Status.Disabled,
             colors = { ButtonDefaults.filledTonalButtonColors() },
             expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
-                alpha = DisabledBorderAndContainerAlpha
+                alpha = DisabledContainerAlpha
             ) },
             expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
                 alpha = ContentAlpha.disabled
@@ -461,9 +461,7 @@
         rule.verifyButtonColors(
             status = Status.Disabled,
             colors = { ButtonDefaults.childButtonColors() },
-            expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
-                alpha = DisabledBorderAndContainerAlpha
-            ) },
+            expectedContainerColor = { Color.Transparent },
             expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
                 alpha = ContentAlpha.disabled
             ) },
@@ -563,7 +561,9 @@
     fun gives_disabled_outlined_button_correct_border_colors() {
         val status = Status.Disabled
         rule.verifyButtonBorderColor(
-            expectedBorderColor = { MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f) },
+            expectedBorderColor = {
+                MaterialTheme.colorScheme.onSurface.copy(alpha = DisabledBorderAlpha)
+            },
             content = { modifier: Modifier ->
                 OutlinedButton(
                     onClick = {},
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonScreenshotTest.kt
index e1bcf92..07d2ceb 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonScreenshotTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonScreenshotTest.kt
@@ -70,7 +70,7 @@
     }
 
     @Test
-    fun filled_icon_button_disabled() = verifyScreenshot("icon_button_disabled") {
+    fun filled_icon_button_disabled() = verifyScreenshot {
         sampleFilledIconButton(enabled = false, isCompact = false)
     }
 
@@ -80,7 +80,7 @@
     }
 
     @Test
-    fun filled_tonal_icon_button_disabled() = verifyScreenshot("icon_button_disabled") {
+    fun filled_tonal_icon_button_disabled() = verifyScreenshot {
         sampleFilledTonalIconButton(enabled = false, isCompact = false)
     }
 
@@ -110,7 +110,7 @@
     }
 
     @Test
-    fun filled_compact_icon_button_disabled() = verifyScreenshot("compact_icon_button_disabled") {
+    fun filled_compact_icon_button_disabled() = verifyScreenshot {
         sampleFilledIconButton(enabled = false, isCompact = true)
     }
 
@@ -121,7 +121,7 @@
 
     @Test
     fun filled_tonal_compact_icon_button_disabled() =
-        verifyScreenshot("compact_icon_button_disabled") {
+        verifyScreenshot {
             sampleFilledTonalIconButton(enabled = false, isCompact = true)
         }
 
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonTest.kt
index d12eb43..f6f9e3b 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconButtonTest.kt
@@ -31,6 +31,8 @@
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.test.SemanticsMatcher
 import androidx.compose.ui.test.assert
 import androidx.compose.ui.test.assertHasClickAction
@@ -191,6 +193,27 @@
     }
 
     @Test
+    fun allows_custom_role() {
+        val overrideRole = Role.Checkbox
+
+        rule.setContentWithTheme {
+            IconButton(
+                onClick = {},
+                modifier = Modifier.testTag(TEST_TAG).semantics { role = overrideRole }
+            ) {
+                TestImage()
+            }
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assert(
+            SemanticsMatcher.expectValue(
+                SemanticsProperties.Role,
+                overrideRole
+            )
+        )
+    }
+
+    @Test
     fun gives_default_button_correct_tap_size() {
         rule.verifyTapSize(DefaultButtonSize) { modifier ->
             IconButton(
@@ -317,7 +340,7 @@
             status = Status.Disabled,
             colors = { IconButtonDefaults.filledIconButtonColors() },
             expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
-                alpha = DisabledBorderAndContainerAlpha
+                alpha = DisabledContainerAlpha
             ) },
             expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
                 alpha = ContentAlpha.disabled
@@ -343,7 +366,7 @@
             status = Status.Disabled,
             colors = { IconButtonDefaults.filledTonalIconButtonColors() },
             expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
-                alpha = DisabledBorderAndContainerAlpha
+                alpha = DisabledContainerAlpha
             ) },
             expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
                 alpha = ContentAlpha.disabled
@@ -397,7 +420,7 @@
         val status = Status.Disabled
         rule.verifyButtonBorderColor(
             expectedBorderColor = {
-                MaterialTheme.colorScheme.onSurface.copy(alpha = DisabledBorderAndContainerAlpha)
+                MaterialTheme.colorScheme.onSurface.copy(alpha = DisabledBorderAlpha)
             },
             content = { modifier: Modifier ->
                 OutlinedIconButton(
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconToggleButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconToggleButtonTest.kt
new file mode 100644
index 0000000..25cdf7d
--- /dev/null
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconToggleButtonTest.kt
@@ -0,0 +1,761 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3
+
+import android.os.Build
+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.padding
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.testutils.assertContainsColor
+import androidx.compose.testutils.assertShape
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.compositeOver
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.test.SemanticsMatcher
+import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.assertHasClickAction
+import androidx.compose.ui.test.assertHeightIsEqualTo
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.assertIsOff
+import androidx.compose.ui.test.assertIsOn
+import androidx.compose.ui.test.assertIsToggleable
+import androidx.compose.ui.test.assertWidthIsEqualTo
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+
+class IconToggleButtonTest {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun supports_testTag() {
+        rule.setContentWithTheme {
+            IconToggleButton(
+                checked = true,
+                onCheckedChange = {},
+                content = { TestImage() },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(TEST_TAG).assertExists()
+    }
+
+    @Test
+    fun has_clickAction_when_enabled() {
+        rule.setContentWithTheme {
+            IconToggleButton(
+                enabled = true,
+                checked = true,
+                onCheckedChange = {},
+                content = { TestImage() },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(TEST_TAG).assertHasClickAction()
+    }
+
+    @Test
+    fun has_clickAction_when_disabled() {
+        rule.setContentWithTheme {
+            IconToggleButton(
+                enabled = false,
+                checked = true,
+                onCheckedChange = {},
+                content = { TestImage() },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(TEST_TAG).assertHasClickAction()
+    }
+
+    @Test
+    fun is_toggleable() {
+        rule.setContentWithTheme {
+            IconToggleButton(
+                enabled = true,
+                checked = true,
+                onCheckedChange = {},
+                content = { TestImage() },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(TEST_TAG).assertIsToggleable()
+    }
+
+    @Test
+    fun is_correctly_enabled() {
+        rule.setContentWithTheme {
+            IconToggleButton(
+                enabled = true,
+                checked = true,
+                onCheckedChange = {},
+                content = { TestImage() },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(TEST_TAG).assertIsEnabled()
+    }
+
+    @Test
+    fun is_correctly_disabled() {
+        rule.setContentWithTheme {
+            IconToggleButton(
+                enabled = false,
+                checked = true,
+                onCheckedChange = {},
+                content = { TestImage() },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(TEST_TAG).assertIsNotEnabled()
+    }
+
+    @Test
+    fun is_on_when_checked() {
+        rule.setContentWithTheme {
+            IconToggleButton(
+                checked = true,
+                onCheckedChange = {},
+                content = { TestImage() },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(TEST_TAG).assertIsOn()
+    }
+
+    @Test
+    fun is_off_when_unchecked() {
+        rule.setContentWithTheme {
+            IconToggleButton(
+                checked = false,
+                onCheckedChange = {},
+                content = { TestImage() },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(TEST_TAG).assertIsOff()
+    }
+
+    @Test
+    fun responds_to_toggle_on() {
+        rule.setContentWithTheme {
+            val (checked, onCheckedChange) = remember { mutableStateOf(false) }
+            IconToggleButton(
+                checked = checked,
+                onCheckedChange = onCheckedChange,
+                content = { TestImage() },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(TEST_TAG)
+            .assertIsOff()
+            .performClick()
+            .assertIsOn()
+    }
+
+    @Test
+    fun responds_to_toggle_off() {
+        rule.setContentWithTheme {
+            val (checked, onCheckedChange) = remember { mutableStateOf(true) }
+            IconToggleButton(
+                checked = checked,
+                onCheckedChange = onCheckedChange,
+                content = { TestImage() },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(TEST_TAG)
+            .assertIsOn()
+            .performClick()
+            .assertIsOff()
+    }
+
+    @Test
+    fun does_not_toggle_when_disabled() {
+        rule.setContentWithTheme {
+            val (checked, onCheckedChange) = remember { mutableStateOf(false) }
+            IconToggleButton(
+                enabled = false,
+                checked = checked,
+                onCheckedChange = onCheckedChange,
+                content = { TestImage() },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(TEST_TAG)
+            .assertIsOff()
+            .performClick()
+            .assertIsOff()
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun is_circular_under_ltr() =
+        rule.isShape(
+            shape = CircleShape,
+            layoutDirection = LayoutDirection.Ltr,
+            shapeColorComposable = { shapeColor() }
+        ) {
+            IconToggleButton(
+                enabled = true,
+                checked = true,
+                onCheckedChange = { },
+                content = { },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun is_circular_under_rtl() =
+        rule.isShape(
+            shape = CircleShape,
+            layoutDirection = LayoutDirection.Rtl,
+            shapeColorComposable = { shapeColor() }
+        ) {
+            IconToggleButton(
+                enabled = true,
+                checked = true,
+                onCheckedChange = { },
+                content = { },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun allows_shape_overrides() =
+        rule.isShape(
+            shape = RectangleShape,
+            layoutDirection = LayoutDirection.Ltr,
+            shapeColorComposable = { shapeColor() }
+        ) {
+            IconToggleButton(
+                enabled = true,
+                checked = true,
+                shape = RectangleShape,
+                onCheckedChange = { },
+                content = { },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+    @Test
+    fun gives_default_correct_tapSize() =
+        rule.verifyTapSize(52.dp) {
+            IconToggleButton(
+                enabled = true,
+                checked = true,
+                onCheckedChange = { },
+                content = { },
+                modifier = it.touchTargetAwareSize(IconButtonDefaults.DefaultButtonSize)
+            )
+        }
+
+    @Test
+    fun gives_small_correct_tapSize() =
+        rule.verifyTapSize(48.dp) {
+            IconToggleButton(
+                enabled = true,
+                checked = true,
+                onCheckedChange = { },
+                content = { },
+                modifier = it.touchTargetAwareSize(IconButtonDefaults.SmallButtonSize)
+            )
+        }
+
+    @Test
+    fun gives_extraSmall_correct_tapSize() =
+        rule.verifyTapSize(48.dp) {
+            IconToggleButton(
+                enabled = true,
+                checked = true,
+                onCheckedChange = { },
+                content = { },
+                modifier = it.touchTargetAwareSize(IconButtonDefaults.ExtraSmallButtonSize)
+            )
+        }
+
+    @Test
+    fun gives_large_correct_tapSize() =
+        rule.verifyTapSize(60.dp) {
+            IconToggleButton(
+                enabled = true,
+                checked = true,
+                onCheckedChange = { },
+                content = { },
+                modifier = it.touchTargetAwareSize(IconButtonDefaults.LargeButtonSize)
+            )
+        }
+
+    @Test
+    fun gives_default_correct_size() =
+        rule.verifyActualSize(52.dp) {
+            IconToggleButton(
+                enabled = true,
+                checked = true,
+                onCheckedChange = { },
+                content = { },
+                modifier = it.touchTargetAwareSize(IconButtonDefaults.DefaultButtonSize)
+            )
+        }
+
+    @Test
+    fun gives_small_correct_size() =
+        rule.verifyActualSize(48.dp) {
+            IconToggleButton(
+                enabled = true,
+                checked = true,
+                onCheckedChange = { },
+                content = { },
+                modifier = it.touchTargetAwareSize(IconButtonDefaults.SmallButtonSize)
+            )
+        }
+
+    @Test
+    fun gives_extraSmall_correct_size() =
+        rule.verifyActualSize(48.dp) {
+            IconToggleButton(
+                enabled = true,
+                checked = true,
+                onCheckedChange = { },
+                content = { },
+                modifier = it.touchTargetAwareSize(IconButtonDefaults.ExtraSmallButtonSize)
+            )
+        }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun gives_large_correct_size() =
+        rule.verifyActualSize(60.dp) {
+            IconToggleButton(
+                enabled = true,
+                checked = true,
+                onCheckedChange = { },
+                content = { },
+                modifier = it.touchTargetAwareSize(IconButtonDefaults.LargeButtonSize)
+            )
+        }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun gives_checked_primary_colors() =
+        rule.verifyIconToggleButtonColors(
+            status = Status.Enabled,
+            checked = true,
+            colors = { IconButtonDefaults.iconToggleButtonColors() },
+            containerColor = { MaterialTheme.colorScheme.primary },
+            contentColor = { MaterialTheme.colorScheme.onPrimary }
+        )
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun gives_unchecked_surface_colors() =
+        rule.verifyIconToggleButtonColors(
+            status = Status.Enabled,
+            checked = false,
+            colors = { IconButtonDefaults.iconToggleButtonColors() },
+            containerColor = { MaterialTheme.colorScheme.surface },
+            contentColor = { MaterialTheme.colorScheme.onSurfaceVariant }
+        )
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun gives_disabled_unchecked_surface_colors_with_alpha() =
+        rule.verifyIconToggleButtonColors(
+            status = Status.Disabled,
+            checked = false,
+            colors = { IconButtonDefaults.iconToggleButtonColors() },
+            containerColor = { MaterialTheme.colorScheme.surface },
+            contentColor = { MaterialTheme.colorScheme.onSurfaceVariant }
+        )
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun gives_disabled_primary_checked_contrasting_content_color() =
+        rule.verifyIconToggleButtonColors(
+            status = Status.Disabled,
+            checked = true,
+            colors = { IconButtonDefaults.iconToggleButtonColors() },
+            containerColor = { MaterialTheme.colorScheme.primary },
+            contentColor = { MaterialTheme.colorScheme.onPrimary },
+        )
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun allows_custom_checked_background_override() {
+        val overrideColor = Color.Yellow
+
+        rule.verifyIconToggleButtonColors(
+            status = Status.Enabled,
+            checked = true,
+            colors = {
+                IconButtonDefaults.iconToggleButtonColors(
+                    checkedContainerColor = overrideColor
+                )
+            },
+            containerColor = { overrideColor },
+            contentColor = { MaterialTheme.colorScheme.onPrimary }
+        )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun allows_custom_checked_content_override() {
+        val overrideColor = Color.Green
+
+        rule.verifyIconToggleButtonColors(
+            status = Status.Enabled,
+            checked = true,
+            colors = {
+                IconButtonDefaults.iconToggleButtonColors(
+                    checkedContentColor = overrideColor
+                )
+            },
+            containerColor = { MaterialTheme.colorScheme.primary },
+            contentColor = { overrideColor }
+        )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun allows_custom_unchecked_background_override() {
+        val overrideColor = Color.Red
+
+        rule.verifyIconToggleButtonColors(
+            status = Status.Enabled,
+            checked = false,
+            colors = {
+                IconButtonDefaults.iconToggleButtonColors(
+                    uncheckedContainerColor = overrideColor
+                )
+            },
+            containerColor = { overrideColor },
+            contentColor = { MaterialTheme.colorScheme.onSurfaceVariant }
+        )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun allows_custom_unchecked_content_override() {
+        val overrideColor = Color.Green
+
+        rule.verifyIconToggleButtonColors(
+            status = Status.Enabled,
+            checked = false,
+            colors = {
+                IconButtonDefaults.iconToggleButtonColors(
+                    uncheckedContentColor = overrideColor
+                )
+            },
+            containerColor = { MaterialTheme.colorScheme.surface },
+            contentColor = { overrideColor }
+        )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun allows_custom_checked_disabled_background_override() {
+        val overrideColor = Color.Yellow
+
+        rule.verifyIconToggleButtonColors(
+            status = Status.Disabled,
+            checked = true,
+            colors = {
+                IconButtonDefaults.iconToggleButtonColors(
+                    // Apply the content color override for the content alpha to be applied
+                    checkedContainerColor = overrideColor
+                )
+            },
+            containerColor = { overrideColor },
+            contentColor = { MaterialTheme.colorScheme.onPrimary }
+        )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun allows_custom_checked_disabled_content_override() {
+        val overrideColor = Color.Green
+
+        rule.verifyIconToggleButtonColors(
+            status = Status.Disabled,
+            checked = true,
+            colors = {
+                IconButtonDefaults.iconToggleButtonColors(
+                    // Apply the content color override for the content alpha to be applied
+                    checkedContentColor = overrideColor
+                )
+            },
+            containerColor = { MaterialTheme.colorScheme.primary },
+            contentColor = { overrideColor }
+        )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun allows_custom_unchecked_disabled_background_override() {
+        val overrideColor = Color.Red
+
+        rule.verifyIconToggleButtonColors(
+            status = Status.Disabled,
+            checked = false,
+            colors = {
+                IconButtonDefaults.iconToggleButtonColors(
+                    // Apply the content color override for the content alpha to be applied
+                    uncheckedContainerColor = overrideColor
+                )
+            },
+            containerColor = { overrideColor },
+            contentColor = { MaterialTheme.colorScheme.onSurfaceVariant }
+        )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun allows_custom_unchecked_disabled_content_override() {
+        val overrideColor = Color.Green
+
+        rule.verifyIconToggleButtonColors(
+            status = Status.Disabled,
+            checked = false,
+            colors = {
+                IconButtonDefaults.iconToggleButtonColors(
+                    // Apply the content color override for the content alpha to be applied
+                    uncheckedContentColor = overrideColor
+                )
+            },
+            contentColor = { overrideColor },
+            containerColor = { MaterialTheme.colorScheme.surface }
+        )
+    }
+
+    @Test
+    fun default_role_checkbox() {
+        rule.setContentWithTheme {
+            IconToggleButton(
+                checked = false,
+                onCheckedChange = {},
+                enabled = false,
+                content = { TestImage() },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assert(
+            SemanticsMatcher.expectValue(
+                SemanticsProperties.Role,
+                Role.Checkbox
+            )
+        )
+    }
+
+    @Test
+    fun allows_custom_role() {
+        val overrideRole = Role.Button
+
+        rule.setContentWithTheme {
+            IconToggleButton(
+                checked = false,
+                onCheckedChange = {},
+                enabled = false,
+                content = { TestImage() },
+                modifier = Modifier.testTag(TEST_TAG).semantics { role = overrideRole }
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assert(
+            SemanticsMatcher.expectValue(
+                SemanticsProperties.Role,
+                overrideRole
+            )
+        )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    private fun ComposeContentTestRule.verifyIconToggleButtonColors(
+        status: Status,
+        checked: Boolean,
+        colors: @Composable () -> ToggleButtonColors,
+        containerColor: @Composable () -> Color,
+        contentColor: @Composable () -> Color,
+    ) {
+        verifyColors(
+            status = status,
+            expectedContainerColor = containerColor,
+            expectedContentColor = contentColor,
+            content = {
+                var actualContentColor = Color.Transparent
+                IconToggleButton(
+                    onCheckedChange = { },
+                    enabled = status.enabled(),
+                    checked = checked,
+                    colors = colors(),
+                    modifier = Modifier.testTag(TEST_TAG),
+                ) {
+                    actualContentColor = LocalContentColor.current
+                }
+                return@verifyColors actualContentColor
+            }
+        )
+    }
+
+    @Composable
+    private fun shapeColor(): Color {
+        return IconButtonDefaults.iconToggleButtonColors().containerColor(
+            enabled = true,
+            checked = true
+        ).value
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    private fun ComposeContentTestRule.isShape(
+        shape: Shape = CircleShape,
+        layoutDirection: LayoutDirection,
+        padding: Dp = 0.dp,
+        backgroundColor: Color = Color.Red,
+        shapeColorComposable: @Composable () -> Color,
+        content: @Composable () -> Unit
+    ) {
+        var shapeColor = Color.Transparent
+        setContentWithTheme {
+            shapeColor = shapeColorComposable.invoke()
+            CompositionLocalProvider(LocalLayoutDirection provides layoutDirection) {
+                Box(
+                    Modifier
+                        .padding(padding)
+                        .background(backgroundColor)
+                ) {
+                    content()
+                }
+            }
+        }
+
+        this.waitForIdle()
+        onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertShape(
+                density = density,
+                shape = shape,
+                horizontalPadding = padding,
+                verticalPadding = padding,
+                backgroundColor = backgroundColor,
+                shapeOverlapPixelCount = 2.0f,
+                shapeColor = shapeColor
+            )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    private fun ComposeContentTestRule.verifyColors(
+        status: Status,
+        expectedContainerColor: @Composable () -> Color,
+        expectedContentColor: @Composable () -> Color,
+        applyAlphaForDisabled: Boolean = true,
+        content: @Composable () -> Color
+    ) {
+        val testBackgroundColor = Color.White
+        var finalExpectedContainerColor = Color.Transparent
+        var finalExpectedContent = Color.Transparent
+        var actualContentColor = Color.Transparent
+        setContentWithTheme {
+            finalExpectedContainerColor =
+                if (status.enabled() || !applyAlphaForDisabled) {
+                    expectedContainerColor()
+                } else {
+                    expectedContainerColor().copy(ContentAlpha.disabled)
+                }.compositeOver(testBackgroundColor)
+            finalExpectedContent =
+                if (status.enabled() || !applyAlphaForDisabled) {
+                    expectedContentColor()
+                } else {
+                    expectedContentColor().copy(ContentAlpha.disabled)
+                }
+            Box(
+                Modifier
+                    .fillMaxSize()
+                    .background(testBackgroundColor)
+            ) {
+                actualContentColor = content()
+            }
+        }
+        Assert.assertEquals(finalExpectedContent, actualContentColor)
+        onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertContainsColor(finalExpectedContainerColor)
+    }
+
+    private fun ComposeContentTestRule.verifyActualSize(
+        expectedSize: Dp,
+        content: @Composable (modifier: Modifier) -> Unit
+    ) {
+        setContentWithTheme {
+            content(Modifier.testTag(TEST_TAG))
+        }
+        waitForIdle()
+
+        onNodeWithTag(TEST_TAG)
+            .assertHeightIsEqualTo(expectedSize)
+            .assertWidthIsEqualTo(expectedSize)
+    }
+}
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ListHeaderTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ListHeaderTest.kt
index 2cd45757..0eb56c3 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ListHeaderTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ListHeaderTest.kt
@@ -18,7 +18,10 @@
 
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.isHeading
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.test.SemanticsMatcher
+import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.text.TextStyle
@@ -58,7 +61,7 @@
             }
         }
 
-        rule.onNode(isHeading())
+        rule.assertNodeIsHeading(TEST_TAG)
     }
 
     @Test
@@ -71,7 +74,7 @@
             }
         }
 
-        rule.onNode(isHeading())
+        rule.assertNodeIsHeading(TEST_TAG)
     }
 
     @Test
@@ -103,4 +106,13 @@
 
         Assert.assertEquals(expectedTextStyle, actualTextStyle)
     }
+
+    private fun ComposeContentTestRule.assertNodeIsHeading(tag: String) {
+        onNodeWithTag(tag)
+            .assert(
+                SemanticsMatcher.keyIsDefined(
+                    SemanticsProperties.Heading
+                )
+            )
+    }
 }
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/SelectionControlsScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/SelectionControlsScreenshotTest.kt
new file mode 100644
index 0000000..810058b8
--- /dev/null
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/SelectionControlsScreenshotTest.kt
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3
+
+import android.os.Build
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.testutils.assertAgainstGolden
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.compositeOver
+import androidx.compose.ui.platform.LocalLayoutDirection
+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.compose.ui.unit.LayoutDirection
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.screenshot.AndroidXScreenshotTestRule
+import androidx.test.screenshot.matchers.MSSIMMatcher
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestName
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+class SelectionControlsScreenshotTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    @get:Rule
+    val screenshotRule = AndroidXScreenshotTestRule(SCREENSHOT_GOLDEN_PATH)
+
+    @get:Rule
+    val testName = TestName()
+
+    @Test
+    fun checkbox_checked_enabled() =
+        verifyScreenshot {
+            Checkbox(checked = true, enabled = true, modifier = testBackgroundModifier())
+        }
+
+    @Test
+    fun checkbox_unchecked_enabled() =
+        verifyScreenshot {
+            Checkbox(checked = false, enabled = true, modifier = testBackgroundModifier())
+        }
+
+    @Test
+    fun checkbox_checked_disabled() =
+        verifyScreenshot {
+            Checkbox(checked = true, enabled = false, modifier = testBackgroundModifier())
+        }
+
+    @Test
+    fun checkbox_unchecked_disabled() =
+        verifyScreenshot {
+            Checkbox(checked = false, enabled = false, modifier = testBackgroundModifier())
+        }
+
+    @Test
+    fun switch_checked_enabled() =
+        verifyScreenshot {
+            Switch(checked = true, enabled = true, modifier = testBackgroundModifier())
+        }
+
+    @Test
+    fun switch_unchecked_enabled() =
+        verifyScreenshot {
+            Switch(checked = false, enabled = true, modifier = testBackgroundModifier())
+        }
+
+    @Test
+    fun switch_checked_disabled() =
+        verifyScreenshot {
+            Switch(checked = true, enabled = false, modifier = testBackgroundModifier())
+        }
+
+    @Test
+    fun switch_unchecked_disabled() =
+        verifyScreenshot {
+            Switch(checked = false, enabled = false, modifier = testBackgroundModifier())
+        }
+
+    @Test
+    fun switch_rtl_checked_enabled() =
+        verifyScreenshot {
+            CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
+                Switch(checked = true, enabled = true, modifier = testBackgroundModifier())
+            }
+        }
+
+    @Test
+    fun switch_rtl_unchecked_enabled() =
+        verifyScreenshot {
+            CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
+                Switch(checked = false, enabled = true, modifier = testBackgroundModifier())
+            }
+        }
+
+    @Test
+    fun switch_rtl_checked_disabled() =
+        verifyScreenshot {
+            CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
+                Switch(checked = true, enabled = false, modifier = testBackgroundModifier())
+            }
+        }
+
+    @Test
+    fun switch_rtl_unchecked_disabled() =
+        verifyScreenshot {
+            CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
+                Switch(checked = false, enabled = false, modifier = testBackgroundModifier())
+            }
+        }
+
+    @Test
+    fun radiobutton_checked_enabled() =
+        verifyScreenshot {
+            RadioButton(selected = true, enabled = true, modifier = testBackgroundModifier())
+        }
+
+    @Test
+    fun radiobutton_unchecked_enabled() =
+        verifyScreenshot {
+            RadioButton(selected = false, enabled = true, modifier = testBackgroundModifier())
+        }
+
+    @Test
+    fun radiobutton_checked_disabled() =
+        verifyScreenshot {
+            RadioButton(selected = true, enabled = false, modifier = testBackgroundModifier())
+        }
+
+    @Test
+    fun radiobutton_unchecked_disabled() =
+        verifyScreenshot {
+            RadioButton(selected = false, enabled = false, modifier = testBackgroundModifier())
+        }
+
+    private fun verifyScreenshot(
+        threshold: Double = 0.98,
+        content: @Composable BoxScope.() -> Unit
+    ) {
+        rule.setContentWithTheme(composable = content)
+        rule.onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, testName.methodName, MSSIMMatcher(threshold))
+    }
+
+    @Composable
+    private fun testBackgroundModifier(): Modifier =
+        Modifier
+            .testTag(TEST_TAG)
+            .background(
+                MaterialTheme.colorScheme.primary
+                    .copy(alpha = 0.5f)
+                    .compositeOver(MaterialTheme.colorScheme.surface)
+            )
+}
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/SelectionControlsTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/SelectionControlsTest.kt
new file mode 100644
index 0000000..f5728b5
--- /dev/null
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/SelectionControlsTest.kt
@@ -0,0 +1,716 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3
+
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.testutils.assertContainsColor
+import androidx.compose.testutils.assertDoesNotContainColor
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+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.assertHasNoClickAction
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.assertIsNotSelected
+import androidx.compose.ui.test.assertIsOff
+import androidx.compose.ui.test.assertIsOn
+import androidx.compose.ui.test.assertIsSelected
+import androidx.compose.ui.test.assertWidthIsEqualTo
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.isSelectable
+import androidx.compose.ui.test.isToggleable
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.unit.dp
+import org.junit.Rule
+import org.junit.Test
+
+class SelectionControlsTest {
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun checkbox_supports_testtag() {
+        rule.setContentWithTheme {
+            Checkbox(
+                checked = true,
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertExists()
+    }
+
+    @Test
+    fun checkbox_is_expected_size() {
+        rule.setContentWithThemeForSizeAssertions {
+            Checkbox(
+                checked = true,
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }.assertHeightIsEqualTo(24.dp).assertWidthIsEqualTo(32.dp)
+    }
+
+    @Test
+    fun checkbox_has_role_checkbox_when_oncheckedchange_defined() {
+        rule.setContentWithTheme {
+            Checkbox(
+                checked = true,
+                onCheckedChange = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG)
+            .assert(
+                SemanticsMatcher.expectValue(
+                    SemanticsProperties.Role,
+                    Role.Checkbox
+                )
+            )
+    }
+
+    @Test
+    fun checkbox_has_no_clickaction_by_default() {
+        rule.setContentWithTheme {
+            Checkbox(
+                checked = true,
+                enabled = true,
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertHasNoClickAction()
+    }
+
+    @Test
+    fun checkbox_has_clickaction_when_oncheckedchange_defined() {
+        rule.setContentWithTheme {
+            Checkbox(
+                checked = true,
+                enabled = true,
+                onCheckedChange = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertHasClickAction()
+    }
+
+    @Test
+    fun checkbox_is_toggleable_when_oncheckedchange_defined() {
+        rule.setContentWithTheme {
+            Checkbox(
+                checked = true,
+                enabled = true,
+                onCheckedChange = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNode(isToggleable()).assertExists()
+    }
+
+    @Test
+    fun checkbox_is_correctly_enabled() {
+        rule.setContentWithTheme {
+            Checkbox(
+                checked = true,
+                enabled = true,
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsEnabled()
+    }
+
+    @Test
+    fun checkbox_is_correctly_disabled() {
+        // This test only applies when onCheckedChange is defined.
+        rule.setContentWithTheme {
+            Checkbox(
+                checked = true,
+                enabled = false,
+                onCheckedChange = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsNotEnabled()
+    }
+
+    @Test
+    fun checkbox_is_on_when_checked() {
+        // This test only applies when onCheckedChange is defined.
+        rule.setContentWithTheme {
+            Checkbox(
+                checked = true,
+                onCheckedChange = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsOn()
+    }
+
+    @Test
+    fun checkbox_is_off_when_checked() {
+        // This test only applies when onCheckedChange is defined.
+        rule.setContentWithTheme {
+            Checkbox(
+                checked = false,
+                onCheckedChange = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsOff()
+    }
+
+    @Test
+    fun checkbox_responds_to_toggle_on() {
+        // This test only applies when onCheckedChange is defined.
+        rule.setContentWithTheme {
+            val (checked, onCheckedChange) = remember { mutableStateOf(false) }
+            Checkbox(
+                checked = checked,
+                onCheckedChange = onCheckedChange,
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .assertIsOff()
+            .performClick()
+            .assertIsOn()
+    }
+
+    @Test
+    fun checkbox_responds_to_toggle_off() {
+        // This test only applies when onCheckedChange is defined.
+        rule.setContentWithTheme {
+            val (checked, onCheckedChange) = remember { mutableStateOf(true) }
+            Checkbox(
+                checked = checked,
+                onCheckedChange = onCheckedChange,
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .assertIsOn()
+            .performClick()
+            .assertIsOff()
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun checkbox_checked_colors_are_customisable() {
+        val boxColor = Color.Green
+        val checkmarkColor = Color.Blue
+        rule.setContentWithTheme {
+            Checkbox(
+                checked = true,
+                colors = CheckboxDefaults.colors(
+                    checkedBoxColor = boxColor,
+                    checkedCheckmarkColor = checkmarkColor
+                ),
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        val checkboxImage = rule.onNodeWithTag(TEST_TAG).captureToImage()
+        checkboxImage.assertContainsColor(boxColor)
+        checkboxImage.assertContainsColor(checkmarkColor)
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun checkbox_unchecked_colors_are_customisable() {
+        // NB checkmark is erased during animation, so we don't test uncheckedCheckmarkColor
+        // as it is just used as a target color for the animation.
+        val boxColor = Color.Green
+        rule.setContentWithTheme {
+            Checkbox(
+                checked = false,
+                colors = CheckboxDefaults.colors(
+                    uncheckedBoxColor = boxColor,
+                ),
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        val checkboxImage = rule.onNodeWithTag(TEST_TAG).captureToImage()
+        checkboxImage.assertContainsColor(boxColor)
+    }
+
+    @Test
+    fun switch_supports_testtag() {
+        rule.setContentWithTheme {
+            Switch(
+                checked = true,
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertExists()
+    }
+
+    @Test
+    fun switch_is_expected_size() {
+        rule.setContentWithThemeForSizeAssertions {
+            Switch(
+                checked = true,
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }.assertHeightIsEqualTo(24.dp).assertWidthIsEqualTo(32.dp)
+    }
+
+    @Test
+    fun switch_has_role_switch_when_oncheckedchange_defined() {
+        rule.setContentWithTheme {
+            Switch(
+                checked = true,
+                onCheckedChange = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG)
+            .assert(
+                SemanticsMatcher.expectValue(
+                    SemanticsProperties.Role,
+                    Role.Switch
+                )
+            )
+    }
+
+    @Test
+    fun switch_has_no_clickaction_by_default() {
+        rule.setContentWithTheme {
+            Switch(
+                checked = true,
+                enabled = true,
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertHasNoClickAction()
+    }
+
+    @Test
+    fun switch_has_clickaction_when_oncheckedchange_defined() {
+        rule.setContentWithTheme {
+            Switch(
+                checked = true,
+                enabled = true,
+                onCheckedChange = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertHasClickAction()
+    }
+
+    @Test
+    fun switch_is_toggleable_when_oncheckedchange_defined() {
+        rule.setContentWithTheme {
+            Switch(
+                checked = true,
+                enabled = true,
+                onCheckedChange = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNode(isToggleable()).assertExists()
+    }
+
+    @Test
+    fun switch_is_correctly_enabled_when_enabled_equals_true() {
+        rule.setContentWithTheme {
+            Switch(
+                checked = true,
+                enabled = true,
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsEnabled()
+    }
+
+    @Test
+    fun switch_is_correctly_disabled_when_enabled_equals_false() {
+        rule.setContentWithTheme {
+            Switch(
+                checked = true,
+                enabled = false,
+                onCheckedChange = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsNotEnabled()
+    }
+
+    @Test
+    fun switch_is_on_when_checked() {
+        // This test only applies when onCheckedChange is defined.
+        rule.setContentWithTheme {
+            Switch(
+                checked = true,
+                onCheckedChange = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsOn()
+    }
+
+    @Test
+    fun switch_is_off_when_checked() {
+        // This test only applies when onCheckedChange is defined.
+        rule.setContentWithTheme {
+            Switch(
+                checked = false,
+                onCheckedChange = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsOff()
+    }
+
+    @Test
+    fun switch_responds_to_toggle_on() {
+        // This test only applies when onCheckedChange is defined.
+        rule.setContentWithTheme {
+            val (checked, onCheckedChange) = remember { mutableStateOf(false) }
+            Switch(
+                checked = checked,
+                onCheckedChange = onCheckedChange,
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .assertIsOff()
+            .performClick()
+            .assertIsOn()
+    }
+
+    @Test
+    fun switch_responds_to_toggle_off() {
+        // This test only applies when onCheckedChange is defined.
+        rule.setContentWithTheme {
+            val (checked, onCheckedChange) = remember { mutableStateOf(true) }
+            Switch(
+                checked = checked,
+                onCheckedChange = onCheckedChange,
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .assertIsOn()
+            .performClick()
+            .assertIsOff()
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun switch_checked_colors_are_customisable() {
+        val thumbColor = Color.Green
+        val thumbIconColor = Color.Yellow
+        val trackColor = Color.Blue
+        val trackStrokeColor = Color.Red
+        rule.setContentWithTheme {
+            Switch(
+                checked = true,
+                colors = SwitchDefaults.colors(
+                    checkedThumbColor = thumbColor,
+                    checkedThumbIconColor = thumbIconColor,
+                    checkedTrackColor = trackColor,
+                    checkedTrackStrokeColor = trackStrokeColor
+                ),
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        val image = rule.onNodeWithTag(TEST_TAG).captureToImage()
+        image.assertContainsColor(thumbColor)
+        image.assertContainsColor(thumbIconColor)
+        image.assertContainsColor(trackColor)
+        image.assertContainsColor(trackStrokeColor)
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun switch_unchecked_colors_are_customisable() {
+        val thumbColor = Color.Green
+        val thumbIconColor = Color.Yellow
+        val trackColor = Color.Blue
+        val trackStrokeColor = Color.Red
+        rule.setContentWithTheme {
+            Switch(
+                checked = false,
+                colors = SwitchDefaults.colors(
+                    uncheckedThumbColor = thumbColor,
+                    uncheckedThumbIconColor = thumbIconColor,
+                    uncheckedTrackColor = trackColor,
+                    uncheckedTrackStrokeColor = trackStrokeColor
+                ),
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        val image = rule.onNodeWithTag(TEST_TAG).captureToImage()
+        image.assertContainsColor(thumbColor)
+        // NB opacity of icon is decreased to 0 in unchecked state, hence we are asserting that the thumbIconColor should not exist.
+        image.assertDoesNotContainColor(thumbIconColor)
+        image.assertContainsColor(trackColor)
+    }
+
+    @Test
+    fun radiobutton_supports_testtag() {
+        rule.setContentWithTheme {
+            RadioButton(
+                selected = true,
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertExists()
+    }
+
+    @Test
+    fun radiobutton_is_expected_size() {
+        rule.setContentWithThemeForSizeAssertions {
+            Switch(
+                checked = true,
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }.assertHeightIsEqualTo(24.dp).assertWidthIsEqualTo(32.dp)
+    }
+
+    @Test
+    fun radiobutton_has_role_radiobutton_when_onclick_defined() {
+        rule.setContentWithTheme {
+            RadioButton(
+                selected = true,
+                onClick = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG)
+            .assert(
+                SemanticsMatcher.expectValue(
+                    SemanticsProperties.Role,
+                    Role.RadioButton
+                )
+            )
+    }
+
+    @Test
+    fun radiobutton_has_no_clickaction_by_default() {
+        rule.setContentWithTheme {
+            RadioButton(
+                selected = true,
+                enabled = true,
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertHasNoClickAction()
+    }
+
+    @Test
+    fun radiobutton_has_clickaction_when_onclick_defined() {
+        rule.setContentWithTheme {
+            RadioButton(
+                selected = true,
+                enabled = true,
+                onClick = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertHasClickAction()
+    }
+
+    @Test
+    fun radiobutton_is_selectable_when_onclick_defined() {
+        rule.setContentWithTheme {
+            RadioButton(
+                selected = true,
+                enabled = true,
+                onClick = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNode(isSelectable()).assertExists()
+    }
+
+    @Test
+    fun radiobutton_is_correctly_enabled() {
+        rule.setContentWithTheme {
+            RadioButton(
+                selected = true,
+                enabled = true,
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsEnabled()
+    }
+
+    @Test
+    fun radiobutton_is_correctly_disabled() {
+        // This test only applies when onClick is provided and the RadioButton itself is selectable.
+        rule.setContentWithTheme {
+            RadioButton(
+                selected = true,
+                enabled = false,
+                onClick = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsNotEnabled()
+    }
+
+    @Test
+    fun radiobutton_is_on_when_checked() {
+        // This test only applies when onClick is provided and the RadioButton itself is selectable.
+        rule.setContentWithTheme {
+            RadioButton(
+                selected = true,
+                onClick = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsSelected()
+    }
+
+    @Test
+    fun radiobutton_is_off_when_checked() {
+        // This test only applies when onClick is provided and the RadioButton itself is selectable.
+        rule.setContentWithTheme {
+            RadioButton(
+                selected = false,
+                onClick = {},
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assertIsNotSelected()
+    }
+
+    @Test
+    fun radiobutton_responds_to_toggle_on() {
+        // This test only applies when onCheckedChange is defined.
+        rule.setContentWithTheme {
+            var selected by remember { mutableStateOf(false) }
+            RadioButton(
+                selected = selected,
+                onClick = { selected = !selected },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .assertIsNotSelected()
+            .performClick()
+            .assertIsSelected()
+    }
+
+    @Test
+    fun radiobutton_responds_to_toggle_off() {
+        // This test only applies when onCheckedChange is defined.
+        rule.setContentWithTheme {
+            var selected by remember { mutableStateOf(true) }
+            RadioButton(
+                selected = selected,
+                onClick = { selected = !selected },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .assertIsSelected()
+            .performClick()
+            .assertIsNotSelected()
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun radiobutton_selected_colors_are_customisable() {
+        val color = Color.Green
+        rule.setContentWithTheme {
+            RadioButton(
+                selected = true,
+                colors = RadioButtonDefaults.colors(
+                    selectedColor = color
+                ),
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        val image = rule.onNodeWithTag(TEST_TAG).captureToImage()
+        image.assertContainsColor(color)
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun radiobutton_unselected_colors_are_customisable() {
+        // NB Dot is erased during animation, so we don't test uncheckedDotColor
+        // as it is just used as a target color for the animation.
+        val color = Color.Green
+        rule.setContentWithTheme {
+            RadioButton(
+                selected = false,
+                colors = RadioButtonDefaults.colors(
+                    unselectedColor = color,
+                ),
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        val image = rule.onNodeWithTag(TEST_TAG).captureToImage()
+        image.assertContainsColor(color)
+    }
+}
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonScreenshotTest.kt
index 967714f3..11b0b55 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonScreenshotTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonScreenshotTest.kt
@@ -66,7 +66,7 @@
 
     @Test
     fun filled_text_button_disabled() =
-        verifyScreenshot("text_button_disabled") {
+        verifyScreenshot {
             sampleFilledTextButton(enabled = false, isCompact = false)
         }
 
@@ -77,7 +77,7 @@
 
     @Test
     fun filled_tonal_text_button_disabled() =
-        verifyScreenshot("text_button_disabled") {
+        verifyScreenshot {
             sampleFilledTonalTextButton(enabled = false, isCompact = false)
         }
 
@@ -108,7 +108,7 @@
 
     @Test
     fun filled_compact_text_button_disabled() =
-        verifyScreenshot("compact_text_button_disabled") {
+        verifyScreenshot {
             sampleFilledTextButton(enabled = false, isCompact = true)
         }
 
@@ -119,7 +119,7 @@
 
     @Test
     fun filled_tonal_compact_text_button_disabled() =
-        verifyScreenshot("compact_text_button_disabled") {
+        verifyScreenshot {
             sampleFilledTonalTextButton(enabled = false, isCompact = true)
         }
 
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonTest.kt
index a37ef7b..f6a0b7b 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextButtonTest.kt
@@ -31,6 +31,8 @@
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.test.SemanticsMatcher
 import androidx.compose.ui.test.assert
 import androidx.compose.ui.test.assertHasClickAction
@@ -222,6 +224,27 @@
     }
 
     @Test
+    fun allows_custom_role() {
+        val overrideRole = Role.Checkbox
+
+        rule.setContentWithTheme {
+            TextButton(
+                onClick = {},
+                modifier = Modifier.testTag(TEST_TAG).semantics { role = overrideRole }
+            ) {
+                Text("Test")
+            }
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assert(
+            SemanticsMatcher.expectValue(
+                SemanticsProperties.Role,
+                overrideRole
+            )
+        )
+    }
+
+    @Test
     fun sets_correct_font() {
         var actualTextStyle = TextStyle.Default
         var expectedTextStyle = TextStyle.Default
@@ -365,7 +388,7 @@
             status = Status.Disabled,
             colors = { TextButtonDefaults.filledTextButtonColors() },
             expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
-                alpha = DisabledBorderAndContainerAlpha
+                alpha = DisabledContainerAlpha
             ) },
             expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
                 alpha = ContentAlpha.disabled
@@ -391,7 +414,7 @@
             status = Status.Disabled,
             colors = { TextButtonDefaults.filledTonalTextButtonColors() },
             expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
-                alpha = DisabledBorderAndContainerAlpha
+                alpha = DisabledContainerAlpha
             ) },
             expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
                 alpha = ContentAlpha.disabled
@@ -416,9 +439,7 @@
         rule.verifyTextButtonColors(
             status = Status.Disabled,
             colors = { TextButtonDefaults.outlinedTextButtonColors() },
-            expectedContainerColor = { MaterialTheme.colorScheme.onSurface.copy(
-                alpha = DisabledBorderAndContainerAlpha
-            ) },
+            expectedContainerColor = { Color.Transparent },
             expectedContentColor = { MaterialTheme.colorScheme.onSurface.copy(
                 alpha = ContentAlpha.disabled
             ) }
@@ -449,7 +470,7 @@
         val status = Status.Disabled
         rule.verifyButtonBorderColor(
             expectedBorderColor = {
-                MaterialTheme.colorScheme.onSurface.copy(alpha = DisabledBorderAndContainerAlpha)
+                MaterialTheme.colorScheme.onSurface.copy(alpha = DisabledBorderAlpha)
             },
             content = { modifier: Modifier ->
                 TextButton(
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextToggleButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextToggleButtonTest.kt
new file mode 100644
index 0000000..d4dd6db
--- /dev/null
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextToggleButtonTest.kt
@@ -0,0 +1,776 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3
+
+import android.os.Build
+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.padding
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.testutils.assertContainsColor
+import androidx.compose.testutils.assertShape
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.compositeOver
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.test.SemanticsMatcher
+import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.assertHasClickAction
+import androidx.compose.ui.test.assertHeightIsEqualTo
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.assertIsOff
+import androidx.compose.ui.test.assertIsOn
+import androidx.compose.ui.test.assertIsToggleable
+import androidx.compose.ui.test.assertWidthIsEqualTo
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+
+class TextToggleButtonTest {
+
+    @get:Rule
+    val rule = createComposeRule()
+
+    @Test
+    fun supports_testTag() {
+        rule.setContentWithTheme {
+            TextToggleButton(
+                checked = true,
+                onCheckedChange = {},
+                content = { Text("ABC") },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(TEST_TAG).assertExists()
+    }
+
+    @Test
+    fun has_clickAction_when_enabled() {
+        rule.setContentWithTheme {
+            TextToggleButton(
+                enabled = true,
+                checked = true,
+                onCheckedChange = {},
+                content = { Text("ABC") },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(TEST_TAG).assertHasClickAction()
+    }
+
+    @Test
+    fun has_clickAction_when_disabled() {
+        rule.setContentWithTheme {
+            TextToggleButton(
+                enabled = false,
+                checked = true,
+                onCheckedChange = {},
+                content = { Text("ABC") },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(TEST_TAG).assertHasClickAction()
+    }
+
+    @Test
+    fun is_toggleable() {
+        rule.setContentWithTheme {
+            TextToggleButton(
+                enabled = true,
+                checked = true,
+                onCheckedChange = {},
+                content = { Text("ABC") },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(TEST_TAG).assertIsToggleable()
+    }
+
+    @Test
+    fun is_correctly_enabled() {
+        rule.setContentWithTheme {
+            TextToggleButton(
+                enabled = true,
+                checked = true,
+                onCheckedChange = {},
+                content = { Text("ABC") },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(TEST_TAG).assertIsEnabled()
+    }
+
+    @Test
+    fun is_correctly_disabled() {
+        rule.setContentWithTheme {
+            TextToggleButton(
+                enabled = false,
+                checked = true,
+                onCheckedChange = {},
+                content = { Text("ABC") },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(TEST_TAG).assertIsNotEnabled()
+    }
+
+    @Test
+    fun is_on_when_checked() {
+        rule.setContentWithTheme {
+            TextToggleButton(
+                checked = true,
+                onCheckedChange = {},
+                content = { Text("ABC") },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(TEST_TAG).assertIsOn()
+    }
+
+    @Test
+    fun is_off_when_unchecked() {
+        rule.setContentWithTheme {
+            TextToggleButton(
+                checked = false,
+                onCheckedChange = {},
+                content = { Text("ABC") },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(TEST_TAG).assertIsOff()
+    }
+
+    @Test
+    fun responds_to_toggle_on() {
+        rule.setContentWithTheme {
+            val (checked, onCheckedChange) = remember { mutableStateOf(false) }
+            TextToggleButton(
+                checked = checked,
+                onCheckedChange = onCheckedChange,
+                content = { Text("ABC") },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(TEST_TAG)
+            .assertIsOff()
+            .performClick()
+            .assertIsOn()
+    }
+
+    @Test
+    fun responds_to_toggle_off() {
+        rule.setContentWithTheme {
+            val (checked, onCheckedChange) = remember { mutableStateOf(true) }
+            TextToggleButton(
+                checked = checked,
+                onCheckedChange = onCheckedChange,
+                content = { Text("ABC") },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(TEST_TAG)
+            .assertIsOn()
+            .performClick()
+            .assertIsOff()
+    }
+
+    @Test
+    fun does_not_toggle_when_disabled() {
+        rule.setContentWithTheme {
+            val (checked, onCheckedChange) = remember { mutableStateOf(false) }
+            TextToggleButton(
+                enabled = false,
+                checked = checked,
+                onCheckedChange = onCheckedChange,
+                content = { Text("ABC") },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+        rule.waitForIdle()
+
+        rule.onNodeWithTag(TEST_TAG)
+            .assertIsOff()
+            .performClick()
+            .assertIsOff()
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun is_circular_under_ltr() =
+        rule.isShape(
+            shape = CircleShape,
+            layoutDirection = LayoutDirection.Ltr,
+            shapeColorComposable = { shapeColor() }
+        ) {
+            TextToggleButton(
+                enabled = true,
+                checked = true,
+                onCheckedChange = { },
+                content = { },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun is_circular_under_rtl() =
+        rule.isShape(
+            shape = CircleShape,
+            layoutDirection = LayoutDirection.Rtl,
+            shapeColorComposable = { shapeColor() }
+        ) {
+            TextToggleButton(
+                enabled = true,
+                checked = true,
+                onCheckedChange = { },
+                content = { },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun allows_shape_overrides() =
+        rule.isShape(
+            shape = RectangleShape,
+            layoutDirection = LayoutDirection.Ltr,
+            shapeColorComposable = { shapeColor() }
+        ) {
+            TextToggleButton(
+                enabled = true,
+                checked = true,
+                shape = RectangleShape,
+                onCheckedChange = { },
+                content = { },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+    @Test
+    fun gives_default_correct_tapSize() =
+        rule.verifyTapSize(52.dp) {
+            TextToggleButton(
+                enabled = true,
+                checked = true,
+                onCheckedChange = { },
+                content = { },
+                modifier = Modifier
+                    .testTag(TEST_TAG)
+                    .touchTargetAwareSize(TextButtonDefaults.DefaultButtonSize)
+            )
+        }
+
+    @Test
+    fun gives_small_correct_tapSize() =
+        rule.verifyTapSize(48.dp) {
+            TextToggleButton(
+                enabled = true,
+                checked = true,
+                onCheckedChange = { },
+                content = { },
+                modifier = Modifier
+                    .testTag(TEST_TAG)
+                    .touchTargetAwareSize(TextButtonDefaults.SmallButtonSize)
+            )
+        }
+
+    @Test
+    fun gives_extraSmall_correct_tapSize() =
+        rule.verifyTapSize(48.dp) {
+            TextToggleButton(
+                enabled = true,
+                checked = true,
+                onCheckedChange = { },
+                content = { },
+                modifier = Modifier
+                    .testTag(TEST_TAG)
+                    .touchTargetAwareSize(TextButtonDefaults.ExtraSmallButtonSize)
+            )
+        }
+
+    @Test
+    fun gives_large_correct_tapSize() =
+        rule.verifyTapSize(60.dp) {
+            TextToggleButton(
+                enabled = true,
+                checked = true,
+                onCheckedChange = { },
+                content = { },
+                modifier = Modifier
+                    .testTag(TEST_TAG)
+                    .touchTargetAwareSize(TextButtonDefaults.LargeButtonSize)
+            )
+        }
+
+    @Test
+    fun gives_default_correct_size() =
+        rule.verifyActualSize(52.dp) {
+            TextToggleButton(
+                enabled = true,
+                checked = true,
+                onCheckedChange = { },
+                content = { },
+                modifier = Modifier
+                    .testTag(TEST_TAG)
+                    .touchTargetAwareSize(TextButtonDefaults.DefaultButtonSize)
+            )
+        }
+
+    @Test
+    fun gives_small_correct_size() =
+        rule.verifyActualSize(48.dp) {
+            TextToggleButton(
+                enabled = true,
+                checked = true,
+                onCheckedChange = { },
+                content = { },
+                modifier = Modifier
+                    .testTag(TEST_TAG)
+                    .touchTargetAwareSize(TextButtonDefaults.SmallButtonSize)
+            )
+        }
+
+    @Test
+    fun gives_extraSmall_correct_size() =
+        rule.verifyActualSize(48.dp) {
+            TextToggleButton(
+                enabled = true,
+                checked = true,
+                onCheckedChange = { },
+                content = { },
+                modifier = Modifier
+                    .testTag(TEST_TAG)
+                    .touchTargetAwareSize(TextButtonDefaults.ExtraSmallButtonSize)
+            )
+        }
+
+    @Test
+    fun gives_large_correct_size() =
+        rule.verifyActualSize(60.dp) {
+            TextToggleButton(
+                enabled = true,
+                checked = true,
+                onCheckedChange = { },
+                content = { },
+                modifier = Modifier
+                    .testTag(TEST_TAG)
+                    .touchTargetAwareSize(TextButtonDefaults.LargeButtonSize)
+            )
+        }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun gives_checked_primary_colors() =
+        rule.verifyTextToggleButtonColors(
+            status = Status.Enabled,
+            checked = true,
+            colors = { TextButtonDefaults.textToggleButtonColors() },
+            containerColor = { MaterialTheme.colorScheme.primary },
+            contentColor = { MaterialTheme.colorScheme.onPrimary }
+        )
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun gives_unchecked_surface_colors() =
+        rule.verifyTextToggleButtonColors(
+            status = Status.Enabled,
+            checked = false,
+            colors = { TextButtonDefaults.textToggleButtonColors() },
+            containerColor = { MaterialTheme.colorScheme.surface },
+            contentColor = { MaterialTheme.colorScheme.onSurfaceVariant }
+        )
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun gives_disabled_unchecked_surface_colors_with_alpha() =
+        rule.verifyTextToggleButtonColors(
+            status = Status.Disabled,
+            checked = false,
+            colors = { TextButtonDefaults.textToggleButtonColors() },
+            containerColor = { MaterialTheme.colorScheme.surface },
+            contentColor = { MaterialTheme.colorScheme.onSurfaceVariant }
+        )
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun gives_disabled_primary_checked_contrasting_content_color() =
+        rule.verifyTextToggleButtonColors(
+            status = Status.Disabled,
+            checked = true,
+            colors = { TextButtonDefaults.textToggleButtonColors() },
+            containerColor = { MaterialTheme.colorScheme.primary },
+            contentColor = { MaterialTheme.colorScheme.onPrimary },
+        )
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun allows_custom_checked_background_override() {
+        val override = Color.Yellow
+
+        rule.verifyTextToggleButtonColors(
+            status = Status.Enabled,
+            checked = true,
+            colors = {
+                TextButtonDefaults.textToggleButtonColors(
+                    checkedContainerColor = override
+                )
+            },
+            containerColor = { override },
+            contentColor = { MaterialTheme.colorScheme.onPrimary }
+        )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun allows_custom_checked_content_override() {
+        val override = Color.Green
+
+        rule.verifyTextToggleButtonColors(
+            status = Status.Enabled,
+            checked = true,
+            colors = {
+                TextButtonDefaults.textToggleButtonColors(
+                    checkedContentColor = override
+                )
+            },
+            containerColor = { MaterialTheme.colorScheme.primary },
+            contentColor = { override }
+        )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun allows_custom_unchecked_background_override() {
+        val override = Color.Red
+
+        rule.verifyTextToggleButtonColors(
+            status = Status.Enabled,
+            checked = false,
+            colors = {
+                TextButtonDefaults.textToggleButtonColors(
+                    uncheckedContainerColor = override
+                )
+            },
+            containerColor = { override },
+            contentColor = { MaterialTheme.colorScheme.onSurfaceVariant }
+        )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun allows_custom_unchecked_content_override() {
+        val override = Color.Green
+
+        rule.verifyTextToggleButtonColors(
+            status = Status.Enabled,
+            checked = false,
+            colors = {
+                TextButtonDefaults.textToggleButtonColors(
+                    uncheckedContentColor = override
+                )
+            },
+            containerColor = { MaterialTheme.colorScheme.surface },
+            contentColor = { override }
+        )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun allows_custom_checked_disabled_background_override() {
+        val override = Color.Yellow
+
+        rule.verifyTextToggleButtonColors(
+            status = Status.Disabled,
+            checked = true,
+            colors = {
+                TextButtonDefaults.textToggleButtonColors(
+                    // Apply the content color override for the content alpha to be applied
+                    checkedContainerColor = override
+                )
+            },
+            containerColor = { override },
+            contentColor = { MaterialTheme.colorScheme.onPrimary }
+        )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun allows_custom_checked_disabled_content_override() {
+        val override = Color.Green
+
+        rule.verifyTextToggleButtonColors(
+            status = Status.Disabled,
+            checked = true,
+            colors = {
+                TextButtonDefaults.textToggleButtonColors(
+                    // Apply the content color override for the content alpha to be applied
+                    checkedContentColor = override
+                )
+            },
+            containerColor = { MaterialTheme.colorScheme.primary },
+            contentColor = { override }
+        )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun allows_custom_unchecked_disabled_background_override() {
+        val override = Color.Red
+
+        rule.verifyTextToggleButtonColors(
+            status = Status.Disabled,
+            checked = false,
+            colors = {
+                TextButtonDefaults.textToggleButtonColors(
+                    // Apply the content color override for the content alpha to be applied
+                    uncheckedContainerColor = override
+                )
+            },
+            containerColor = { override },
+            contentColor = { MaterialTheme.colorScheme.onSurfaceVariant }
+        )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun allows_custom_unchecked_disabled_content_override() {
+        val override = Color.Green
+
+        rule.verifyTextToggleButtonColors(
+            status = Status.Disabled,
+            checked = false,
+            colors = {
+                TextButtonDefaults.textToggleButtonColors(
+                    // Apply the content color override for the content alpha to be applied
+                    uncheckedContentColor = override
+                )
+            },
+            contentColor = { override },
+            containerColor = { MaterialTheme.colorScheme.surface }
+        )
+    }
+
+    @Test
+    fun default_role_checkbox() {
+
+        rule.setContentWithTheme {
+            TextToggleButton(
+                checked = false,
+                onCheckedChange = {},
+                enabled = false,
+                content = { Text("ABC") },
+                modifier = Modifier.testTag(TEST_TAG)
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assert(
+            SemanticsMatcher.expectValue(
+                SemanticsProperties.Role,
+                Role.Checkbox
+            )
+        )
+    }
+
+    @Test
+    fun allows_custom_role() {
+        val overrideRole = Role.Button
+
+        rule.setContentWithTheme {
+            TextToggleButton(
+                checked = false,
+                onCheckedChange = {},
+                enabled = true,
+                content = { Text("ABC") },
+                modifier = Modifier.testTag(TEST_TAG).semantics { role = overrideRole }
+            )
+        }
+
+        rule.onNodeWithTag(TEST_TAG).assert(
+            SemanticsMatcher.expectValue(
+                SemanticsProperties.Role,
+                overrideRole
+            )
+        )
+    }
+
+    @Composable
+    private fun shapeColor(): Color {
+        return TextButtonDefaults.textToggleButtonColors().containerColor(
+            enabled = true,
+            checked = true
+        ).value
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    private fun ComposeContentTestRule.verifyTextToggleButtonColors(
+        status: Status,
+        checked: Boolean,
+        colors: @Composable () -> ToggleButtonColors,
+        containerColor: @Composable () -> Color,
+        contentColor: @Composable () -> Color,
+    ) {
+        verifyColors(
+            status = status,
+            expectedContainerColor = containerColor,
+            expectedContentColor = contentColor,
+            content = {
+                var actualContentColor = Color.Transparent
+                TextToggleButton(
+                    onCheckedChange = { },
+                    enabled = status.enabled(),
+                    checked = checked,
+                    colors = colors(),
+                    modifier = Modifier.testTag(TEST_TAG),
+                ) {
+                    actualContentColor = LocalContentColor.current
+                }
+                return@verifyColors actualContentColor
+            }
+        )
+    }
+    @RequiresApi(Build.VERSION_CODES.O)
+    private fun ComposeContentTestRule.isShape(
+        shape: Shape = CircleShape,
+        layoutDirection: LayoutDirection,
+        padding: Dp = 0.dp,
+        backgroundColor: Color = Color.Red,
+        shapeColorComposable: @Composable () -> Color,
+        content: @Composable () -> Unit
+    ) {
+        var shapeColor = Color.Transparent
+        setContentWithTheme {
+            shapeColor = shapeColorComposable.invoke()
+            CompositionLocalProvider(LocalLayoutDirection provides layoutDirection) {
+                Box(
+                    Modifier
+                        .padding(padding)
+                        .background(backgroundColor)
+                ) {
+                    content()
+                }
+            }
+        }
+
+        this.waitForIdle()
+        onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertShape(
+                density = density,
+                shape = shape,
+                horizontalPadding = padding,
+                verticalPadding = padding,
+                backgroundColor = backgroundColor,
+                shapeOverlapPixelCount = 2.0f,
+                shapeColor = shapeColor
+            )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    private fun ComposeContentTestRule.verifyColors(
+        status: Status,
+        expectedContainerColor: @Composable () -> Color,
+        expectedContentColor: @Composable () -> Color,
+        applyAlphaForDisabled: Boolean = true,
+        content: @Composable () -> Color
+    ) {
+        val testBackgroundColor = Color.White
+        var finalExpectedContainerColor = Color.Transparent
+        var finalExpectedContent = Color.Transparent
+        var actualContentColor = Color.Transparent
+        setContentWithTheme {
+            finalExpectedContainerColor =
+                if (status.enabled() || !applyAlphaForDisabled) {
+                    expectedContainerColor()
+                } else {
+                    expectedContainerColor().copy(ContentAlpha.disabled)
+                }.compositeOver(testBackgroundColor)
+            finalExpectedContent =
+                if (status.enabled() || !applyAlphaForDisabled) {
+                    expectedContentColor()
+                } else {
+                    expectedContentColor().copy(ContentAlpha.disabled)
+                }
+            Box(
+                Modifier
+                    .fillMaxSize()
+                    .background(testBackgroundColor)
+            ) {
+                actualContentColor = content()
+            }
+        }
+        Assert.assertEquals(finalExpectedContent, actualContentColor)
+        onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertContainsColor(finalExpectedContainerColor)
+    }
+
+    private fun ComposeContentTestRule.verifyActualSize(
+        expectedSize: Dp,
+        content: @Composable (modifier: Modifier) -> Unit
+    ) {
+        setContentWithTheme {
+            content(Modifier.testTag(TEST_TAG))
+        }
+        waitForIdle()
+
+        onNodeWithTag(TEST_TAG)
+            .assertHeightIsEqualTo(expectedSize)
+            .assertWidthIsEqualTo(expectedSize)
+    }
+}
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Button.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Button.kt
index 6c8de9eb59..e608707 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Button.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Button.kt
@@ -715,7 +715,7 @@
         containerColor: Color = MaterialTheme.colorScheme.surface,
         contentColor: Color = MaterialTheme.colorScheme.onSurface,
         secondaryContentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
-        iconColor: Color = MaterialTheme.colorScheme.onSurface
+        iconColor: Color = MaterialTheme.colorScheme.primary,
     ): ButtonColors {
         return buttonColors(
             containerColor = containerColor,
@@ -742,13 +742,14 @@
     fun outlinedButtonColors(
         contentColor: Color = MaterialTheme.colorScheme.onSurface,
         secondaryContentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
-        iconColor: Color = MaterialTheme.colorScheme.onSurface
+        iconColor: Color = MaterialTheme.colorScheme.primary,
     ): ButtonColors {
         return buttonColors(
             containerColor = Color.Transparent,
             contentColor = contentColor,
             secondaryContentColor = secondaryContentColor,
-            iconColor = iconColor
+            iconColor = iconColor,
+            disabledContainerColor = Color.Transparent,
         )
     }
 
@@ -769,13 +770,14 @@
     fun childButtonColors(
         contentColor: Color = MaterialTheme.colorScheme.onSurface,
         secondaryContentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
-        iconColor: Color = MaterialTheme.colorScheme.onSurface
+        iconColor: Color = MaterialTheme.colorScheme.primary
     ): ButtonColors {
         return buttonColors(
             containerColor = Color.Transparent,
             contentColor = contentColor,
             secondaryContentColor = secondaryContentColor,
-            iconColor = iconColor
+            iconColor = iconColor,
+            disabledContainerColor = Color.Transparent,
         )
     }
 
@@ -851,7 +853,7 @@
         enabled: Boolean,
         borderColor: Color = MaterialTheme.colorScheme.outline,
         disabledBorderColor: Color = MaterialTheme.colorScheme.onSurface.toDisabledColor(
-            disabledAlpha = DisabledBorderAndContainerAlpha
+            disabledAlpha = DisabledBorderAlpha
         ),
         borderWidth: Dp = 1.dp
     ): BorderStroke {
@@ -901,7 +903,7 @@
         secondaryContentColor: Color = contentColor,
         iconColor: Color = contentColor,
         disabledContainerColor: Color = MaterialTheme.colorScheme.onSurface.toDisabledColor(
-            disabledAlpha = DisabledBorderAndContainerAlpha
+            disabledAlpha = DisabledContainerAlpha
         ),
         disabledContentColor: Color = MaterialTheme.colorScheme.onSurface.toDisabledColor(),
         disabledSecondaryContentColor: Color =
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ColorScheme.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ColorScheme.kt
index 1ee0789..b0c3bbb 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ColorScheme.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ColorScheme.kt
@@ -24,6 +24,8 @@
 import androidx.compose.runtime.staticCompositionLocalOf
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.takeOrElse
+import androidx.wear.compose.material3.tokens.ColorSchemeKeyTokens
+import androidx.wear.compose.material3.tokens.ColorTokens
 
 /**
  * A [ColorScheme] holds all the named color parameters for a [MaterialTheme].
@@ -77,32 +79,32 @@
  * @property onError Color used for text and icons displayed on top of the error color.
  */@Stable
 public class ColorScheme(
-    primary: Color = Color(0xFFD3E3FD),
-    primaryDim: Color = Color(0xFFA8C7FA),
-    primaryContainer: Color = Color(0xFF04409F),
-    onPrimary: Color = Color(0xFF001944),
-    onPrimaryContainer: Color = Color(0xFFD3E3FD),
-    secondary: Color = Color(0xFFC2E7FF),
-    secondaryDim: Color = Color(0xFF7FCFFF),
-    secondaryContainer: Color = Color(0xFF004A77),
-    onSecondary: Color = Color(0xFF001D35),
-    onSecondaryContainer: Color = Color(0xFFC2E7FF),
-    tertiary: Color = Color(0xFFC3EDCF),
-    tertiaryDim: Color = Color(0xFF73DC92),
-    tertiaryContainer: Color = Color(0xFF0F5223),
-    onTertiary: Color = Color(0xFF02210C),
-    onTertiaryContainer: Color = Color(0xFFC3EDCF),
-    surfaceDim: Color = Color(0xFF252626),
-    surface: Color = Color(0xFF303030),
-    surfaceBright: Color = Color(0xFF474747),
-    onSurface: Color = Color(0xFFF2F2F2),
-    onSurfaceVariant: Color = Color(0xFFC4C7C5),
-    outline: Color = Color(0xFF8E918F),
-    outlineVariant: Color = Color(0xFF5C5F5E),
-    background: Color = Color.Black,
-    onBackground: Color = Color(0xFFFFFFFF),
-    error: Color = Color(0xFFFD7267),
-    onError: Color = Color(0xFF410002),
+    primary: Color = ColorTokens.Primary,
+    primaryDim: Color = ColorTokens.PrimaryDim,
+    primaryContainer: Color = ColorTokens.PrimaryContainer,
+    onPrimary: Color = ColorTokens.OnPrimary,
+    onPrimaryContainer: Color = ColorTokens.OnPrimaryContainer,
+    secondary: Color = ColorTokens.Secondary,
+    secondaryDim: Color = ColorTokens.SecondaryDim,
+    secondaryContainer: Color = ColorTokens.SecondaryContainer,
+    onSecondary: Color = ColorTokens.OnSecondary,
+    onSecondaryContainer: Color = ColorTokens.OnSecondaryContainer,
+    tertiary: Color = ColorTokens.Tertiary,
+    tertiaryDim: Color = ColorTokens.TertiaryDim,
+    tertiaryContainer: Color = ColorTokens.TertiaryContainer,
+    onTertiary: Color = ColorTokens.OnTertiary,
+    onTertiaryContainer: Color = ColorTokens.OnTertiaryContainer,
+    surfaceDim: Color = ColorTokens.SurfaceDim,
+    surface: Color = ColorTokens.Surface,
+    surfaceBright: Color = ColorTokens.SurfaceBright,
+    onSurface: Color = ColorTokens.OnSurface,
+    onSurfaceVariant: Color = ColorTokens.OnSurfaceVariant,
+    outline: Color = ColorTokens.Outline,
+    outlineVariant: Color = ColorTokens.OutlineVariant,
+    background: Color = ColorTokens.Background,
+    onBackground: Color = ColorTokens.OnBackground,
+    error: Color = ColorTokens.Error,
+    onError: Color = ColorTokens.OnError,
 ) {
     /**
      * [primary] is the main color used across screens and components
@@ -414,11 +416,11 @@
 /**
  * Updates the internal values of the given [ColorScheme] with values from the [other] [ColorScheme]. This
  * allows efficiently updating a subset of [ColorScheme], without recomposing every composable that
- * consumes values from [LocalColors].
+ * consumes values from [LocalColorScheme].
  *
  * Because [ColorScheme] is very wide-reaching, and used by many expensive composables in the
- * hierarchy, providing a new value to [LocalColors] causes every composable consuming
- * [LocalColors] to recompose, which is prohibitively expensive in cases such as animating one
+ * hierarchy, providing a new value to [LocalColorScheme] causes every composable consuming
+ * [LocalColorScheme] to recompose, which is prohibitively expensive in cases such as animating one
  * color in the theme. Instead, [ColorScheme] is internally backed by [mutableStateOf], and this
  * function mutates the internal state of [this] to match values in [other]. This means that any
  * changes will mutate the internal state of [this], and only cause composables that are reading
@@ -453,7 +455,43 @@
     onError = other.onError
 }
 
-internal val LocalColors = staticCompositionLocalOf<ColorScheme> { ColorScheme() }
+/**
+ * Helper function for component color tokens. Here is an example on how to use component color
+ * tokens:
+ * ``MaterialTheme.colorScheme.fromToken(FilledButtonTokens.ContainerColor)``
+ */
+internal fun ColorScheme.fromToken(value: ColorSchemeKeyTokens): Color {
+    return when (value) {
+        ColorSchemeKeyTokens.Primary -> primary
+        ColorSchemeKeyTokens.PrimaryDim -> primaryDim
+        ColorSchemeKeyTokens.PrimaryContainer -> primaryContainer
+        ColorSchemeKeyTokens.OnPrimary -> onPrimary
+        ColorSchemeKeyTokens.OnPrimaryContainer -> onPrimaryContainer
+        ColorSchemeKeyTokens.Secondary -> secondary
+        ColorSchemeKeyTokens.SecondaryDim -> secondaryDim
+        ColorSchemeKeyTokens.SecondaryContainer -> secondaryContainer
+        ColorSchemeKeyTokens.OnSecondary -> onSecondary
+        ColorSchemeKeyTokens.OnSecondaryContainer -> onSecondaryContainer
+        ColorSchemeKeyTokens.Tertiary -> tertiary
+        ColorSchemeKeyTokens.TertiaryDim -> tertiaryDim
+        ColorSchemeKeyTokens.TertiaryContainer -> tertiaryContainer
+        ColorSchemeKeyTokens.OnTertiary -> onTertiary
+        ColorSchemeKeyTokens.OnTertiaryContainer -> onTertiaryContainer
+        ColorSchemeKeyTokens.SurfaceDim -> surfaceDim
+        ColorSchemeKeyTokens.Surface -> surface
+        ColorSchemeKeyTokens.SurfaceBright -> surfaceBright
+        ColorSchemeKeyTokens.OnSurface -> onSurface
+        ColorSchemeKeyTokens.OnSurfaceVariant -> onSurfaceVariant
+        ColorSchemeKeyTokens.Outline -> outline
+        ColorSchemeKeyTokens.OutlineVariant -> outlineVariant
+        ColorSchemeKeyTokens.Background -> background
+        ColorSchemeKeyTokens.OnBackground -> onBackground
+        ColorSchemeKeyTokens.Error -> error
+        ColorSchemeKeyTokens.OnError -> onError
+    }
+}
+
+internal val LocalColorScheme = staticCompositionLocalOf<ColorScheme> { ColorScheme() }
 
 /**
  * Convert given color to disabled color.
@@ -462,3 +500,10 @@
 @Composable
 internal fun Color.toDisabledColor(disabledAlpha: Float = ContentAlpha.disabled) =
     this.copy(alpha = this.alpha * disabledAlpha)
+
+/** Converts a color token key to the local color scheme provided by the theme */
+@ReadOnlyComposable
+@Composable
+internal fun ColorSchemeKeyTokens.toColor(): Color {
+    return MaterialTheme.colorScheme.fromToken(this)
+}
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ContentAlpha.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ContentAlpha.kt
index 8471524..b978a2b 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ContentAlpha.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ContentAlpha.kt
@@ -33,18 +33,18 @@
  * body text.
  *
  */
-public val LocalContentAlpha: ProvidableCompositionLocal<Float> = compositionLocalOf { 1f }
+val LocalContentAlpha: ProvidableCompositionLocal<Float> = compositionLocalOf { 1f }
 
 /**
  * Default alpha levels used by Material components.
  *
  * See [LocalContentAlpha].
  */
-public object ContentAlpha {
+object ContentAlpha {
     /**
      * A high level of content alpha, used to represent high emphasis text.
      */
-    public val high: Float
+    val high: Float
         @Composable
         get() = contentAlpha(
             highContrastAlpha = HighContrastContentAlpha.high,
@@ -55,7 +55,7 @@
      * A medium level of content alpha, used to represent medium emphasis text such as
      * placeholder text.
      */
-    public val medium: Float
+    val medium: Float
         @Composable
         get() = contentAlpha(
             highContrastAlpha = HighContrastContentAlpha.medium,
@@ -66,7 +66,7 @@
      * A low level of content alpha used to represent disabled components, such as text in a
      * disabled Button.
      */
-    public val disabled: Float
+    val disabled: Float
         @Composable
         get() = contentAlpha(
             highContrastAlpha = HighContrastContentAlpha.disabled,
@@ -123,4 +123,5 @@
     const val disabled: Float = 0.38f
 }
 
-internal const val DisabledBorderAndContainerAlpha = 0.12f
+internal const val DisabledContainerAlpha = 0.12f
+internal const val DisabledBorderAlpha = 0.20f
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconButton.kt
index 0195c9d..ff669b8 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconButton.kt
@@ -252,6 +252,76 @@
 ) = IconButton(onClick, modifier, enabled, shape, colors, border, interactionSource, content)
 
 /**
+ * Wear Material [IconToggleButton] is a filled icon toggle button which switches between primary
+ * colors and tonal colors depending on [checked] value, and offers a single slot for
+ * icon or image.
+ *
+ * Set the size of the [IconToggleButton] with Modifier.[touchTargetAwareSize]
+ * to ensure that the background padding will correctly reach the edge of the minimum touch target.
+ * The recommended text button sizes are [IconButtonDefaults.DefaultButtonSize],
+ * [IconButtonDefaults.LargeButtonSize], [IconButtonDefaults.SmallButtonSize] and
+ * [IconButtonDefaults.ExtraSmallButtonSize].
+ *
+ * Use [IconButtonDefaults.iconSizeFor] to determine the icon size for a
+ * given [IconToggleButton] size, or refer to icon sizes
+ * [IconButtonDefaults.SmallIconSize], [IconButtonDefaults.DefaultIconSize],
+ * [IconButtonDefaults.LargeIconSize] directly.
+ *
+ * [IconToggleButton] can be enabled or disabled. A disabled button will not respond to
+ * click events. When enabled, the checked and unchecked events are propagated by [onCheckedChange].
+ *
+ * A simple icon toggle button using the default colors
+ * @sample androidx.wear.compose.material3.samples.IconToggleButtonSample
+ *
+ * @param checked Boolean flag indicating whether this toggle button is currently checked.
+ * @param onCheckedChange Callback to be invoked when this toggle button is clicked.
+ * @param modifier Modifier to be applied to the toggle button.
+ * @param enabled Controls the enabled state of the toggle button. When `false`,
+ * this toggle button will not be clickable.
+ * @param colors [ToggleButtonColors] that will be used to resolve the container and
+ * content color for this toggle button.
+ * @param interactionSource The [MutableInteractionSource] representing the stream of
+ * [Interaction]s for this toggle button. You can create and pass in your own remembered
+ * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
+ * appearance / behavior of this ToggleButton in different [Interaction]s.
+ * @param shape Defines the shape for this toggle button. It is strongly recommended to use the
+ * default as this shape is a key characteristic of the Wear Material 3 Theme.
+ * @param border Optional [BorderStroke] for the [IconToggleButton].
+ * @param content The content to be drawn inside the toggle button.
+ */
+@Composable
+fun IconToggleButton(
+    checked: Boolean,
+    onCheckedChange: (Boolean) -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    colors: ToggleButtonColors = IconButtonDefaults.iconToggleButtonColors(),
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    shape: Shape = CircleShape,
+    border: BorderStroke? = null,
+    content: @Composable BoxScope.() -> Unit,
+) {
+    androidx.wear.compose.materialcore.ToggleButton(
+        checked = checked,
+        onCheckedChange = onCheckedChange,
+        modifier = modifier.minimumInteractiveComponentSize(),
+        enabled = enabled,
+        backgroundColor = { isEnabled, isChecked ->
+            colors.containerColor(enabled = isEnabled, checked = isChecked)
+        },
+        border = { _, _ -> rememberUpdatedState(newValue = border) },
+        toggleButtonSize = IconButtonDefaults.DefaultButtonSize,
+        interactionSource = interactionSource,
+        shape = shape,
+        content = provideScopeContent(
+            colors.contentColor(enabled = enabled, checked = checked),
+            MaterialTheme.typography.buttonMedium,
+            content
+        )
+    )
+}
+
+/**
  * Contains the default values used by [IconButton].
  */
 object IconButtonDefaults {
@@ -288,7 +358,10 @@
     ): IconButtonColors {
         return iconButtonColors(
             containerColor = containerColor,
-            contentColor = contentColor
+            contentColor = contentColor,
+            disabledContainerColor = MaterialTheme.colorScheme.onSurface.toDisabledColor(
+                disabledAlpha = DisabledContainerAlpha
+            ),
         )
     }
 
@@ -308,7 +381,10 @@
     ): IconButtonColors {
         return iconButtonColors(
             containerColor = containerColor,
-            contentColor = contentColor
+            contentColor = contentColor,
+            disabledContainerColor = MaterialTheme.colorScheme.onSurface.toDisabledColor(
+                disabledAlpha = DisabledContainerAlpha
+            ),
         )
     }
 
@@ -326,7 +402,7 @@
     ): IconButtonColors {
         return iconButtonColors(
             containerColor = Color.Transparent,
-            contentColor = contentColor
+            contentColor = contentColor,
         )
     }
 
@@ -345,9 +421,7 @@
     fun iconButtonColors(
         containerColor: Color = Color.Transparent,
         contentColor: Color = MaterialTheme.colorScheme.onBackground,
-        disabledContainerColor: Color = MaterialTheme.colorScheme.onSurface.toDisabledColor(
-            disabledAlpha = DisabledBorderAndContainerAlpha
-        ),
+        disabledContainerColor: Color = Color.Transparent,
         disabledContentColor: Color = MaterialTheme.colorScheme.onSurface.toDisabledColor()
     ): IconButtonColors = IconButtonColors(
         containerColor = containerColor,
@@ -357,6 +431,52 @@
     )
 
     /**
+     * Creates a [ToggleButtonColors] for a [IconToggleButton]
+     * - by default, a colored background with a contrasting content color.
+     * If the button is disabled, then the colors will have an alpha
+     * ([ContentAlpha.disabled]) value applied.
+     *
+     * @param checkedContainerColor The container color of this [IconToggleButton] when enabled
+     * and checked
+     * @param checkedContentColor The content color of this [IconToggleButton] when enabled and
+     * checked
+     * @param uncheckedContainerColor The container color of this [IconToggleButton] when enabled
+     * and unchecked
+     * @param uncheckedContentColor The content color of this [IconToggleButton] when enabled and
+     * unchecked
+     * @param disabledCheckedContainerColor The container color of this [IconToggleButton] when
+     * checked and not enabled
+     * @param disabledCheckedContentColor The content color of this [IconToggleButton] when checked
+     * and not enabled
+     * @param disabledUncheckedContainerColor The container color of this [IconToggleButton] when
+     * unchecked and not enabled
+     * @param disabledUncheckedContentColor The content color of this [IconToggleButton] when
+     * unchecked and not enabled
+     */
+    @Composable
+    fun iconToggleButtonColors(
+        checkedContainerColor: Color = MaterialTheme.colorScheme.primary,
+        checkedContentColor: Color = MaterialTheme.colorScheme.onPrimary,
+        uncheckedContainerColor: Color = MaterialTheme.colorScheme.surface,
+        uncheckedContentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
+        disabledCheckedContainerColor: Color = checkedContainerColor.toDisabledColor(),
+        disabledCheckedContentColor: Color = checkedContentColor.toDisabledColor(),
+        disabledUncheckedContainerColor: Color = uncheckedContainerColor.toDisabledColor(),
+        disabledUncheckedContentColor: Color = uncheckedContentColor.toDisabledColor(),
+    ): ToggleButtonColors {
+        return ToggleButtonColors(
+            checkedContainerColor = checkedContainerColor,
+            checkedContentColor = checkedContentColor,
+            uncheckedContainerColor = uncheckedContainerColor,
+            uncheckedContentColor = uncheckedContentColor,
+            disabledCheckedContainerColor = disabledCheckedContainerColor,
+            disabledCheckedContentColor = disabledCheckedContentColor,
+            disabledUncheckedContainerColor = disabledUncheckedContainerColor,
+            disabledUncheckedContentColor = disabledUncheckedContentColor,
+        )
+    }
+
+    /**
      * The recommended size of an icon when used inside an icon button with size
      * [SmallButtonSize] or [ExtraSmallButtonSize].
      * Use [iconSizeFor] to easily determine the icon size.
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ListHeader.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ListHeader.kt
index 519a0c7..90c2caa 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ListHeader.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ListHeader.kt
@@ -54,7 +54,7 @@
  * @param content Slot for [ListHeader] content, expected to be a single line of text.
  */
 @Composable
-public fun ListHeader(
+fun ListHeader(
     modifier: Modifier = Modifier,
     backgroundColor: Color = Color.Transparent,
     contentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
@@ -67,7 +67,7 @@
             .wrapContentSize()
             .background(backgroundColor)
             .padding(contentPadding)
-            .semantics { heading() }
+            .semantics(mergeDescendants = true) { heading() }
     ) {
         CompositionLocalProvider(
             LocalContentColor provides contentColor,
@@ -100,7 +100,7 @@
  * @param label A slot for providing label to the [ListSubheader].
  */
 @Composable
-public fun ListSubheader(
+fun ListSubheader(
     modifier: Modifier = Modifier,
     backgroundColor: Color = Color.Transparent,
     contentColor: Color = MaterialTheme.colorScheme.onBackground,
@@ -115,7 +115,7 @@
             .wrapContentSize()
             .background(backgroundColor)
             .padding(contentPadding)
-            .semantics { heading() }
+            .semantics(mergeDescendants = true) { heading() }
     ) {
         CompositionLocalProvider(
             LocalContentColor provides contentColor,
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/MaterialTheme.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/MaterialTheme.kt
index 4a3bdb1..cea4edb 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/MaterialTheme.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/MaterialTheme.kt
@@ -69,7 +69,7 @@
     val rippleIndication = rememberRipple()
     val selectionColors = rememberTextSelectionColors(rememberedColors)
     CompositionLocalProvider(
-        LocalColors provides rememberedColors,
+        LocalColorScheme provides rememberedColors,
         LocalShapes provides shapes,
         LocalTypography provides typography,
         LocalContentAlpha provides ContentAlpha.high,
@@ -87,7 +87,7 @@
     public val colorScheme: ColorScheme
         @ReadOnlyComposable
         @Composable
-        get() = LocalColors.current
+        get() = LocalColorScheme.current
 
     public val typography: Typography
         @ReadOnlyComposable
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/SelectionControls.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/SelectionControls.kt
new file mode 100644
index 0000000..128b9a5
--- /dev/null
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/SelectionControls.kt
@@ -0,0 +1,736 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3
+
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.TweenSpec
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.interaction.Interaction
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.CornerRadius
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.drawscope.DrawScope
+import androidx.compose.ui.graphics.drawscope.Fill
+import androidx.compose.ui.graphics.drawscope.Stroke
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.lerp
+import androidx.wear.compose.materialcore.animateSelectionColor
+import androidx.wear.compose.materialcore.directionVector
+import androidx.wear.compose.materialcore.toRadians
+
+/**
+ * [Checkbox] provides an animated checkbox for use as a selection control in
+ * [SelectionButton] or [SplitSelectionButton].
+ *
+ * Checkbox sample:
+ * @sample androidx.wear.compose.material3.samples.CheckboxSample
+ *
+ * @param checked Boolean flag indicating whether this checkbox is currently checked.
+ * @param modifier Modifier to be applied to the checkbox. This can be used to provide a
+ * content description for accessibility.
+ * @param colors [CheckboxColors] from which the box and checkmark colors will be obtained.
+ * @param enabled Boolean flag indicating the enabled state of the [Checkbox] (affects
+ * the color).
+ * @param onCheckedChange Callback to be invoked when Checkbox is clicked. If null, then this is
+ * passive and relies entirely on a higher-level component to control the state
+ * (such as [SelectionButton] or [SplitSelectionButton]).
+ * @param interactionSource When also providing [onCheckedChange], the [MutableInteractionSource]
+ * representing the stream of [Interaction]s for the "toggleable" tap area -
+ * can be used to customise the appearance / behavior of the Checkbox.
+ */
+@Composable
+fun Checkbox(
+    checked: Boolean,
+    modifier: Modifier = Modifier,
+    colors: CheckboxColors = CheckboxDefaults.colors(),
+    enabled: Boolean = true,
+    onCheckedChange: ((Boolean) -> Unit)? = null,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+) = androidx.wear.compose.materialcore.Checkbox(
+    checked = checked,
+    modifier = modifier,
+    boxColor = { isEnabled, isChecked ->
+        colors.boxColor(
+            enabled = isEnabled,
+            checked = isChecked
+        )
+    },
+    checkmarkColor = { isEnabled, isChecked ->
+        colors.checkmarkColor(
+            enabled = isEnabled,
+            checked = isChecked
+        )
+    },
+    enabled = enabled,
+    onCheckedChange = onCheckedChange,
+    interactionSource = interactionSource,
+    drawBox = { drawScope, color, progress ->
+        drawScope.drawBox(
+            color = color,
+            progress = progress
+        )
+    },
+    progressAnimationSpec = PROGRESS_ANIMATION_SPEC,
+    width = WIDTH,
+    height = HEIGHT
+)
+
+/**
+ * [Switch] provides an animated switch for use as a selection control in
+ * [SelectionButton] or [SplitSelectionButton].
+ *
+ * Switch samples:
+ * @sample androidx.wear.compose.material3.samples.SwitchSample
+ * Example of a switch in an RTL locale
+ * @sample androidx.wear.compose.material3.samples.RtlSwitchSample
+ *
+ * @param checked Boolean flag indicating whether this switch is currently toggled on.
+ * @param modifier Modifier to be applied to the switch. This can be used to provide a
+ * content description for accessibility.
+ * @param colors [SwitchColors] from which the colors of the thumb and track will be obtained.
+ * @param enabled Boolean flag indicating the enabled state of the [Switch] (affects
+ * the color).
+ * @param onCheckedChange Callback to be invoked when Switch is clicked. If null, then this is
+ * passive and relies entirely on a higher-level component to control the state
+ * (such as [SelectionButton] or [SplitSelectionButton]).
+ * @param interactionSource When also providing [onCheckedChange], the [MutableInteractionSource]
+ * representing the stream of [Interaction]s for the "toggleable" tap area -
+ * can be used to customise the appearance / behavior of the Switch.
+ */
+@Composable
+fun Switch(
+    checked: Boolean,
+    modifier: Modifier = Modifier,
+    colors: SwitchColors = SwitchDefaults.colors(),
+    enabled: Boolean = true,
+    onCheckedChange: ((Boolean) -> Unit)? = null,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+) = androidx.wear.compose.materialcore.Switch(
+    modifier = modifier,
+    checked = checked,
+    enabled = enabled,
+    onCheckedChange = onCheckedChange,
+    interactionSource = interactionSource,
+    trackFillColor = { isEnabled, isChecked ->
+        colors.trackColor(
+            enabled = isEnabled,
+            checked = isChecked
+        )
+    },
+    trackStrokeColor = { isEnabled, isChecked ->
+        colors.trackStrokeColor(
+            enabled = isEnabled,
+            checked = isChecked
+        )
+    },
+    thumbColor = { isEnabled, isChecked ->
+        colors.thumbColor(
+            enabled = isEnabled,
+            checked = isChecked
+        )
+    },
+    thumbIconColor = { isEnabled, isChecked ->
+        colors.thumbIconColor(
+            enabled = isEnabled,
+            checked = isChecked
+        )
+    },
+    trackWidth = TRACK_WIDTH,
+    trackHeight = TRACK_HEIGHT,
+    drawThumb = { drawScope, thumbColor, progress, thumbIconColor, isRtl ->
+        drawScope.drawThumb(
+            thumbColor,
+            progress,
+            thumbIconColor,
+            isRtl
+        )
+    },
+    progressAnimationSpec = SWITCH_PROGRESS_ANIMATION_SPEC,
+    width = WIDTH,
+    height = HEIGHT
+)
+
+/**
+ * [RadioButton] provides an animated radio button for use as a selection control in
+ * [SelectionButton] or [SplitSelectionButton].
+ *
+ * RadioButton sample:
+ * @sample androidx.wear.compose.material3.samples.RadioButtonSample
+ *
+ * @param selected Boolean flag indicating whether this radio button is currently toggled on.
+ * @param modifier Modifier to be applied to the radio button. This can be used to provide a
+ * content description for accessibility.
+ * @param colors [RadioButtonColors] from which the RadioButton colors will be obtained.
+ * @param enabled Boolean flag indicating the enabled state of the [RadioButton] (affects
+ * the color).
+ * @param onClick Callback to be invoked when RadioButton is clicked. If null, then this is
+ * passive and relies entirely on a higher-level component to control the state
+ * (such as [SelectionButton] or [SplitSelectionButton]).
+ * @param interactionSource When also providing [onClick], the [MutableInteractionSource]
+ * representing the stream of [Interaction]s for the "toggleable" tap area -
+ * can be used to customise the appearance / behavior of the RadioButton.
+ */
+@Composable
+fun RadioButton(
+    selected: Boolean,
+    modifier: Modifier = Modifier,
+    colors: RadioButtonColors = RadioButtonDefaults.colors(),
+    enabled: Boolean = true,
+    onClick: (() -> Unit)? = null,
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+) = androidx.wear.compose.materialcore.RadioButton(
+    modifier = modifier,
+    selected = selected,
+    enabled = enabled,
+    ringColor = { isEnabled, isSelected ->
+        colors.color(
+            enabled = isEnabled,
+            selected = isSelected
+        )
+    },
+    dotColor = { isEnabled, isSelected ->
+        colors.color(
+            enabled = isEnabled,
+            selected = isSelected
+        )
+    },
+    onClick = onClick,
+    interactionSource = interactionSource,
+    dotRadiusProgressDuration = { isSelected -> if (isSelected) MEDIUM_1 else SHORT_3 },
+    dotAlphaProgressDuration = SHORT_3,
+    dotAlphaProgressDelay = SHORT_2,
+    easing = STANDARD_DECELERATE,
+    width = WIDTH,
+    height = HEIGHT
+)
+
+/**
+ * Represents the content colors used in [Checkbox] in different states.
+ */
+@Immutable
+class CheckboxColors internal constructor(
+    val checkedBoxColor: Color,
+    val checkedCheckmarkColor: Color,
+    val uncheckedCheckmarkColor: Color,
+    val uncheckedBoxColor: Color,
+    val disabledCheckedBoxColor: Color,
+    val disabledCheckedCheckmarkColor: Color,
+    val disabledUncheckedBoxColor: Color,
+    val disabledUncheckedCheckmarkColor: Color,
+) {
+
+    /**
+     * Represents the box color for this [Checkbox], depending on the [enabled] and [checked]
+     * properties.
+     *
+     * @param enabled Whether the [Checkbox] is enabled
+     * @param checked Whether the [Checkbox] is currently checked or unchecked
+     */
+    @Composable
+    internal fun boxColor(enabled: Boolean, checked: Boolean): State<Color> = animateSelectionColor(
+        enabled = enabled,
+        checked = checked,
+        checkedColor = checkedBoxColor,
+        uncheckedColor = uncheckedBoxColor,
+        disabledCheckedColor = disabledCheckedBoxColor,
+        disabledUncheckedColor = disabledUncheckedBoxColor,
+        animationSpec = COLOR_ANIMATION_SPEC
+    )
+
+    /**
+     * Represents the checkmark color for this [Checkbox], depending on the [enabled] and [checked]
+     * properties.
+     *
+     * @param enabled Whether the [Checkbox] is enabled
+     * @param checked Whether the [Checkbox] is currently checked or unchecked
+     */
+    @Composable
+    internal fun checkmarkColor(enabled: Boolean, checked: Boolean): State<Color> =
+        animateSelectionColor(
+            enabled = enabled,
+            checked = checked,
+            checkedColor = checkedCheckmarkColor,
+            uncheckedColor = uncheckedCheckmarkColor,
+            disabledCheckedColor = disabledCheckedCheckmarkColor,
+            disabledUncheckedColor = disabledUncheckedCheckmarkColor,
+            animationSpec = COLOR_ANIMATION_SPEC
+        )
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || other !is CheckboxColors) return false
+
+        if (checkedBoxColor != other.checkedBoxColor) return false
+        if (checkedCheckmarkColor != other.checkedCheckmarkColor) return false
+        if (uncheckedCheckmarkColor != other.uncheckedCheckmarkColor) return false
+        if (uncheckedBoxColor != other.uncheckedBoxColor) return false
+        if (disabledCheckedBoxColor != other.disabledCheckedBoxColor) return false
+        if (disabledCheckedCheckmarkColor != other.disabledCheckedCheckmarkColor) return false
+        if (disabledUncheckedBoxColor != other.disabledUncheckedBoxColor) return false
+        if (disabledUncheckedCheckmarkColor != other.disabledUncheckedCheckmarkColor) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = checkedBoxColor.hashCode()
+        result = 31 * result + checkedCheckmarkColor.hashCode()
+        result = 31 * result + uncheckedCheckmarkColor.hashCode()
+        result = 31 * result + uncheckedBoxColor.hashCode()
+        result = 31 * result + disabledCheckedBoxColor.hashCode()
+        result = 31 * result + disabledCheckedCheckmarkColor.hashCode()
+        result = 31 * result + disabledUncheckedBoxColor.hashCode()
+        result = 31 * result + disabledUncheckedCheckmarkColor.hashCode()
+        return result
+    }
+}
+
+/**
+ * Represents the content colors used in [Switch] in different states.
+ */
+@Immutable
+class SwitchColors internal constructor(
+    val checkedThumbColor: Color,
+    val checkedThumbIconColor: Color,
+    val checkedTrackColor: Color,
+    val checkedTrackBorderColor: Color,
+    val uncheckedThumbColor: Color,
+    val uncheckedThumbIconColor: Color,
+    val uncheckedTrackColor: Color,
+    val uncheckedTrackBorderColor: Color,
+    val disabledCheckedThumbColor: Color,
+    val disabledCheckedThumbIconColor: Color,
+    val disabledCheckedTrackColor: Color,
+    val disabledCheckedTrackBorderColor: Color,
+    val disabledUncheckedThumbColor: Color,
+    val disabledUncheckedThumbIconColor: Color,
+    val disabledUncheckedTrackColor: Color,
+    val disabledUncheckedTrackBorderColor: Color,
+) {
+    @Composable
+    internal fun thumbColor(enabled: Boolean, checked: Boolean): State<Color> =
+        animateSelectionColor(
+            enabled = enabled,
+            checked = checked,
+            checkedColor = checkedThumbColor,
+            uncheckedColor = uncheckedThumbColor,
+            disabledCheckedColor = disabledCheckedThumbColor,
+            disabledUncheckedColor = disabledUncheckedThumbColor,
+            animationSpec = COLOR_ANIMATION_SPEC
+        )
+
+    @Composable
+    internal fun thumbIconColor(enabled: Boolean, checked: Boolean): State<Color> =
+        animateSelectionColor(
+            enabled = enabled,
+            checked = checked,
+            checkedColor = checkedThumbIconColor,
+            uncheckedColor = uncheckedThumbIconColor,
+            disabledCheckedColor = disabledCheckedThumbIconColor,
+            disabledUncheckedColor = disabledUncheckedThumbIconColor,
+            animationSpec = COLOR_ANIMATION_SPEC
+        )
+
+    @Composable
+    internal fun trackColor(enabled: Boolean, checked: Boolean): State<Color> =
+        animateSelectionColor(
+            enabled = enabled,
+            checked = checked,
+            checkedColor = checkedTrackColor,
+            uncheckedColor = uncheckedTrackColor,
+            disabledCheckedColor = disabledCheckedTrackBorderColor,
+            disabledUncheckedColor = disabledUncheckedTrackColor,
+            animationSpec = COLOR_ANIMATION_SPEC
+        )
+
+    @Composable
+    internal fun trackStrokeColor(enabled: Boolean, checked: Boolean): State<Color> =
+        animateSelectionColor(
+            enabled = enabled,
+            checked = checked,
+            checkedColor = checkedTrackBorderColor,
+            uncheckedColor = uncheckedTrackBorderColor,
+            disabledCheckedColor = disabledCheckedTrackColor,
+            disabledUncheckedColor = disabledUncheckedTrackBorderColor,
+            animationSpec = COLOR_ANIMATION_SPEC
+        )
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || other !is SwitchColors) return false
+
+        if (checkedThumbColor != other.checkedThumbColor) return false
+        if (checkedThumbIconColor != other.checkedThumbIconColor) return false
+        if (checkedTrackColor != other.checkedTrackColor) return false
+        if (checkedTrackBorderColor != other.checkedTrackBorderColor) return false
+        if (uncheckedThumbColor != other.uncheckedThumbColor) return false
+        if (uncheckedThumbIconColor != other.uncheckedThumbIconColor) return false
+        if (uncheckedTrackColor != other.uncheckedTrackColor) return false
+        if (uncheckedTrackBorderColor != other.uncheckedTrackBorderColor) return false
+        if (disabledCheckedThumbColor != other.disabledCheckedThumbColor) return false
+        if (disabledCheckedThumbIconColor != other.disabledCheckedThumbIconColor) return false
+        if (disabledCheckedTrackColor != other.disabledCheckedTrackColor) return false
+        if (disabledCheckedTrackBorderColor != other.disabledCheckedTrackBorderColor) return false
+        if (disabledUncheckedThumbColor != other.disabledUncheckedThumbColor) return false
+        if (disabledUncheckedThumbIconColor != other.disabledUncheckedThumbIconColor) return false
+        if (disabledUncheckedTrackColor != other.disabledUncheckedTrackColor) return false
+        if (disabledUncheckedTrackBorderColor != other.disabledCheckedTrackBorderColor) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = checkedThumbColor.hashCode()
+        result = 31 * result + checkedThumbIconColor.hashCode()
+        result = 31 * result + checkedTrackColor.hashCode()
+        result = 31 * result + checkedTrackBorderColor.hashCode()
+        result = 31 * result + uncheckedThumbColor.hashCode()
+        result = 31 * result + uncheckedThumbIconColor.hashCode()
+        result = 31 * result + uncheckedTrackColor.hashCode()
+        result = 31 * result + uncheckedTrackBorderColor.hashCode()
+        result = 31 * result + disabledCheckedThumbColor.hashCode()
+        result = 31 * result + disabledCheckedThumbIconColor.hashCode()
+        result = 31 * result + disabledCheckedTrackColor.hashCode()
+        result = 31 * result + disabledCheckedTrackBorderColor.hashCode()
+        result = 31 * result + disabledUncheckedThumbColor.hashCode()
+        result = 31 * result + disabledUncheckedThumbIconColor.hashCode()
+        result = 31 * result + disabledUncheckedTrackColor.hashCode()
+        result = 31 * result + disabledUncheckedTrackBorderColor.hashCode()
+        return result
+    }
+}
+
+/**
+ * Represents the content colors used in [RadioButton] in different states.
+ */
+@Immutable
+class RadioButtonColors internal constructor(
+    val selectedColor: Color,
+    val unselectedColor: Color,
+    val disabledSelectedColor: Color,
+    val disabledUnselectedColor: Color
+) {
+    @Composable
+    internal fun color(enabled: Boolean, selected: Boolean): State<Color> = animateSelectionColor(
+        enabled = enabled,
+        checked = selected,
+        checkedColor = selectedColor,
+        uncheckedColor = unselectedColor,
+        disabledCheckedColor = disabledSelectedColor,
+        disabledUncheckedColor = disabledUnselectedColor,
+        animationSpec = COLOR_ANIMATION_SPEC
+    )
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || other !is RadioButtonColors) return false
+
+        if (selectedColor != other.selectedColor) return false
+        if (unselectedColor != other.unselectedColor) return false
+        if (disabledSelectedColor != other.disabledSelectedColor) return false
+        if (disabledUnselectedColor != other.disabledUnselectedColor) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = selectedColor.hashCode()
+        result = 31 * result + unselectedColor.hashCode()
+        result = 31 * result + disabledSelectedColor.hashCode()
+        result = 31 * result + disabledUnselectedColor.hashCode()
+        return result
+    }
+}
+
+/**
+ * Contains the default values used by [Checkbox].
+ */
+object CheckboxDefaults {
+    /**
+     * Creates a [CheckboxColors] for use in a [Checkbox].
+     *
+     * @param checkedBoxColor The box color of this [Checkbox] when enabled and checked.
+     * @param checkedCheckmarkColor The check mark color of this [Checkbox] when enabled
+     * and checked.
+     * @param uncheckedBoxColor The box color of this [Checkbox] when enabled and unchecked.
+     * @param uncheckedCheckmarkColor The check mark color of this [Checkbox] when enabled
+     * and unchecked.
+     */
+    @Composable
+    fun colors(
+        checkedBoxColor: Color = MaterialTheme.colorScheme.primary,
+        checkedCheckmarkColor: Color = MaterialTheme.colorScheme.onPrimary,
+        uncheckedBoxColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
+        uncheckedCheckmarkColor: Color = MaterialTheme.colorScheme.background,
+    ): CheckboxColors = CheckboxColors(
+        checkedBoxColor = checkedBoxColor,
+        checkedCheckmarkColor = checkedCheckmarkColor,
+        uncheckedBoxColor = uncheckedBoxColor,
+        uncheckedCheckmarkColor = uncheckedCheckmarkColor,
+        disabledCheckedBoxColor = checkedBoxColor.toDisabledColor(
+            disabledAlpha = DisabledContainerAlpha
+        ),
+        disabledCheckedCheckmarkColor = checkedCheckmarkColor.toDisabledColor(),
+        disabledUncheckedBoxColor = uncheckedBoxColor.toDisabledColor(
+            disabledAlpha = DisabledContainerAlpha
+        ),
+        disabledUncheckedCheckmarkColor = uncheckedCheckmarkColor.toDisabledColor()
+    )
+}
+
+/**
+ * Contains the default values used by [Switch].
+ */
+object SwitchDefaults {
+    /**
+     * Creates a [SwitchColors] for use in a [Switch].
+     *
+     * @param checkedThumbColor The thumb color of this [Switch] when enabled and checked.
+     * @param checkedThumbIconColor The thumb icon color of this [Switch] when enabled and checked.
+     * @param checkedTrackColor The track color of this [Switch] when enabled and checked.
+     * @param checkedTrackStrokeColor The track border color of this [Switch] when enabled and checked.
+     * @param uncheckedThumbColor The thumb color of this [Switch] when enabled and unchecked.
+     * @param uncheckedThumbIconColor The thumb icon color of this [Switch] when enabled and checked.
+     * @param uncheckedTrackColor The track color of this [Switch] when enabled and unchecked.
+     * @param uncheckedTrackStrokeColor The track border color of this [Switch] when enabled and unchecked.
+     */
+    @Composable
+    fun colors(
+        checkedThumbColor: Color = MaterialTheme.colorScheme.onPrimary,
+        checkedThumbIconColor: Color = MaterialTheme.colorScheme.primary,
+        checkedTrackColor: Color = MaterialTheme.colorScheme.primaryDim,
+        checkedTrackStrokeColor: Color = MaterialTheme.colorScheme.primaryDim,
+        uncheckedThumbColor: Color = MaterialTheme.colorScheme.outline,
+        uncheckedThumbIconColor: Color = MaterialTheme.colorScheme.background,
+        uncheckedTrackColor: Color = MaterialTheme.colorScheme.surface,
+        uncheckedTrackStrokeColor: Color = MaterialTheme.colorScheme.outline
+    ): SwitchColors = SwitchColors(
+        checkedThumbColor = checkedThumbColor,
+        checkedThumbIconColor = checkedThumbIconColor,
+        checkedTrackColor = checkedTrackColor,
+        checkedTrackBorderColor = checkedTrackStrokeColor,
+        uncheckedThumbColor = uncheckedThumbColor,
+        uncheckedThumbIconColor = uncheckedThumbIconColor,
+        uncheckedTrackColor = uncheckedTrackColor,
+        uncheckedTrackBorderColor = uncheckedTrackStrokeColor,
+        disabledCheckedThumbColor = checkedThumbColor.toDisabledColor(),
+        disabledCheckedThumbIconColor = checkedThumbIconColor.toDisabledColor(),
+        disabledCheckedTrackColor = checkedTrackColor.toDisabledColor(
+            disabledAlpha = DisabledContainerAlpha
+        ),
+        disabledCheckedTrackBorderColor = checkedTrackStrokeColor.toDisabledColor(
+            disabledAlpha = DisabledBorderAlpha
+        ),
+        disabledUncheckedThumbColor = uncheckedThumbColor.toDisabledColor(),
+        disabledUncheckedThumbIconColor = uncheckedThumbIconColor.toDisabledColor(),
+        disabledUncheckedTrackColor = uncheckedTrackColor.toDisabledColor(
+            disabledAlpha = DisabledContainerAlpha
+        ),
+        disabledUncheckedTrackBorderColor = uncheckedTrackStrokeColor.toDisabledColor(
+            disabledAlpha = DisabledBorderAlpha
+        )
+    )
+}
+
+/**
+ * Contains the default values used by [RadioButton].
+ */
+object RadioButtonDefaults {
+    /**
+     * Creates a [RadioButtonColors] for use in a [RadioButton].
+     *
+     * @param selectedColor The color of the radio button when enabled and selected.
+     * @param unselectedColor The color of the radio button when enabled and unselected.
+     */
+    @Composable
+    fun colors(
+        selectedColor: Color = MaterialTheme.colorScheme.primary,
+        unselectedColor: Color = MaterialTheme.colorScheme.onSurfaceVariant
+    ): RadioButtonColors {
+        return RadioButtonColors(
+            selectedColor = selectedColor,
+            unselectedColor = unselectedColor,
+            disabledSelectedColor = selectedColor.toDisabledColor(),
+            disabledUnselectedColor = unselectedColor.toDisabledColor()
+        )
+    }
+}
+
+private fun DrawScope.drawBox(color: Color, progress: Float) {
+    // Centering vertically.
+    val topCornerPx = (HEIGHT - BOX_SIZE).toPx() / 2
+    val strokeWidthPx = BOX_STROKE.toPx()
+    val halfStrokeWidthPx = strokeWidthPx / 2.0f
+    val radiusPx = BOX_RADIUS.toPx()
+    val checkboxSizePx = BOX_SIZE.toPx()
+    // Aligning the box to the right.
+    val startXOffsetPx = (WIDTH - HEIGHT).toPx()
+
+    // Draw the outline of the box.
+    drawRoundRect(
+        color,
+        topLeft = Offset(
+            topCornerPx + halfStrokeWidthPx + startXOffsetPx,
+            topCornerPx + halfStrokeWidthPx
+        ),
+        size = Size(checkboxSizePx - strokeWidthPx, checkboxSizePx - strokeWidthPx),
+        cornerRadius = CornerRadius(radiusPx - halfStrokeWidthPx),
+        alpha = 1 - progress,
+        style = Stroke(strokeWidthPx)
+    )
+
+    // Fills the box.
+    drawRoundRect(
+        color,
+        topLeft = Offset(topCornerPx + startXOffsetPx, topCornerPx),
+        size = Size(checkboxSizePx, checkboxSizePx),
+        cornerRadius = CornerRadius(radiusPx),
+        alpha = progress,
+        style = Fill
+    )
+}
+
+private fun DrawScope.drawThumb(
+    thumbColor: Color,
+    progress: Float,
+    thumbIconColor: Color,
+    isRtl: Boolean
+) {
+
+    val thumbPaddingUnchecked = TRACK_HEIGHT / 2 - THUMB_RADIUS_UNCHECKED
+    val thumbPaddingChecked = TRACK_HEIGHT / 2 - THUMB_RADIUS_CHECKED
+
+    val switchThumbRadiusPx = lerp(
+        start = THUMB_RADIUS_UNCHECKED.toPx(),
+        stop = THUMB_RADIUS_CHECKED.toPx(),
+        fraction = progress
+    )
+
+    val switchTrackLengthPx = WIDTH.toPx()
+
+    // For Rtl mode the thumb progress will start from the end of the switch.
+    val thumbProgressPx = if (isRtl)
+        lerp(
+            start = switchTrackLengthPx - switchThumbRadiusPx - thumbPaddingUnchecked.toPx(),
+            stop = switchThumbRadiusPx + thumbPaddingChecked.toPx(),
+            fraction = progress
+        )
+    else
+        lerp(
+            start = switchThumbRadiusPx + thumbPaddingUnchecked.toPx(),
+            stop = switchTrackLengthPx - switchThumbRadiusPx - thumbPaddingChecked.toPx(),
+            fraction = progress
+        )
+
+    drawCircle(
+        color = thumbColor,
+        radius = switchThumbRadiusPx,
+        center = Offset(thumbProgressPx, center.y)
+    )
+
+    val totalDist = switchTrackLengthPx - 2 * switchThumbRadiusPx - 4.dp.toPx()
+
+    // Offset value to be added if RTL mode is enabled.
+    // We need to move the tick to the checked position in ltr mode when unchecked.
+    val rtlOffset = switchTrackLengthPx - 2 * THUMB_RADIUS_CHECKED.toPx() - 4.dp.toPx()
+
+    val distMoved = if (isRtl) rtlOffset - progress * totalDist else progress * totalDist
+
+    // Draw tick icon
+    drawTickIcon(thumbIconColor, progress, distMoved)
+}
+
+private fun DrawScope.drawTickIcon(tickColor: Color, alpha: Float, distMoved: Float) {
+    val tickBaseLength = TICK_BASE_LENGTH.toPx()
+    val tickStickLength = TICK_STICK_LENGTH.toPx()
+    val tickTotalLength = tickBaseLength + tickStickLength
+    val center = Offset(9.dp.toPx(), 9.dp.toPx())
+    val angle = TICK_ROTATION - TICK_ROTATION / tickTotalLength * tickTotalLength
+    val angleRadians = angle.toRadians()
+
+    val baseStart = Offset(6.7f.dp.toPx() + distMoved, 13.3f.dp.toPx())
+
+    val path = Path()
+    path.moveTo(baseStart.rotate(angleRadians, center))
+    path.lineTo(
+        (baseStart + Offset(tickBaseLength, tickBaseLength)).rotate(angleRadians, center)
+    )
+
+    val stickStart = Offset(9.3f.dp.toPx() + distMoved, 16.3f.dp.toPx())
+    // Move back to the start of the stick (without drawing)
+    path.moveTo(stickStart.rotate(angleRadians, center))
+    path.lineTo(
+        Offset(stickStart.x + tickStickLength, stickStart.y - tickStickLength).rotate(
+            angleRadians,
+            center
+        )
+    )
+    // Use StrokeCap.Butt because Square adds an extension on the end of each line.
+    drawPath(
+        path,
+        tickColor,
+        style = Stroke(width = 1.dp.toPx(), cap = StrokeCap.Butt),
+        alpha = alpha
+    )
+}
+
+private fun Path.moveTo(offset: Offset) {
+    moveTo(offset.x, offset.y)
+}
+
+private fun Path.lineTo(offset: Offset) {
+    lineTo(offset.x, offset.y)
+}
+
+private fun Offset.rotate(angleRadians: Float): Offset {
+    val angledDirection = directionVector(angleRadians)
+    return angledDirection * x + angledDirection.rotate90() * y
+}
+
+private fun Offset.rotate(angleRadians: Float, center: Offset): Offset =
+    (this - center).rotate(angleRadians) + center
+
+private fun Offset.rotate90() = Offset(-y, x)
+
+private val BOX_STROKE = 2.dp
+private val BOX_RADIUS = 2.dp
+private val BOX_SIZE = 18.dp
+
+private val THUMB_RADIUS_UNCHECKED = 7.dp
+private val THUMB_RADIUS_CHECKED = 9.dp
+private val TRACK_WIDTH = 32.dp
+private val TRACK_HEIGHT = 22.dp
+private val TICK_BASE_LENGTH = 3.dp
+private val TICK_STICK_LENGTH = 7.dp
+private const val TICK_ROTATION = 15f
+
+private val COLOR_ANIMATION_SPEC: AnimationSpec<Color> = tween(MEDIUM_1, 0, STANDARD_DECELERATE)
+private val PROGRESS_ANIMATION_SPEC: TweenSpec<Float> = tween(MEDIUM_1, 0, STANDARD_DECELERATE)
+private val SWITCH_PROGRESS_ANIMATION_SPEC: TweenSpec<Float> =
+    tween(MEDIUM_2, 0, STANDARD_DECELERATE)
+
+private val WIDTH = 32.dp
+private val HEIGHT = 24.dp
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Shapes.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Shapes.kt
index d318e17..f2d76f6 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Shapes.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Shapes.kt
@@ -15,13 +15,16 @@
  */
 package androidx.wear.compose.material3
 
+import androidx.compose.foundation.shape.CornerBasedShape
 import androidx.compose.foundation.shape.CornerSize
 import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.ReadOnlyComposable
 import androidx.compose.runtime.staticCompositionLocalOf
-import androidx.compose.ui.graphics.RectangleShape
 import androidx.compose.ui.graphics.Shape
-import androidx.compose.ui.unit.dp
+import androidx.wear.compose.material3.tokens.ShapeKeyTokens
+import androidx.wear.compose.material3.tokens.ShapeTokens
 
 /**
  * Material surfaces can be displayed in different shapes. Shapes direct attention, identify
@@ -31,22 +34,20 @@
  * curved shapes (mostly polygonal). The default [Shapes] theme for Material3 is rounded rectangles,
  * with various degrees of corner roundness:
  *
- * - None
  * - Extra Small
  * - Small
  * - Medium
  * - Large
- * - Full
+ * - Extra Large
  *
  * You can customize the shape system for all components in the [MaterialTheme] or you can do it
  * on a per component basis by overriding the shape parameter for that
- * component. For example, by default, buttons use the shape style “full.” If your product requires
+ * component. For example, by default, buttons use the shape style "large". If your product requires
  * a smaller amount of roundness, you can override the shape parameter with a different shape
  * value like [Shapes.small].
  *
  * TODO(b/273226734) Review documentation with references to components that use the shape themes.
  *
- * @param none By default, provides [ShapeDefaults.None], which is [RectangleShape]
  * @param extraSmall By default, provides [ShapeDefaults.ExtraSmall], a [RoundedCornerShape]
  * with 4dp [CornerSize] (used by bundled Cards).
  * @param small By default, provides [ShapeDefaults.Small], a [RoundedCornerShape]
@@ -57,71 +58,57 @@
  * with 24dp [CornerSize] (used by Cards).
  * @param extraLarge By default, provides [ShapeDefaults.ExtraLarge], a
  * [RoundedCornerShape] with 32dp [CornerSize].
- * @param full By default, provides [ShapeDefaults.Full], a Stadium shape with
- * 50% rounded [CornerSize] (used by Button).
  */
 @Immutable
 class Shapes(
-    val none: Shape = ShapeDefaults.None,
-    val extraSmall: Shape = ShapeDefaults.ExtraSmall,
-    val small: Shape = ShapeDefaults.Small,
-    val medium: Shape = ShapeDefaults.Medium,
-    val large: Shape = ShapeDefaults.Large,
-    val extraLarge: Shape = ShapeDefaults.ExtraLarge,
-    val full: Shape = ShapeDefaults.Full,
+    val extraSmall: CornerBasedShape = ShapeDefaults.ExtraSmall,
+    val small: CornerBasedShape = ShapeDefaults.Small,
+    val medium: CornerBasedShape = ShapeDefaults.Medium,
+    val large: CornerBasedShape = ShapeDefaults.Large,
+    val extraLarge: CornerBasedShape = ShapeDefaults.ExtraLarge,
 ) {
     /** Returns a copy of this Shapes, optionally overriding some of the values. */
     fun copy(
-        none: Shape = this.none,
-        extraSmall: Shape = this.extraSmall,
-        small: Shape = this.small,
-        medium: Shape = this.medium,
-        large: Shape = this.large,
-        extraLarge: Shape = this.extraLarge,
-        full: Shape = this.full
+        extraSmall: CornerBasedShape = this.extraSmall,
+        small: CornerBasedShape = this.small,
+        medium: CornerBasedShape = this.medium,
+        large: CornerBasedShape = this.large,
+        extraLarge: CornerBasedShape = this.extraLarge,
     ): Shapes = Shapes(
-        none = none,
         extraSmall = extraSmall,
         small = small,
         medium = medium,
         large = large,
         extraLarge = extraLarge,
-        full = full,
     )
 
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (other !is Shapes) return false
-        if (none != other.none) return false
         if (extraSmall != other.extraSmall) return false
         if (small != other.small) return false
         if (medium != other.medium) return false
         if (large != other.large) return false
         if (extraLarge != other.extraLarge) return false
-        if (full != other.full) return false
         return true
     }
 
     override fun hashCode(): Int {
-        var result = none.hashCode()
-        result = 31 * result + extraSmall.hashCode()
+        var result = extraSmall.hashCode()
         result = 31 * result + small.hashCode()
         result = 31 * result + medium.hashCode()
         result = 31 * result + large.hashCode()
         result = 31 * result + extraLarge.hashCode()
-        result = 31 * result + full.hashCode()
         return result
     }
 
     override fun toString(): String {
         return "Shapes(" +
-            "none=$none, " +
             "extraSmall=$extraSmall, " +
             "small=$small, " +
             "medium=$medium, " +
             "large=$large, " +
-            "extraLarge=$extraLarge, " +
-            "full=$full)"
+            "extraLarge=$extraLarge)"
     }
 }
 
@@ -129,26 +116,45 @@
  * Contains the default values used by [Shapes]
  */
 object ShapeDefaults {
-    /** None provides a RectangleShape */
-    val None = RectangleShape
 
     /** Extra small sized corner shape */
-    val ExtraSmall = RoundedCornerShape(corner = CornerSize(4.dp))
+    val ExtraSmall = ShapeTokens.CornerExtraSmall
 
     /** Small sized corner shape */
-    val Small = RoundedCornerShape(corner = CornerSize(8.dp))
+    val Small = ShapeTokens.CornerSmall
 
     /** Medium sized corner shape */
-    val Medium = RoundedCornerShape(corner = CornerSize(16.dp))
+    val Medium = ShapeTokens.CornerMedium
 
     /** Large sized corner shape */
-    val Large = RoundedCornerShape(corner = CornerSize(26.dp))
+    val Large = ShapeTokens.CornerLarge
 
     /** Extra large sized corner shape */
-    val ExtraLarge = RoundedCornerShape(corner = CornerSize(32.dp))
+    val ExtraLarge = ShapeTokens.CornerExtraLarge
+}
 
-    /** Full provides a stadium-shape with 50% rounded corners */
-    val Full = RoundedCornerShape(corner = CornerSize(50))
+/**
+ * Helper function for component shape tokens. Here is an example on how to use component color
+ * tokens:
+ * ``MaterialTheme.shapes.fromToken(FabPrimarySmallTokens.ContainerShape)``
+ */
+internal fun Shapes.fromToken(value: ShapeKeyTokens): Shape {
+    return when (value) {
+        ShapeKeyTokens.CornerExtraSmall -> extraSmall
+        ShapeKeyTokens.CornerSmall -> small
+        ShapeKeyTokens.CornerMedium -> medium
+        ShapeKeyTokens.CornerLarge -> large
+        ShapeKeyTokens.CornerExtraLarge -> extraLarge
+        ShapeKeyTokens.CornerFull -> ShapeTokens.CornerFull
+        ShapeKeyTokens.CornerNone -> ShapeTokens.CornerNone
+    }
+}
+
+/** Converts a shape token key to the local shape provided by the theme */
+@Composable
+@ReadOnlyComposable
+internal fun ShapeKeyTokens.toShape(): Shape {
+    return MaterialTheme.shapes.fromToken(this)
 }
 
 /** CompositionLocal used to specify the default shapes for the surfaces. */
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Slider.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Slider.kt
index cfe19ea..9563288 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Slider.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Slider.kt
@@ -28,6 +28,7 @@
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Add
 import androidx.compose.material.ripple.rememberRipple
@@ -122,7 +123,7 @@
                 value, enabled, onValueChange, valueRange, steps
             )
             .height(InlineSliderDefaults.SliderHeight)
-            .clip(MaterialTheme.shapes.full)
+            .clip(CircleShape) // TODO(b/290625297) Replace with tokens
     ) {
         val visibleSegments = if (segmented) steps + 1 else 1
 
@@ -172,7 +173,7 @@
                     modifier = Modifier
                         .height(InlineSliderDefaults.BarHeight)
                         .weight(1f)
-                        .clip(MaterialTheme.shapes.full)
+                        .clip(CircleShape) // TODO(b/290625297) Replace with token
                         .drawProgressBar(
                             selectedBarColor = selectedBarColor,
                             unselectedBarColor = unselectedBarColor,
@@ -327,13 +328,13 @@
         unselectedBarColor: Color = MaterialTheme.colorScheme.background.copy(alpha = 0.3f),
         barSeparatorColor: Color = MaterialTheme.colorScheme.primaryDim,
         disabledContainerColor: Color = containerColor.toDisabledColor(
-            disabledAlpha = DisabledBorderAndContainerAlpha
+            disabledAlpha = DisabledContainerAlpha
         ),
         disabledButtonIconColor: Color = buttonIconColor.toDisabledColor(),
         disabledSelectedBarColor: Color = selectedBarColor.toDisabledColor(),
         disabledUnselectedBarColor: Color = unselectedBarColor.toDisabledColor(),
         disabledBarSeparatorColor: Color = barSeparatorColor.toDisabledColor(
-            disabledAlpha = DisabledBorderAndContainerAlpha
+            disabledAlpha = DisabledContainerAlpha
         )
     ): InlineSliderColors = InlineSliderColors(
         containerColor = containerColor,
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/SwipeToDismissBox.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/SwipeToDismissBox.kt
index 03a8069..9e9eb84 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/SwipeToDismissBox.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/SwipeToDismissBox.kt
@@ -26,12 +26,19 @@
 import androidx.wear.compose.foundation.LocalSwipeToDismissContentScrimColor
 import androidx.wear.compose.foundation.SwipeToDismissBoxState
 import androidx.wear.compose.foundation.SwipeToDismissKeys
+import androidx.wear.compose.foundation.edgeSwipeToDismiss
 import androidx.wear.compose.foundation.rememberSwipeToDismissBoxState
 
 /**
  * Wear Material 3 [SwipeToDismissBox] that handles the swipe-to-dismiss gesture. Takes a single
  * slot for the background (only displayed during the swipe gesture) and the foreground content.
  *
+ * Example of a [SwipeToDismissBox] with stateful composables:
+ * @sample androidx.wear.compose.material3.samples.StatefulSwipeToDismissBox
+ *
+ * Example of using [Modifier.edgeSwipeToDismiss] with [SwipeToDismissBox]:
+ * @sample androidx.wear.compose.material3.samples.EdgeSwipeForSwipeToDismiss
+ *
  * For more information, see the
  * [Swipe to dismiss](https://developer.android.com/training/wearables/components/swipe-to-dismiss)
  * guide.
@@ -86,6 +93,12 @@
  * This overload takes an [onDismissed] parameter which is used to execute a command when the
  * swipe to dismiss has completed, such as navigating to another screen.
  *
+ * Example of a simple SwipeToDismissBox:
+ * @sample androidx.wear.compose.material3.samples.SimpleSwipeToDismissBox
+ *
+ * Example of using [Modifier.edgeSwipeToDismiss] with [SwipeToDismissBox]:
+ * @sample androidx.wear.compose.material3.samples.EdgeSwipeForSwipeToDismiss
+ *
  * For more information, see the
  * [Swipe to dismiss](https://developer.android.com/training/wearables/components/swipe-to-dismiss)
  * guide.
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextButton.kt
index fefc897..e0349a1 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextButton.kt
@@ -102,6 +102,71 @@
 }
 
 /**
+ * Wear Material [TextToggleButton] is a filled text toggle button which switches between primary
+ * colors and tonal colors depending on [checked] value, and offers a single slot for
+ * text.
+ *
+ * Set the size of the [TextToggleButton] with Modifier.[touchTargetAwareSize]
+ * to ensure that the background padding will correctly reach the edge of the minimum touch target.
+ * The recommended text button sizes are [TextButtonDefaults.DefaultButtonSize],
+ * [TextButtonDefaults.LargeButtonSize], [TextButtonDefaults.SmallButtonSize] and
+ * [TextButtonDefaults.ExtraSmallButtonSize].
+ *
+ * [TextToggleButton] can be enabled or disabled. A disabled button will not respond to
+ * click events. When enabled, the checked and unchecked events are propagated by [onCheckedChange].
+ *
+ * A simple text toggle button using the default colors
+ * @sample androidx.wear.compose.material3.samples.TextToggleButtonSample
+ *
+ * @param checked Boolean flag indicating whether this toggle button is currently checked.
+ * @param onCheckedChange Callback to be invoked when this toggle button is clicked.
+ * @param modifier Modifier to be applied to the toggle button.
+ * @param enabled Controls the enabled state of the toggle button. When `false`,
+ * this toggle button will not be clickable.
+ * @param colors [ToggleButtonColors] that will be used to resolve the container and
+ * content color for this toggle button.
+ * @param interactionSource The [MutableInteractionSource] representing the stream of
+ * [Interaction]s for this toggle button. You can create and pass in your own remembered
+ * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
+ * appearance / behavior of this ToggleButton in different [Interaction]s.
+ * @param shape Defines the shape for this toggle button. It is strongly recommended to use the
+ * default as this shape is a key characteristic of the Wear Material 3 Theme.
+ * @param border Optional [BorderStroke] for the [TextToggleButton].
+ * @param content The text to be drawn inside the toggle button.
+ */
+@Composable
+fun TextToggleButton(
+    checked: Boolean,
+    onCheckedChange: (Boolean) -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    colors: ToggleButtonColors = TextButtonDefaults.textToggleButtonColors(),
+    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+    shape: Shape = CircleShape,
+    border: BorderStroke? = null,
+    content: @Composable BoxScope.() -> Unit,
+) {
+    androidx.wear.compose.materialcore.ToggleButton(
+        checked = checked,
+        onCheckedChange = onCheckedChange,
+        modifier = modifier.minimumInteractiveComponentSize(),
+        enabled = enabled,
+        backgroundColor = { isEnabled, isChecked ->
+            colors.containerColor(enabled = isEnabled, checked = isChecked)
+        },
+        border = { _, _ -> rememberUpdatedState(border) },
+        toggleButtonSize = TextButtonDefaults.DefaultButtonSize,
+        interactionSource = interactionSource,
+        shape = shape,
+        content = provideScopeContent(
+            colors.contentColor(enabled = enabled, checked = checked),
+            MaterialTheme.typography.buttonMedium,
+            content
+        )
+    )
+}
+
+/**
  * Contains the default values used by [TextButton].
  */
 object TextButtonDefaults {
@@ -129,7 +194,10 @@
     ): TextButtonColors {
         return textButtonColors(
             containerColor = containerColor,
-            contentColor = contentColor
+            contentColor = contentColor,
+            disabledContainerColor = MaterialTheme.colorScheme.onSurface.toDisabledColor(
+                disabledAlpha = DisabledContainerAlpha
+            ),
         )
     }
 
@@ -152,7 +220,10 @@
     ): TextButtonColors {
         return textButtonColors(
             containerColor = containerColor,
-            contentColor = contentColor
+            contentColor = contentColor,
+            disabledContainerColor = MaterialTheme.colorScheme.onSurface.toDisabledColor(
+                disabledAlpha = DisabledContainerAlpha
+            ),
         )
     }
 
@@ -191,9 +262,7 @@
     fun textButtonColors(
         containerColor: Color = Color.Transparent,
         contentColor: Color = MaterialTheme.colorScheme.onBackground,
-        disabledContainerColor: Color = MaterialTheme.colorScheme.onSurface.toDisabledColor(
-            disabledAlpha = DisabledBorderAndContainerAlpha
-        ),
+        disabledContainerColor: Color = Color.Transparent,
         disabledContentColor: Color = MaterialTheme.colorScheme.onSurface.toDisabledColor()
     ): TextButtonColors = TextButtonColors(
         containerColor = containerColor,
@@ -203,6 +272,52 @@
     )
 
     /**
+     * Creates a [ToggleButtonColors] for a [TextToggleButton]
+     * - by default, a colored background with a contrasting content color.
+     * If the button is disabled, then the
+     * colors will have an alpha ([ContentAlpha.disabled]) value applied.
+     *
+     * @param checkedContainerColor the container color of this [TextToggleButton] when enabled
+     * and checked
+     * @param checkedContentColor the content color of this [TextToggleButton] when enabled and
+     * checked
+     * @param uncheckedContainerColor the container color of this [TextToggleButton] when enabled
+     * and unchecked
+     * @param uncheckedContentColor the content color of this [TextToggleButton] when enabled and
+     * unchecked
+     * @param disabledCheckedContainerColor the container color of this [TextToggleButton] when
+     * checked and not enabled
+     * @param disabledCheckedContentColor the content color of this [TextToggleButton] when checked
+     * and not enabled
+     * @param disabledUncheckedContainerColor the container color of this [TextToggleButton] when
+     * unchecked and not enabled
+     * @param disabledUncheckedContentColor the content color of this [TextToggleButton] when
+     * unchecked and not enabled
+     */
+    @Composable
+    fun textToggleButtonColors(
+        checkedContainerColor: Color = MaterialTheme.colorScheme.primary,
+        checkedContentColor: Color = MaterialTheme.colorScheme.onPrimary,
+        uncheckedContainerColor: Color = MaterialTheme.colorScheme.surface,
+        uncheckedContentColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
+        disabledCheckedContainerColor: Color = checkedContainerColor.toDisabledColor(),
+        disabledCheckedContentColor: Color = checkedContentColor.toDisabledColor(),
+        disabledUncheckedContainerColor: Color = uncheckedContainerColor.toDisabledColor(),
+        disabledUncheckedContentColor: Color = uncheckedContentColor.toDisabledColor(),
+    ): ToggleButtonColors {
+        return ToggleButtonColors(
+            checkedContainerColor = checkedContainerColor,
+            checkedContentColor = checkedContentColor,
+            uncheckedContainerColor = uncheckedContainerColor,
+            uncheckedContentColor = uncheckedContentColor,
+            disabledCheckedContainerColor = disabledCheckedContainerColor,
+            disabledCheckedContentColor = disabledCheckedContentColor,
+            disabledUncheckedContainerColor = disabledUncheckedContainerColor,
+            disabledUncheckedContentColor = disabledUncheckedContentColor,
+        )
+    }
+
+    /**
      * The recommended background size of an extra small, compact button.
      * It is recommended to apply this size using [Modifier.touchTargetAwareSize].
      */
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ToggleButtonColors.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ToggleButtonColors.kt
new file mode 100644
index 0000000..c83f367
--- /dev/null
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ToggleButtonColors.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.ui.graphics.Color
+
+/**
+ * Represents the different container and content colors used for toggle buttons
+ * ([IconToggleButton] and [TextToggleButton]) in various states, that are checked, unchecked,
+ * enabled and disabled.
+ */
+@Immutable
+public class ToggleButtonColors public constructor(
+    val checkedContainerColor: Color,
+    val checkedContentColor: Color,
+    val uncheckedContainerColor: Color,
+    val uncheckedContentColor: Color,
+    val disabledCheckedContainerColor: Color,
+    val disabledCheckedContentColor: Color,
+    val disabledUncheckedContainerColor: Color,
+    val disabledUncheckedContentColor: Color,
+) {
+
+    /**
+     * Determines the container color based on whether the toggle button is [enabled]
+     * and [checked].
+     *
+     * @param enabled Whether the toggle button is enabled
+     * @param checked Whether the toggle button is checked
+     */
+    @Composable
+    fun containerColor(enabled: Boolean, checked: Boolean): State<Color> {
+        return rememberUpdatedState(
+            if (enabled) {
+                if (checked) checkedContainerColor else uncheckedContainerColor
+            } else {
+                if (checked) disabledCheckedContainerColor else disabledUncheckedContainerColor
+            }
+        )
+    }
+
+    /**
+     * Determines the content color based on whether the toggle button is [enabled]
+     * and [checked].
+     *
+     * @param enabled Whether the toggle button is enabled
+     * @param checked Whether the toggle button is checked
+     */
+    @Composable
+    fun contentColor(enabled: Boolean, checked: Boolean): State<Color> {
+        return rememberUpdatedState(
+            if (enabled) {
+                if (checked) checkedContentColor else uncheckedContentColor
+            } else {
+                if (checked) disabledCheckedContentColor else disabledUncheckedContentColor
+            }
+        )
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null) return false
+        if (this::class != other::class) return false
+
+        other as ToggleButtonColors
+
+        if (checkedContainerColor != other.checkedContainerColor) return false
+        if (checkedContentColor != other.checkedContentColor) return false
+        if (uncheckedContainerColor != other.uncheckedContainerColor) return false
+        if (uncheckedContentColor != other.uncheckedContentColor) return false
+        if (disabledCheckedContainerColor != other.disabledCheckedContainerColor) return false
+        if (disabledCheckedContentColor != other.disabledCheckedContentColor) return false
+        if (disabledUncheckedContainerColor != other.disabledUncheckedContainerColor) return false
+        if (disabledUncheckedContentColor != other.disabledUncheckedContentColor) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = checkedContainerColor.hashCode()
+        result = 31 * result + checkedContentColor.hashCode()
+        result = 31 * result + uncheckedContainerColor.hashCode()
+        result = 31 * result + uncheckedContentColor.hashCode()
+        result = 31 * result + disabledCheckedContainerColor.hashCode()
+        result = 31 * result + disabledCheckedContentColor.hashCode()
+        result = 31 * result + disabledUncheckedContainerColor.hashCode()
+        result = 31 * result + disabledUncheckedContentColor.hashCode()
+        return result
+    }
+}
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ColorSchemeKeyTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ColorSchemeKeyTokens.kt
new file mode 100644
index 0000000..9bbfe57
--- /dev/null
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ColorSchemeKeyTokens.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// VERSION: v0_7
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+package androidx.wear.compose.material3.tokens
+
+internal enum class ColorSchemeKeyTokens {
+    Background,
+    Error,
+    OnBackground,
+    OnError,
+    OnPrimary,
+    OnPrimaryContainer,
+    OnSecondary,
+    OnSecondaryContainer,
+    OnSurface,
+    OnSurfaceVariant,
+    OnTertiary,
+    OnTertiaryContainer,
+    Outline,
+    OutlineVariant,
+    Primary,
+    PrimaryContainer,
+    PrimaryDim,
+    Secondary,
+    SecondaryContainer,
+    SecondaryDim,
+    Surface,
+    SurfaceBright,
+    SurfaceDim,
+    Tertiary,
+    TertiaryContainer,
+    TertiaryDim,
+}
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ColorTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ColorTokens.kt
new file mode 100644
index 0000000..e3be595
--- /dev/null
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ColorTokens.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// VERSION: v0_8
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+package androidx.wear.compose.material3.tokens
+
+internal object ColorTokens {
+    val Background = PaletteTokens.Neutral0
+    val Error = PaletteTokens.Error65
+    val OnBackground = PaletteTokens.Neutral100
+    val OnError = PaletteTokens.Error10
+    val OnPrimary = PaletteTokens.Primary10
+    val OnPrimaryContainer = PaletteTokens.Primary90
+    val OnSecondary = PaletteTokens.Secondary10
+    val OnSecondaryContainer = PaletteTokens.Secondary90
+    val OnSurface = PaletteTokens.Neutral95
+    val OnSurfaceVariant = PaletteTokens.NeutralVariant80
+    val OnTertiary = PaletteTokens.Tertiary10
+    val OnTertiaryContainer = PaletteTokens.Tertiary90
+    val Outline = PaletteTokens.NeutralVariant60
+    val OutlineVariant = PaletteTokens.NeutralVariant40
+    val Primary = PaletteTokens.Primary90
+    val PrimaryContainer = PaletteTokens.Primary30
+    val PrimaryDim = PaletteTokens.Primary80
+    val Secondary = PaletteTokens.Secondary90
+    val SecondaryContainer = PaletteTokens.Secondary30
+    val SecondaryDim = PaletteTokens.Secondary80
+    val Surface = PaletteTokens.Neutral20
+    val SurfaceBright = PaletteTokens.Neutral30
+    val SurfaceDim = PaletteTokens.Neutral15
+    val Tertiary = PaletteTokens.Tertiary90
+    val TertiaryContainer = PaletteTokens.Tertiary30
+    val TertiaryDim = PaletteTokens.Tertiary80
+}
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/PaletteTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/PaletteTokens.kt
new file mode 100644
index 0000000..65e3d32
--- /dev/null
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/PaletteTokens.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// VERSION: v0_8
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+package androidx.wear.compose.material3.tokens
+
+import androidx.compose.ui.graphics.Color
+
+internal object PaletteTokens {
+    val Error0 = Color(red = 0, green = 0, blue = 0)
+    val Error10 = Color(red = 65, green = 0, blue = 2)
+    val Error100 = Color(red = 255, green = 255, blue = 255)
+    val Error20 = Color(red = 104, green = 1, blue = 7)
+    val Error30 = Color(red = 137, green = 29, blue = 26)
+    val Error40 = Color(red = 170, green = 53, blue = 47)
+    val Error50 = Color(red = 203, green = 77, blue = 69)
+    val Error60 = Color(red = 236, green = 102, blue = 91)
+    val Error65 = Color(red = 253, green = 114, blue = 103)
+    val Error70 = Color(red = 255, green = 137, blue = 126)
+    val Error80 = Color(red = 255, green = 180, blue = 172)
+    val Error85 = Color(red = 255, green = 199, blue = 193)
+    val Error90 = Color(red = 255, green = 218, blue = 214)
+    val Error95 = Color(red = 255, green = 237, blue = 234)
+    val Neutral0 = Color(red = 0, green = 0, blue = 0)
+    val Neutral10 = Color(red = 31, green = 31, blue = 31)
+    val Neutral100 = Color(red = 255, green = 255, blue = 255)
+    val Neutral15 = Color(red = 37, green = 38, blue = 38)
+    val Neutral20 = Color(red = 48, green = 48, blue = 48)
+    val Neutral30 = Color(red = 71, green = 71, blue = 71)
+    val Neutral40 = Color(red = 94, green = 94, blue = 94)
+    val Neutral50 = Color(red = 117, green = 117, blue = 117)
+    val Neutral60 = Color(red = 143, green = 143, blue = 143)
+    val Neutral70 = Color(red = 171, green = 171, blue = 171)
+    val Neutral80 = Color(red = 199, green = 199, blue = 199)
+    val Neutral90 = Color(red = 227, green = 227, blue = 227)
+    val Neutral95 = Color(red = 242, green = 242, blue = 242)
+    val NeutralVariant0 = Color(red = 0, green = 0, blue = 0)
+    val NeutralVariant10 = Color(red = 25, green = 29, blue = 28)
+    val NeutralVariant100 = Color(red = 255, green = 255, blue = 255)
+    val NeutralVariant20 = Color(red = 45, green = 49, blue = 47)
+    val NeutralVariant30 = Color(red = 68, green = 71, blue = 70)
+    val NeutralVariant40 = Color(red = 92, green = 95, blue = 94)
+    val NeutralVariant50 = Color(red = 116, green = 119, blue = 117)
+    val NeutralVariant60 = Color(red = 142, green = 145, blue = 143)
+    val NeutralVariant70 = Color(red = 169, green = 172, blue = 170)
+    val NeutralVariant80 = Color(red = 196, green = 199, blue = 197)
+    val NeutralVariant90 = Color(red = 225, green = 227, blue = 225)
+    val NeutralVariant95 = Color(red = 239, green = 242, blue = 239)
+    val Primary0 = Color(red = 0, green = 0, blue = 0)
+    val Primary10 = Color(red = 4, green = 30, blue = 73)
+    val Primary100 = Color(red = 255, green = 255, blue = 255)
+    val Primary20 = Color(red = 4, green = 30, blue = 73)
+    val Primary30 = Color(red = 8, green = 66, blue = 160)
+    val Primary40 = Color(red = 11, green = 87, blue = 208)
+    val Primary50 = Color(red = 27, green = 110, blue = 243)
+    val Primary60 = Color(red = 76, green = 141, blue = 246)
+    val Primary70 = Color(red = 124, green = 172, blue = 248)
+    val Primary80 = Color(red = 168, green = 199, blue = 250)
+    val Primary90 = Color(red = 211, green = 227, blue = 253)
+    val Primary95 = Color(red = 236, green = 243, blue = 254)
+    val Secondary0 = Color(red = 0, green = 0, blue = 0)
+    val Secondary10 = Color(red = 0, green = 29, blue = 53)
+    val Secondary100 = Color(red = 255, green = 255, blue = 255)
+    val Secondary20 = Color(red = 0, green = 51, blue = 85)
+    val Secondary30 = Color(red = 0, green = 51, blue = 85)
+    val Secondary40 = Color(red = 0, green = 99, blue = 155)
+    val Secondary50 = Color(red = 4, green = 125, blue = 183)
+    val Secondary60 = Color(red = 57, green = 152, blue = 211)
+    val Secondary70 = Color(red = 90, green = 179, blue = 240)
+    val Secondary80 = Color(red = 127, green = 207, blue = 255)
+    val Secondary90 = Color(red = 194, green = 231, blue = 255)
+    val Secondary95 = Color(red = 223, green = 243, blue = 255)
+    val Tertiary0 = Color(red = 0, green = 0, blue = 0)
+    val Tertiary10 = Color(red = 7, green = 39, blue = 17)
+    val Tertiary100 = Color(red = 255, green = 255, blue = 255)
+    val Tertiary20 = Color(red = 7, green = 39, blue = 17)
+    val Tertiary30 = Color(red = 15, green = 82, blue = 35)
+    val Tertiary40 = Color(red = 20, green = 108, blue = 46)
+    val Tertiary50 = Color(red = 25, green = 134, blue = 57)
+    val Tertiary60 = Color(red = 30, green = 164, blue = 70)
+    val Tertiary70 = Color(red = 55, green = 190, blue = 95)
+    val Tertiary80 = Color(red = 109, green = 213, blue = 140)
+    val Tertiary90 = Color(red = 196, green = 238, blue = 208)
+    val Tertiary95 = Color(red = 231, green = 248, blue = 237)
+}
diff --git a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence2.java b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ShapeKeyTokens.kt
similarity index 63%
copy from lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence2.java
copy to wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ShapeKeyTokens.kt
index aeb87da..0b8f5ed 100644
--- a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence2.java
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ShapeKeyTokens.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,15 +14,17 @@
  * limitations under the License.
  */
 
-package androidx.lifecycle.observers;
+// VERSION: v0_7
+// GENERATED CODE - DO NOT MODIFY BY HAND
 
-import androidx.lifecycle.Lifecycle;
+package androidx.wear.compose.material3.tokens
 
-@SuppressWarnings("deprecation")
-public class DerivedSequence2 extends DerivedSequence1 {
-
-    @androidx.lifecycle.OnLifecycleEvent(Lifecycle.Event.ON_STOP)
-    void onStop() {
-
-    }
+internal enum class ShapeKeyTokens {
+    CornerExtraLarge,
+    CornerExtraSmall,
+    CornerFull,
+    CornerLarge,
+    CornerMedium,
+    CornerNone,
+    CornerSmall,
 }
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ShapeTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ShapeTokens.kt
new file mode 100644
index 0000000..7a65a84
--- /dev/null
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/ShapeTokens.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// VERSION: v0_7
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+package androidx.wear.compose.material3.tokens
+
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.unit.dp
+
+internal object ShapeTokens {
+    val CornerExtraLarge = RoundedCornerShape(36.0.dp)
+    val CornerExtraSmall = RoundedCornerShape(4.0.dp)
+    val CornerFull = CircleShape
+    val CornerLarge = RoundedCornerShape(26.0.dp)
+    val CornerMedium = RoundedCornerShape(16.0.dp)
+    val CornerNone = RectangleShape
+    val CornerSmall = RoundedCornerShape(8.0.dp)
+}
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/DemoApp.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/DemoApp.kt
index 341e909..7e7a1a8 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/DemoApp.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/DemoApp.kt
@@ -40,8 +40,6 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.input.rotary.onRotaryScrollEvent
 import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.semantics.heading
-import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
 import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
@@ -147,7 +145,7 @@
                     style = MaterialTheme.typography.caption1,
                     color = Color.White,
                     textAlign = TextAlign.Center,
-                    modifier = Modifier.fillMaxWidth().semantics { heading() }
+                    modifier = Modifier.fillMaxWidth()
 
                 )
             }
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ExpandableDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ExpandableDemo.kt
index 2d24e25..a676141 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ExpandableDemo.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ExpandableDemo.kt
@@ -26,6 +26,7 @@
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.mutableStateListOf
 import androidx.compose.runtime.remember
@@ -45,6 +46,7 @@
 import androidx.wear.compose.material.Chip
 import androidx.wear.compose.material.ChipDefaults
 import androidx.wear.compose.material.CompactChip
+import androidx.wear.compose.material.ListHeader
 import androidx.wear.compose.material.MaterialTheme
 import androidx.wear.compose.material.Text
 
@@ -117,6 +119,7 @@
 @Composable
 fun ExpandableText() {
     val state = rememberExpandableState()
+    val state2 = rememberExpandableState()
 
     ContainingScalingLazyColumn {
         expandableItem(state) { expanded ->
@@ -132,6 +135,22 @@
             )
         }
         expandButton(state, outline = false)
+
+        demoSeparator()
+        item {
+            ListHeader {
+                Text("Inline expandable.")
+            }
+        }
+        expandableItem(state2) { expanded ->
+            Row(verticalAlignment = CenterVertically) {
+                Text(if (expanded) "Expanded" else "Collapsed")
+                Spacer(Modifier.width(10.dp))
+                Button(onClick = { state2.expanded = !expanded }) {
+                    Text(if (expanded) "-" else "+")
+                }
+            }
+        }
     }
 }
 
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 5562839..f4dabe1 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
@@ -52,6 +52,7 @@
 import androidx.wear.compose.material.samples.ConfirmationWithAnimation
 import androidx.wear.compose.material.samples.CurvedTextDemo
 import androidx.wear.compose.material.samples.CurvedTextProviderDemo
+import androidx.wear.compose.material.samples.EdgeSwipeForSwipeToDismiss
 import androidx.wear.compose.material.samples.FixedFontSize
 import androidx.wear.compose.material.samples.HorizontalPageIndicatorSample
 import androidx.wear.compose.material.samples.IndeterminateCircularProgressIndicator
@@ -70,7 +71,9 @@
 import androidx.wear.compose.material.samples.SimpleScalingLazyColumn
 import androidx.wear.compose.material.samples.SimpleScalingLazyColumnWithContentPadding
 import androidx.wear.compose.material.samples.SimpleScalingLazyColumnWithSnap
+import androidx.wear.compose.material.samples.SimpleSwipeToDismissBox
 import androidx.wear.compose.material.samples.SplitToggleChipWithCheckbox
+import androidx.wear.compose.material.samples.StatefulSwipeToDismissBox
 import androidx.wear.compose.material.samples.StepperSample
 import androidx.wear.compose.material.samples.StepperWithCustomSemanticsSample
 import androidx.wear.compose.material.samples.StepperWithIntegerSample
@@ -557,6 +560,14 @@
             )
         ),
         DemoCategory(
+            title = "Swipe To Dismiss",
+            listOf(
+                ComposableDemo("Simple") { SimpleSwipeToDismissBox(it.navigateBack) },
+                ComposableDemo("Stateful") { StatefulSwipeToDismissBox() },
+                ComposableDemo("Edge swipe") { EdgeSwipeForSwipeToDismiss(it.navigateBack) },
+            )
+        ),
+        DemoCategory(
             "List (Scaling Lazy Column)",
             listOf(
                 ComposableDemo(
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ScrollAwayDemos.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ScrollAwayDemos.kt
index 8dd28b2..2109c6a 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ScrollAwayDemos.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/ScrollAwayDemos.kt
@@ -158,7 +158,7 @@
                 )
         ) {
             val modifier = Modifier.height(LocalConfiguration.current.screenHeightDp.dp / 2)
-            repeat(3) { i ->
+            repeat(10) { i ->
                 ExampleCard(modifier, i)
             }
         }
@@ -192,7 +192,7 @@
                 focusRequester = focusRequester
             )
         ) {
-            items(5) { i ->
+            items(10) { i ->
                 val modifier = Modifier.fillParentMaxHeight(0.5f)
                 ExampleCard(modifier = modifier, i = i)
             }
@@ -235,7 +235,7 @@
                 ListHeader { Text("Cards") }
             }
 
-            items(5) { i ->
+            items(10) { i ->
                 ExampleCard(Modifier.fillParentMaxHeight(0.5f), i)
             }
         }
@@ -276,7 +276,7 @@
                 ListHeader { Text("Chips") }
             }
 
-            items(5) { i ->
+            items(10) { i ->
                 ExampleChip(Modifier.fillMaxWidth(), i)
             }
         }
diff --git a/wear/protolayout/protolayout-expression/build.gradle b/wear/protolayout/protolayout-expression/build.gradle
index 85d4ee8..6421982 100644
--- a/wear/protolayout/protolayout-expression/build.gradle
+++ b/wear/protolayout/protolayout-expression/build.gradle
@@ -25,13 +25,10 @@
     annotationProcessor(libs.nullaway)
     api("androidx.annotation:annotation:1.2.0")
 
-    implementation("androidx.annotation:annotation-experimental:1.3.0")
+    implementation("androidx.annotation:annotation-experimental:1.3.1")
     implementation("androidx.collection:collection:1.2.0")
     implementation(project(path: ":wear:protolayout:protolayout-proto", configuration: "shadow"))
 
-    // Upgrade transitive kotlin-stdlib dependency from annotation-experimental.
-    implementation(libs.kotlinStdlib)
-
     testImplementation(libs.testExtJunit)
     testImplementation(libs.testExtTruth)
     testImplementation(libs.testRunner)
diff --git a/wear/protolayout/protolayout-expression/src/main/java/androidx/wear/protolayout/expression/PlatformHealthSources.java b/wear/protolayout/protolayout-expression/src/main/java/androidx/wear/protolayout/expression/PlatformHealthSources.java
index b5f203e..2df95fe 100644
--- a/wear/protolayout/protolayout-expression/src/main/java/androidx/wear/protolayout/expression/PlatformHealthSources.java
+++ b/wear/protolayout/protolayout-expression/src/main/java/androidx/wear/protolayout/expression/PlatformHealthSources.java
@@ -103,11 +103,11 @@
                 new PlatformDataKey<>("Daily Distance");
 
         /**
-         * The data source key for daily calories data from platform health sources. This is the
-         * total number of calories over a day (including both BMR and active calories) and it
-         * resets when 00:00 is reached (in whatever is the timezone set at that time). This can
-         * result in the DAILY period being greater than or less than 24 hours when the timezone of
-         * the device is changed.
+         * The data source key for daily calories (kcal) data from platform health sources. This is
+         * the total number of kilocalories over a day (including both BMR and active calories)
+         * and it resets when 00:00 is reached (in whatever is the timezone set at that time).
+         * This can result in the DAILY period being greater than or less than 24 hours when the
+         * timezone of the device is changed.
          */
         @NonNull
         @RequiresPermission(Manifest.permission.ACTIVITY_RECOGNITION)
diff --git a/wear/protolayout/protolayout-renderer/build.gradle b/wear/protolayout/protolayout-renderer/build.gradle
index 79330e35..a29d50f 100644
--- a/wear/protolayout/protolayout-renderer/build.gradle
+++ b/wear/protolayout/protolayout-renderer/build.gradle
@@ -33,7 +33,6 @@
     api(project(":wear:protolayout:protolayout-expression-pipeline"))
     implementation "androidx.concurrent:concurrent-futures:1.1.0"
     implementation("androidx.core:core:1.7.0")
-    implementation("androidx.vectordrawable:vectordrawable-seekable:1.0.0-beta01")
     implementation("androidx.wear:wear:1.3.0-rc01")
 
     testImplementation(libs.mockitoCore4)
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/common/ProtoLayoutDiffer.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/common/ProtoLayoutDiffer.java
index 906634e..65f4fa2 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/common/ProtoLayoutDiffer.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/common/ProtoLayoutDiffer.java
@@ -272,13 +272,24 @@
         return new LayoutDiff(changedNodes);
     }
 
-    /** Check whether 2 nodes represented by the given fingerprints are equivalent. */
+    /** Check whether two nodes represented by the given fingerprints are equivalent. */
     @RestrictTo(Scope.LIBRARY_GROUP)
     public static boolean areNodesEquivalent(
             @NonNull NodeFingerprint nodeA, @NonNull NodeFingerprint nodeB) {
         return getChangeType(nodeA, nodeB) == NodeChangeType.NO_CHANGE;
     }
 
+    /** Check whether two {@link TreeFingerprint} objects are equivalent. */
+    @RestrictTo(Scope.LIBRARY_GROUP)
+    public static boolean areSameFingerprints(
+            @NonNull TreeFingerprint first, @NonNull TreeFingerprint second) {
+        NodeFingerprint prev = first.getRoot();
+        NodeFingerprint current = second.getRoot();
+        return current.getSelfTypeValue() == prev.getSelfTypeValue()
+                && current.getSelfPropsValue() == prev.getSelfPropsValue()
+                && current.getChildNodesValue() == prev.getChildNodesValue();
+    }
+
     private static void addChangedNodes(
             @NonNull NodeFingerprint prevNodeFingerprint,
             @NonNull TreeNode node,
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/common/SeekableAnimatedVectorDrawable.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/common/SeekableAnimatedVectorDrawable.java
new file mode 100644
index 0000000..4680873
--- /dev/null
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/common/SeekableAnimatedVectorDrawable.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.protolayout.renderer.common;
+
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.core.content.res.TypedArrayUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * Placeholder SeekableAnimatedVectorDrawable class for temporarily replacing
+ * androidx.vectordrawable.graphics.drawable.SeekableAnimatedVectorDrawable
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class SeekableAnimatedVectorDrawable extends Drawable {
+    private static final String ANIMATED_VECTOR = "animated-vector";
+    static final int[] STYLEABLE_ANIMATED_VECTOR_DRAWABLE = {
+            android.R.attr.drawable
+    };
+    static final int STYLEABLE_ANIMATED_VECTOR_DRAWABLE_DRAWABLE = 0;
+
+    private final Drawable mDrawable;
+    private long mTotalDuration = 0;
+    private long mCurrentPlayTime = 0;
+
+    SeekableAnimatedVectorDrawable(@NonNull Drawable drawable) {
+       mDrawable = drawable;
+    }
+
+    @NonNull
+    public static SeekableAnimatedVectorDrawable createFromXmlInner(
+            @NonNull Resources res,
+            @NonNull XmlPullParser parser,
+            @NonNull AttributeSet attrs,
+            @Nullable Resources.Theme theme
+    ) throws XmlPullParserException, IOException {
+        int eventType = parser.getEventType();
+        final int innerDepth = parser.getDepth() + 1;
+
+        // Parse everything until the end of the animated-vector element.
+        while (eventType != XmlPullParser.END_DOCUMENT
+                && (parser.getDepth() >= innerDepth || eventType != XmlPullParser.END_TAG)) {
+            if (eventType == XmlPullParser.START_TAG) {
+                final String tagName = parser.getName();
+                 if (ANIMATED_VECTOR.equals(tagName)) {
+                    final TypedArray a =
+                            TypedArrayUtils.obtainAttributes(res, theme, attrs,
+                                    STYLEABLE_ANIMATED_VECTOR_DRAWABLE);
+
+                    int drawableRes = a.getResourceId(
+                            STYLEABLE_ANIMATED_VECTOR_DRAWABLE_DRAWABLE, 0);
+                    Drawable drawable = res.getDrawable(drawableRes, null);
+                    a.recycle();
+
+                    return new SeekableAnimatedVectorDrawable(drawable);
+
+                }
+            }
+            eventType = parser.next();
+        }
+
+        throw new XmlPullParserException("no animated-vector tag in the resource");
+    }
+
+    public long getTotalDuration() {
+        return mTotalDuration;
+    }
+
+    public void setCurrentPlayTime(long playTime) {
+        mCurrentPlayTime = playTime;
+    }
+
+    public long getCurrentPlayTime() {
+        return mCurrentPlayTime;
+    }
+
+    public void start() {}
+
+    @Override
+    public void draw(@NonNull Canvas canvas) {
+        mDrawable.draw(canvas);
+    }
+
+    @Override
+    public void setAlpha(int i) {
+        mDrawable.setAlpha(i);
+    }
+
+    @Override
+    public void setColorFilter(@Nullable ColorFilter colorFilter) {
+        mDrawable.setColorFilter(colorFilter);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    public int getOpacity() {
+        return mDrawable.getOpacity();
+    }
+}
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/NodeInfo.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/NodeInfo.java
index 8483ea6..84a7180 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/NodeInfo.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/NodeInfo.java
@@ -25,7 +25,7 @@
 import androidx.annotation.UiThread;
 import androidx.annotation.VisibleForTesting;
 import androidx.collection.ArraySet;
-import androidx.vectordrawable.graphics.drawable.SeekableAnimatedVectorDrawable;
+import androidx.wear.protolayout.renderer.common.SeekableAnimatedVectorDrawable;
 import androidx.wear.protolayout.expression.pipeline.BoundDynamicType;
 import androidx.wear.protolayout.expression.pipeline.DynamicTypeBindingRequest;
 import androidx.wear.protolayout.expression.pipeline.QuotaManager;
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipeline.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipeline.java
index 463af2e..7d83286 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipeline.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipeline.java
@@ -37,7 +37,7 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.collection.ArrayMap;
 import androidx.collection.ArraySet;
-import androidx.vectordrawable.graphics.drawable.SeekableAnimatedVectorDrawable;
+import androidx.wear.protolayout.renderer.common.SeekableAnimatedVectorDrawable;
 import androidx.wear.protolayout.expression.PlatformDataKey;
 import androidx.wear.protolayout.expression.pipeline.BoundDynamicType;
 import androidx.wear.protolayout.expression.pipeline.DynamicTypeBindingRequest;
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/impl/ProtoLayoutViewInstance.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/impl/ProtoLayoutViewInstance.java
index 5f53a02..1762d0b 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/impl/ProtoLayoutViewInstance.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/impl/ProtoLayoutViewInstance.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2021 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -39,12 +39,16 @@
 import androidx.wear.protolayout.expression.pipeline.FixedQuotaManagerImpl;
 import androidx.wear.protolayout.expression.pipeline.PlatformDataProvider;
 import androidx.wear.protolayout.expression.pipeline.StateStore;
+import androidx.wear.protolayout.proto.LayoutElementProto.ArcLayoutElement;
+import androidx.wear.protolayout.proto.LayoutElementProto.ArcLayoutElement.InnerCase;
 import androidx.wear.protolayout.proto.LayoutElementProto.Layout;
+import androidx.wear.protolayout.proto.LayoutElementProto.LayoutElement;
 import androidx.wear.protolayout.proto.ResourceProto;
 import androidx.wear.protolayout.proto.StateProto.State;
 import androidx.wear.protolayout.renderer.ProtoLayoutExtensionViewProvider;
 import androidx.wear.protolayout.renderer.ProtoLayoutTheme;
 import androidx.wear.protolayout.renderer.ProtoLayoutVisibilityState;
+import androidx.wear.protolayout.renderer.common.ProtoLayoutDiffer;
 import androidx.wear.protolayout.renderer.dynamicdata.ProtoLayoutDynamicDataPipeline;
 import androidx.wear.protolayout.renderer.inflater.ProtoLayoutInflater;
 import androidx.wear.protolayout.renderer.inflater.ProtoLayoutInflater.InflateResult;
@@ -55,12 +59,14 @@
 import androidx.wear.protolayout.renderer.inflater.ResourceResolvers;
 import androidx.wear.protolayout.renderer.inflater.StandardResourceResolvers;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.ListeningExecutorService;
 import com.google.common.util.concurrent.SettableFuture;
 
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CancellationException;
@@ -89,7 +95,7 @@
     }
 
     private static final int DEFAULT_MAX_CONCURRENT_RUNNING_ANIMATIONS = 4;
-
+    static final int MAX_LAYOUT_ELEMENT_DEPTH = 30;
     @NonNull private static final String TAG = "ProtoLayoutViewInstance";
 
     @NonNull private final Context mUiContext;
@@ -134,6 +140,12 @@
     @Nullable private Layout mPrevLayout = null;
 
     /**
+     * This field is used to avoid unnecessarily checking layout depth if the layout was previously
+     * failing the check.
+     */
+    private boolean mPrevLayoutAlreadyFailingDepthCheck = false;
+
+    /**
      * This is used as the Future for the currently running inflation session. The first time
      * "attach" is called, it should start the renderer. Subsequent attach calls should only ever
      * re-attach "inflateParent".
@@ -703,6 +715,21 @@
             return new FailedRenderResult();
         }
 
+        boolean sameFingerprint =
+                prevRenderedMetadata != null
+                        && ProtoLayoutDiffer.areSameFingerprints(
+                                prevRenderedMetadata.getTreeFingerprint(), layout.getFingerprint());
+
+        if (sameFingerprint) {
+            if (mPrevLayoutAlreadyFailingDepthCheck) {
+                throwExceptionForLayoutDepthCheckFailure();
+            }
+        } else {
+            checkLayoutDepth(layout.getRoot(), MAX_LAYOUT_ELEMENT_DEPTH);
+        }
+
+        mPrevLayoutAlreadyFailingDepthCheck = false;
+
         ProtoLayoutInflater.Config.Builder inflaterConfigBuilder =
                 new ProtoLayoutInflater.Config.Builder(mUiContext, layout, resolvers)
                         .setLoadActionExecutor(mUiExecutorService)
@@ -1059,6 +1086,52 @@
         }
     }
 
+    /** Returns true if the layout element depth doesn't exceed the given {@code allowedDepth}. */
+    private void checkLayoutDepth(LayoutElement layoutElement, int allowedDepth) {
+        if (allowedDepth <= 0) {
+            throwExceptionForLayoutDepthCheckFailure();
+        }
+        List<LayoutElement> children = ImmutableList.of();
+        switch (layoutElement.getInnerCase()) {
+            case COLUMN:
+                children = layoutElement.getColumn().getContentsList();
+                break;
+            case ROW:
+                children = layoutElement.getRow().getContentsList();
+                break;
+            case BOX:
+                children = layoutElement.getBox().getContentsList();
+                break;
+            case ARC:
+                List<ArcLayoutElement> arcElements = layoutElement.getArc().getContentsList();
+                if (!arcElements.isEmpty() && allowedDepth == 1) {
+                    throwExceptionForLayoutDepthCheckFailure();
+                }
+                for (ArcLayoutElement element : arcElements) {
+                    if (element.getInnerCase() == InnerCase.ADAPTER) {
+                        checkLayoutDepth(element.getAdapter().getContent(), allowedDepth - 1);
+                    }
+                }
+                break;
+            case SPANNABLE:
+                if (layoutElement.getSpannable().getSpansCount() > 0 && allowedDepth == 1) {
+                    throwExceptionForLayoutDepthCheckFailure();
+                }
+                break;
+            default:
+                // Other LayoutElements have depth of one.
+        }
+        for (LayoutElement child : children) {
+            checkLayoutDepth(child, allowedDepth - 1);
+        }
+    }
+
+    private void throwExceptionForLayoutDepthCheckFailure() {
+        mPrevLayoutAlreadyFailingDepthCheck = true;
+        throw new IllegalStateException(
+                "Layout depth exceeds maximum allowed depth: " + MAX_LAYOUT_ELEMENT_DEPTH);
+    }
+
     @Override
     public void close() throws Exception {
         detachInternal();
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/DefaultAndroidSeekableAnimatedImageResourceByResIdResolver.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/DefaultAndroidSeekableAnimatedImageResourceByResIdResolver.java
index eed00eb..489e8de 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/DefaultAndroidSeekableAnimatedImageResourceByResIdResolver.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/DefaultAndroidSeekableAnimatedImageResourceByResIdResolver.java
@@ -23,7 +23,7 @@
 import android.util.Xml;
 
 import androidx.annotation.NonNull;
-import androidx.vectordrawable.graphics.drawable.SeekableAnimatedVectorDrawable;
+import androidx.wear.protolayout.renderer.common.SeekableAnimatedVectorDrawable;
 import androidx.wear.protolayout.proto.ResourceProto.AndroidSeekableAnimatedImageResourceByResId;
 import androidx.wear.protolayout.proto.ResourceProto.AnimatedImageFormat;
 import androidx.wear.protolayout.renderer.inflater.ResourceResolvers.AndroidSeekableAnimatedImageResourceByResIdResolver;
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflater.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflater.java
index 97c1f9a..7aca4e1 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflater.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflater.java
@@ -86,7 +86,7 @@
 import androidx.core.view.AccessibilityDelegateCompat;
 import androidx.core.view.ViewCompat;
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
-import androidx.vectordrawable.graphics.drawable.SeekableAnimatedVectorDrawable;
+import androidx.wear.protolayout.renderer.common.SeekableAnimatedVectorDrawable;
 import androidx.wear.protolayout.expression.pipeline.AnimationsHelper;
 import androidx.wear.protolayout.expression.proto.AnimationParameterProto.AnimationSpec;
 import androidx.wear.protolayout.expression.proto.DynamicProto.DynamicFloat;
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/RenderedMetadata.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/RenderedMetadata.java
index 11cbf345..5b6bdc5 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/RenderedMetadata.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/RenderedMetadata.java
@@ -43,7 +43,7 @@
     }
 
     @NonNull
-    TreeFingerprint getTreeFingerprint() {
+    public TreeFingerprint getTreeFingerprint() {
         return mTreeFingerprint;
     }
 
diff --git a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/common/ProtoLayoutDifferTest.java b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/common/ProtoLayoutDifferTest.java
index b86f294..deb6dbc 100644
--- a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/common/ProtoLayoutDifferTest.java
+++ b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/common/ProtoLayoutDifferTest.java
@@ -303,6 +303,30 @@
     }
 
     @Test
+    public void areSameFingerprints() {
+        assertThat(
+                        ProtoLayoutDiffer.areSameFingerprints(
+                                referenceLayout().getFingerprint(),
+                                referenceLayout().getFingerprint()))
+                .isTrue();
+        assertThat(
+                        ProtoLayoutDiffer.areSameFingerprints(
+                                referenceLayout().getFingerprint(),
+                                layoutWithOneUpdatedNode().getFingerprint()))
+                .isFalse();
+        assertThat(
+                        ProtoLayoutDiffer.areSameFingerprints(
+                                referenceLayout().getFingerprint(),
+                                layoutWithDifferentNumberOfChildren().getFingerprint()))
+                .isFalse();
+        assertThat(
+                        ProtoLayoutDiffer.areSameFingerprints(
+                                referenceLayout().getFingerprint(),
+                                layoutWithUpdateToNodeSelfFingerprint().getFingerprint()))
+                .isFalse();
+    }
+
+    @Test
     public void isChildOf_forAnActualChild_returnsTrue() {
         String childPosId = "pT1.2.3";
         String parentPosId = "pT1.2";
diff --git a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipelineTest.java b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipelineTest.java
index f4a1cfa..f76bafa 100644
--- a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipelineTest.java
+++ b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/dynamicdata/ProtoLayoutDynamicDataPipelineTest.java
@@ -41,7 +41,7 @@
 import androidx.annotation.Nullable;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.vectordrawable.graphics.drawable.SeekableAnimatedVectorDrawable;
+import androidx.wear.protolayout.renderer.common.SeekableAnimatedVectorDrawable;
 import androidx.wear.protolayout.expression.AppDataKey;
 import androidx.wear.protolayout.expression.DynamicBuilders;
 import androidx.wear.protolayout.expression.pipeline.FixedQuotaManagerImpl;
@@ -94,6 +94,7 @@
 import com.google.common.truth.Expect;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -1105,6 +1106,7 @@
     }
 
     @Test
+    @Ignore("b/286028644")
     public void resolvedSeekableAnimatedImage_canStoreAndRegisterWithAnimatableFixedFloat() {
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
@@ -1137,6 +1139,7 @@
     }
 
     @Test
+    @Ignore("b/286028644")
     public void resolvedSeekableAnimatedImage_canStoreAndRegisterWithAnimatableDynamicFloat() {
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
@@ -1182,6 +1185,7 @@
     }
 
     @Test
+    @Ignore("b/286028644")
     public void resolvedSeekableAnimatedImage_getSeekableAnimationTotalDurationMillis() {
         ProtoLayoutDynamicDataPipeline pipeline =
                 new ProtoLayoutDynamicDataPipeline(
diff --git a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/helper/TestDsl.java b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/helper/TestDsl.java
index b158c38..9f7018ae 100644
--- a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/helper/TestDsl.java
+++ b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/helper/TestDsl.java
@@ -37,6 +37,7 @@
 import androidx.wear.protolayout.proto.FingerprintProto.TreeFingerprint;
 import androidx.wear.protolayout.proto.LayoutElementProto;
 import androidx.wear.protolayout.proto.LayoutElementProto.Arc;
+import androidx.wear.protolayout.proto.LayoutElementProto.ArcAdapter;
 import androidx.wear.protolayout.proto.LayoutElementProto.ArcLayoutElement;
 import androidx.wear.protolayout.proto.LayoutElementProto.ArcText;
 import androidx.wear.protolayout.proto.LayoutElementProto.Box;
@@ -433,6 +434,10 @@
         return arcInternal(/* propsConsumer= */ null, nodes);
     }
 
+    public static LayoutNode arcAdapter(LayoutNode layoutNode) {
+        return arcAdapterInternal(layoutNode);
+    }
+
     private static LayoutNode arcInternal(
             @Nullable Consumer<ArcProps> propsConsumer, LayoutNode... nodes) {
         LayoutNode element = new LayoutNode();
@@ -449,6 +454,15 @@
         return element;
     }
 
+    private static LayoutNode arcAdapterInternal(LayoutNode node) {
+        LayoutNode element = new LayoutNode();
+        ArcAdapter.Builder builder = ArcAdapter.newBuilder().setContent(node.mLayoutElement);
+        int selfPropsFingerprint = 0;
+        element.mArcLayoutElement = ArcLayoutElement.newBuilder().setAdapter(builder.build());
+        element.mFingerprint = fingerprint("arcAdapter", selfPropsFingerprint, node);
+        return element;
+    }
+
     public static LayoutNode arcText(String text) {
         LayoutNode element = new LayoutNode();
         element.mArcLayoutElement =
diff --git a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/impl/ProtoLayoutViewInstanceTest.java b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/impl/ProtoLayoutViewInstanceTest.java
index ca3f94e..3ad5656 100644
--- a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/impl/ProtoLayoutViewInstanceTest.java
+++ b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/impl/ProtoLayoutViewInstanceTest.java
@@ -17,10 +17,16 @@
 package androidx.wear.protolayout.renderer.impl;
 
 import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static androidx.wear.protolayout.renderer.helper.TestDsl.arc;
+import static androidx.wear.protolayout.renderer.helper.TestDsl.arcAdapter;
+import static androidx.wear.protolayout.renderer.helper.TestDsl.box;
 import static androidx.wear.protolayout.renderer.helper.TestDsl.column;
 import static androidx.wear.protolayout.renderer.helper.TestDsl.dynamicFixedText;
 import static androidx.wear.protolayout.renderer.helper.TestDsl.layout;
+import static androidx.wear.protolayout.renderer.helper.TestDsl.spanText;
+import static androidx.wear.protolayout.renderer.helper.TestDsl.spannable;
 import static androidx.wear.protolayout.renderer.helper.TestDsl.text;
+import static androidx.wear.protolayout.renderer.impl.ProtoLayoutViewInstance.MAX_LAYOUT_ELEMENT_DEPTH;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -39,6 +45,7 @@
 import androidx.wear.protolayout.expression.pipeline.StateStore;
 import androidx.wear.protolayout.proto.LayoutElementProto.Layout;
 import androidx.wear.protolayout.proto.ResourceProto.Resources;
+import androidx.wear.protolayout.renderer.helper.TestDsl.LayoutNode;
 import androidx.wear.protolayout.renderer.impl.ProtoLayoutViewInstance.Config;
 
 import com.google.common.collect.ImmutableList;
@@ -55,6 +62,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.AbstractExecutorService;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 
 @RunWith(AndroidJUnit4.class)
@@ -390,6 +398,107 @@
         assertThat(mRootContainer.getChildCount()).isEqualTo(0);
     }
 
+    @Test
+    public void layoutDepthExceedsMaximumDepth_renderingFail() throws Exception {
+        setupInstance(/* adaptiveUpdateRatesEnabled= */ false);
+        assertThrows(
+                ExecutionException.class,
+                () -> renderAndAttachLayout(layout(recursiveBox(MAX_LAYOUT_ELEMENT_DEPTH + 1))));
+    }
+
+    @Test
+    public void layoutDepthIsEqualToMaximumDepth_renderingPass() throws Exception {
+        setupInstance(/* adaptiveUpdateRatesEnabled= */ false);
+
+        LayoutNode[] children = new LayoutNode[MAX_LAYOUT_ELEMENT_DEPTH];
+        for (int i = 0; i < children.length; i++) {
+            children[i] = recursiveBox(MAX_LAYOUT_ELEMENT_DEPTH - 1);
+        }
+        ListenableFuture<Void> result =
+                mInstanceUnderTest.renderAndAttach(
+                        // MAX_LAYOUT_ELEMENT_DEPTH branches of depth MAX_LAYOUT_ELEMENT_DEPTH - 1.
+                        // Total depth is MAX_LAYOUT_ELEMENT_DEPTH (if we count the head).
+                        layout(box(children)), RESOURCES, mRootContainer);
+        shadowOf(Looper.getMainLooper()).idle();
+
+        assertNoException(result);
+        assertThat(mRootContainer.getChildCount()).isEqualTo(1);
+    }
+
+    @Test
+    public void layoutDepthForLayoutWithSpanner() throws Exception {
+        setupInstance(/* adaptiveUpdateRatesEnabled= */ false);
+
+        assertThrows(
+                ExecutionException.class,
+                () ->
+                        renderAndAttachLayout(
+                                // Total number of views is = MAX_LAYOUT_ELEMENT_DEPTH  + 1 (span
+                                // text)
+                                layout(
+                                        recursiveBox(
+                                                MAX_LAYOUT_ELEMENT_DEPTH,
+                                                spannable(spanText("Hello"))))));
+
+        ListenableFuture<Void> result =
+                mInstanceUnderTest.renderAndAttach(
+                        // Total number of views is = (MAX_LAYOUT_ELEMENT_DEPTH -1)  + 1 (span text)
+                        layout(
+                                recursiveBox(
+                                        MAX_LAYOUT_ELEMENT_DEPTH - 1,
+                                        spannable(spanText("Hello")))),
+                        RESOURCES,
+                        mRootContainer);
+        shadowOf(Looper.getMainLooper()).idle();
+
+        assertNoException(result);
+        assertThat(mRootContainer.getChildCount()).isEqualTo(1);
+    }
+
+    @Test
+    public void layoutDepthForLayoutWithArcAdapter() throws Exception {
+        setupInstance(/* adaptiveUpdateRatesEnabled= */ false);
+        assertThrows(
+                ExecutionException.class,
+                () ->
+                        renderAndAttachLayout(
+                                // Total number of views is = 1 (Arc) + (MAX_LAYOUT_ELEMENT_DEPTH)
+                                layout(arc(arcAdapter(recursiveBox(MAX_LAYOUT_ELEMENT_DEPTH))))));
+
+        ListenableFuture<Void> result =
+                mInstanceUnderTest.renderAndAttach(
+                        // Total number of views is = 1 (Arc) + (MAX_LAYOUT_ELEMENT_DEPTH - 1)
+                        // = MAX_LAYOUT_ELEMENT_DEPTH
+                        layout(arc(arcAdapter(recursiveBox(MAX_LAYOUT_ELEMENT_DEPTH - 1)))),
+                        RESOURCES,
+                        mRootContainer);
+        shadowOf(Looper.getMainLooper()).idle();
+
+        assertNoException(result);
+        assertThat(mRootContainer.getChildCount()).isEqualTo(1);
+    }
+
+    private void renderAndAttachLayout(Layout layout) throws Exception {
+        ListenableFuture<Void> result =
+                mInstanceUnderTest.renderAndAttach(layout, RESOURCES, mRootContainer);
+        shadowOf(Looper.getMainLooper()).idle();
+        assertNoException(result);
+    }
+
+    private static LayoutNode recursiveBox(int depth) {
+        if (depth == 1) {
+            return box();
+        }
+        return box(recursiveBox(depth - 1));
+    }
+
+    private static LayoutNode recursiveBox(int depth, LayoutNode leaf) {
+        if (depth == 1) {
+            return leaf;
+        }
+        return box(recursiveBox(depth - 1, leaf));
+    }
+
     private void setupInstance(boolean adaptiveUpdateRatesEnabled) {
         FakeExecutorService uiThreadExecutor =
                 new FakeExecutorService(new Handler(Looper.getMainLooper()));
diff --git a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflaterTest.java b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflaterTest.java
index 07d873b..1285360 100644
--- a/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflaterTest.java
+++ b/wear/protolayout/protolayout-renderer/src/test/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflaterTest.java
@@ -74,7 +74,7 @@
 import androidx.core.content.ContextCompat;
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.vectordrawable.graphics.drawable.SeekableAnimatedVectorDrawable;
+import androidx.wear.protolayout.renderer.common.SeekableAnimatedVectorDrawable;
 import androidx.wear.protolayout.expression.AppDataKey;
 import androidx.wear.protolayout.expression.DynamicBuilders;
 import androidx.wear.protolayout.expression.pipeline.FixedQuotaManagerImpl;
@@ -1661,6 +1661,7 @@
     }
 
     @Test
+    @Ignore("b/286028644")
     public void inflate_imageView_withSeekableAVDResource() {
         LayoutElement root =
                 LayoutElement.newBuilder()
diff --git a/wear/protolayout/protolayout/api/1.0.0-beta01.txt b/wear/protolayout/protolayout/api/1.0.0-beta01.txt
index 55ef432..21ffdf4 100644
--- a/wear/protolayout/protolayout/api/1.0.0-beta01.txt
+++ b/wear/protolayout/protolayout/api/1.0.0-beta01.txt
@@ -354,7 +354,6 @@
     method public androidx.wear.protolayout.DimensionBuilders.DegreesProp? getAnchorAngle();
     method public androidx.wear.protolayout.LayoutElementBuilders.ArcAnchorTypeProp? getAnchorType();
     method public java.util.List<androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement!> getContents();
-    method public androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint? getLayoutConstraintsForDynamicAnchorAngle();
     method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
     method public androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp? getVerticalAlign();
   }
@@ -366,7 +365,6 @@
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorAngle(androidx.wear.protolayout.DimensionBuilders.DegreesProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorType(androidx.wear.protolayout.LayoutElementBuilders.ArcAnchorTypeProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorType(int);
-    method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setLayoutConstraintsForDynamicAnchorAngle(androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setVerticalAlign(androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setVerticalAlign(int);
diff --git a/wear/protolayout/protolayout/api/current.ignore b/wear/protolayout/protolayout/api/current.ignore
index fc89635..2d69c6a 100644
--- a/wear/protolayout/protolayout/api/current.ignore
+++ b/wear/protolayout/protolayout/api/current.ignore
@@ -1,403 +1,5 @@
 // Baseline format: 1.0
-BecameUnchecked: androidx.wear.protolayout.DeviceParametersBuilders.Capabilities:
-    Removed class androidx.wear.protolayout.DeviceParametersBuilders.Capabilities from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.DeviceParametersBuilders.Capabilities#getMinimumFreshnessLimitMillis():
-    Removed method androidx.wear.protolayout.DeviceParametersBuilders.Capabilities.getMinimumFreshnessLimitMillis() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.DeviceParametersBuilders.Capabilities.Builder:
-    Removed class androidx.wear.protolayout.DeviceParametersBuilders.Capabilities.Builder from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.DeviceParametersBuilders.Capabilities.Builder#Builder():
-    Removed constructor androidx.wear.protolayout.DeviceParametersBuilders.Capabilities.Builder() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.DeviceParametersBuilders.Capabilities.Builder#build():
-    Removed method androidx.wear.protolayout.DeviceParametersBuilders.Capabilities.Builder.build() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.DeviceParametersBuilders.Capabilities.Builder#setMinimumFreshnessLimitMillis(long):
-    Removed method androidx.wear.protolayout.DeviceParametersBuilders.Capabilities.Builder.setMinimumFreshnessLimitMillis(long) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.DeviceParametersBuilders.Capabilities.Builder#setMinimumFreshnessLimitMillis(long) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.DeviceParametersBuilders.Capabilities.Builder.setMinimumFreshnessLimitMillis(long arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters#getCapabilities():
-    Removed method androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters.getCapabilities() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters.Builder#setCapabilities(androidx.wear.protolayout.DeviceParametersBuilders.Capabilities):
-    Removed method androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters.Builder.setCapabilities(androidx.wear.protolayout.DeviceParametersBuilders.Capabilities) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters.Builder#setCapabilities(androidx.wear.protolayout.DeviceParametersBuilders.Capabilities) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters.Builder.setCapabilities(androidx.wear.protolayout.DeviceParametersBuilders.Capabilities arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.DimensionBuilders.ExtensionDimension:
-    Removed class androidx.wear.protolayout.DimensionBuilders.ExtensionDimension from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders#FONT_WEIGHT_MEDIUM:
-    Removed field androidx.wear.protolayout.LayoutElementBuilders.FONT_WEIGHT_MEDIUM from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders#TEXT_OVERFLOW_MARQUEE:
-    Removed field androidx.wear.protolayout.LayoutElementBuilders.TEXT_OVERFLOW_MARQUEE from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle:
-    Removed class androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle#getExcludeFontPadding():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle.getExcludeFontPadding() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle.Builder:
-    Removed class androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle.Builder from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle.Builder#Builder():
-    Removed constructor androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle.Builder() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle.Builder#build():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle.Builder.build() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle.Builder#setExcludeFontPadding(boolean):
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle.Builder.setExcludeFontPadding(boolean) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle.Builder#setExcludeFontPadding(boolean) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle.Builder.setExcludeFontPadding(boolean arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement:
-    Removed class androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement#getExtensionId():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.getExtensionId() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement#getHeight():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.getHeight() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement#getPayload():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.getPayload() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement#getWidth():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.getWidth() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder:
-    Removed class androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder#Builder():
-    Removed constructor androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder#build():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder.build() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder#setExtensionId(String):
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder.setExtensionId(String) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder#setExtensionId(String) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder.setExtensionId(String arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder#setHeight(androidx.wear.protolayout.DimensionBuilders.ExtensionDimension):
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder.setHeight(androidx.wear.protolayout.DimensionBuilders.ExtensionDimension) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder#setHeight(androidx.wear.protolayout.DimensionBuilders.ExtensionDimension) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder.setHeight(androidx.wear.protolayout.DimensionBuilders.ExtensionDimension arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder#setPayload(byte[]):
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder.setPayload(byte[]) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder#setPayload(byte[]) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder.setPayload(byte[] arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder#setWidth(androidx.wear.protolayout.DimensionBuilders.ExtensionDimension):
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder.setWidth(androidx.wear.protolayout.DimensionBuilders.ExtensionDimension) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder#setWidth(androidx.wear.protolayout.DimensionBuilders.ExtensionDimension) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder.setWidth(androidx.wear.protolayout.DimensionBuilders.ExtensionDimension arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.FontStyle#getVariant():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.FontStyle.getVariant() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder#setVariant(androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp):
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder.setVariant(androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder#setVariant(androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder.setVariant(androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder#setVariant(int):
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder.setVariant(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder#setVariant(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder.setVariant(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp:
-    Removed class androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp#getValue():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp.getValue() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp.Builder:
-    Removed class androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp.Builder from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp.Builder#Builder():
-    Removed constructor androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp.Builder() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp.Builder#build():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp.Builder.build() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp.Builder#setValue(int):
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp.Builder.setValue(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp.Builder#setValue(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp.Builder.setValue(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.Layout#fromByteArray(byte[]):
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.Layout.fromByteArray(byte[]) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.Layout#fromByteArray(byte[]) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.LayoutElementBuilders.Layout.fromByteArray(byte[] arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.Layout#toByteArray():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.Layout.toByteArray() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.SpanText#getAndroidTextStyle():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.SpanText.getAndroidTextStyle() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.SpanText.Builder#setAndroidTextStyle(androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle):
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.SpanText.Builder.setAndroidTextStyle(androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.SpanText.Builder#setAndroidTextStyle(androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.LayoutElementBuilders.SpanText.Builder.setAndroidTextStyle(androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.Spannable#getMarqueeIterations():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.Spannable.getMarqueeIterations() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder#setMarqueeIterations(int):
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder.setMarqueeIterations(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder#setMarqueeIterations(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder.setMarqueeIterations(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.Text#getAndroidTextStyle():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.Text.getAndroidTextStyle() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.Text#getMarqueeIterations():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.Text.getMarqueeIterations() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.Text.Builder#setAndroidTextStyle(androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle):
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.Text.Builder.setAndroidTextStyle(androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.Text.Builder#setAndroidTextStyle(androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.LayoutElementBuilders.Text.Builder.setAndroidTextStyle(androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.Text.Builder#setMarqueeIterations(int):
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.Text.Builder.setMarqueeIterations(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.Text.Builder#setMarqueeIterations(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.LayoutElementBuilders.Text.Builder.setMarqueeIterations(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders#SLIDE_DIRECTION_BOTTOM_TO_TOP:
-    Removed field androidx.wear.protolayout.ModifiersBuilders.SLIDE_DIRECTION_BOTTOM_TO_TOP from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders#SLIDE_DIRECTION_LEFT_TO_RIGHT:
-    Removed field androidx.wear.protolayout.ModifiersBuilders.SLIDE_DIRECTION_LEFT_TO_RIGHT from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders#SLIDE_DIRECTION_RIGHT_TO_LEFT:
-    Removed field androidx.wear.protolayout.ModifiersBuilders.SLIDE_DIRECTION_RIGHT_TO_LEFT from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders#SLIDE_DIRECTION_TOP_TO_BOTTOM:
-    Removed field androidx.wear.protolayout.ModifiersBuilders.SLIDE_DIRECTION_TOP_TO_BOTTOM from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders#SLIDE_DIRECTION_UNDEFINED:
-    Removed field androidx.wear.protolayout.ModifiersBuilders.SLIDE_DIRECTION_UNDEFINED from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders#SLIDE_PARENT_SNAP_TO_INSIDE:
-    Removed field androidx.wear.protolayout.ModifiersBuilders.SLIDE_PARENT_SNAP_TO_INSIDE from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders#SLIDE_PARENT_SNAP_TO_OUTSIDE:
-    Removed field androidx.wear.protolayout.ModifiersBuilders.SLIDE_PARENT_SNAP_TO_OUTSIDE from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders#SLIDE_PARENT_SNAP_UNDEFINED:
-    Removed field androidx.wear.protolayout.ModifiersBuilders.SLIDE_PARENT_SNAP_UNDEFINED from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility#getEnterTransition():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.getEnterTransition() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility#getExitTransition():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.getExitTransition() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.Builder:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.Builder from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.Builder#Builder():
-    Removed constructor androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.Builder() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.Builder#build():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.Builder.build() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.Builder#setEnterTransition(androidx.wear.protolayout.ModifiersBuilders.EnterTransition):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.Builder.setEnterTransition(androidx.wear.protolayout.ModifiersBuilders.EnterTransition) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.Builder#setEnterTransition(androidx.wear.protolayout.ModifiersBuilders.EnterTransition) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.Builder.setEnterTransition(androidx.wear.protolayout.ModifiersBuilders.EnterTransition arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.Builder#setExitTransition(androidx.wear.protolayout.ModifiersBuilders.ExitTransition):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.Builder.setExitTransition(androidx.wear.protolayout.ModifiersBuilders.ExitTransition) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.Builder#setExitTransition(androidx.wear.protolayout.ModifiersBuilders.ExitTransition) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.Builder.setExitTransition(androidx.wear.protolayout.ModifiersBuilders.ExitTransition arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions#fadeIn():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions.fadeIn() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions#fadeInSlideIn(int):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions.fadeInSlideIn(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions#fadeInSlideIn(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions.fadeInSlideIn(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions#fadeOut():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions.fadeOut() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions#fadeOutSlideOut(int):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions.fadeOutSlideOut(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions#fadeOutSlideOut(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions.fadeOutSlideOut(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions#slideIn(int):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions.slideIn(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions#slideIn(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions.slideIn(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions#slideOut(int):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions.slideOut(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions#slideOut(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions.slideOut(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.EnterTransition:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.EnterTransition from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.EnterTransition#getFadeIn():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.EnterTransition.getFadeIn() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.EnterTransition#getSlideIn():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.EnterTransition.getSlideIn() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.EnterTransition.Builder:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.EnterTransition.Builder from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.EnterTransition.Builder#Builder():
-    Removed constructor androidx.wear.protolayout.ModifiersBuilders.EnterTransition.Builder() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.EnterTransition.Builder#build():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.EnterTransition.Builder.build() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.EnterTransition.Builder#setFadeIn(androidx.wear.protolayout.ModifiersBuilders.FadeInTransition):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.EnterTransition.Builder.setFadeIn(androidx.wear.protolayout.ModifiersBuilders.FadeInTransition) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.EnterTransition.Builder#setFadeIn(androidx.wear.protolayout.ModifiersBuilders.FadeInTransition) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.EnterTransition.Builder.setFadeIn(androidx.wear.protolayout.ModifiersBuilders.FadeInTransition arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.EnterTransition.Builder#setSlideIn(androidx.wear.protolayout.ModifiersBuilders.SlideInTransition):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.EnterTransition.Builder.setSlideIn(androidx.wear.protolayout.ModifiersBuilders.SlideInTransition) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.EnterTransition.Builder#setSlideIn(androidx.wear.protolayout.ModifiersBuilders.SlideInTransition) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.EnterTransition.Builder.setSlideIn(androidx.wear.protolayout.ModifiersBuilders.SlideInTransition arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.ExitTransition:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.ExitTransition from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.ExitTransition#getFadeOut():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.ExitTransition.getFadeOut() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.ExitTransition#getSlideOut():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.ExitTransition.getSlideOut() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.ExitTransition.Builder:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.ExitTransition.Builder from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.ExitTransition.Builder#Builder():
-    Removed constructor androidx.wear.protolayout.ModifiersBuilders.ExitTransition.Builder() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.ExitTransition.Builder#build():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.ExitTransition.Builder.build() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.ExitTransition.Builder#setFadeOut(androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.ExitTransition.Builder.setFadeOut(androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.ExitTransition.Builder#setFadeOut(androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.ExitTransition.Builder.setFadeOut(androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.ExitTransition.Builder#setSlideOut(androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.ExitTransition.Builder.setSlideOut(androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.ExitTransition.Builder#setSlideOut(androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.ExitTransition.Builder.setSlideOut(androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeInTransition:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.FadeInTransition from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeInTransition#getAnimationSpec():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.getAnimationSpec() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeInTransition#getInitialAlpha():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.getInitialAlpha() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.Builder:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.Builder from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.Builder#Builder():
-    Removed constructor androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.Builder() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.Builder#build():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.Builder.build() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.Builder#setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.Builder.setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.Builder#setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.Builder.setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.Builder#setInitialAlpha(float):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.Builder.setInitialAlpha(float) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.Builder#setInitialAlpha(float) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.Builder.setInitialAlpha(float arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition#getAnimationSpec():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.getAnimationSpec() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition#getTargetAlpha():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.getTargetAlpha() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.Builder:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.Builder from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.Builder#Builder():
-    Removed constructor androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.Builder() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.Builder#build():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.Builder.build() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.Builder#setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.Builder.setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.Builder#setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.Builder.setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.Builder#setTargetAlpha(float):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.Builder.setTargetAlpha(float) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.Builder#setTargetAlpha(float) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.Builder.setTargetAlpha(float arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.Modifiers#getContentUpdateAnimation():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.Modifiers.getContentUpdateAnimation() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.Modifiers.Builder#setContentUpdateAnimation(androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.Modifiers.Builder.setContentUpdateAnimation(androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.Modifiers.Builder#setContentUpdateAnimation(androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.Modifiers.Builder.setContentUpdateAnimation(androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideBound:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.SlideBound from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideInTransition:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.SlideInTransition from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideInTransition#getAnimationSpec():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.getAnimationSpec() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideInTransition#getDirection():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.getDirection() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideInTransition#getInitialSlideBound():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.getInitialSlideBound() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder#Builder():
-    Removed constructor androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder#build():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder.build() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder#setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder.setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder#setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder.setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder#setDirection(int):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder.setDirection(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder#setDirection(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder.setDirection(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder#setInitialSlideBound(androidx.wear.protolayout.ModifiersBuilders.SlideBound):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder.setInitialSlideBound(androidx.wear.protolayout.ModifiersBuilders.SlideBound) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder#setInitialSlideBound(androidx.wear.protolayout.ModifiersBuilders.SlideBound) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder.setInitialSlideBound(androidx.wear.protolayout.ModifiersBuilders.SlideBound arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition#getAnimationSpec():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.getAnimationSpec() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition#getDirection():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.getDirection() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition#getTargetSlideBound():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.getTargetSlideBound() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder#Builder():
-    Removed constructor androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder#build():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder.build() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder#setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder.setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder#setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder.setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder#setDirection(int):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder.setDirection(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder#setDirection(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder.setDirection(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder#setTargetSlideBound(androidx.wear.protolayout.ModifiersBuilders.SlideBound):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder.setTargetSlideBound(androidx.wear.protolayout.ModifiersBuilders.SlideBound) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder#setTargetSlideBound(androidx.wear.protolayout.ModifiersBuilders.SlideBound) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder.setTargetSlideBound(androidx.wear.protolayout.ModifiersBuilders.SlideBound arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideParentBound:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.SlideParentBound from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideParentBound#getSnapTo():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideParentBound.getSnapTo() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideParentBound.Builder:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.SlideParentBound.Builder from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideParentBound.Builder#Builder():
-    Removed constructor androidx.wear.protolayout.ModifiersBuilders.SlideParentBound.Builder() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideParentBound.Builder#build():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideParentBound.Builder.build() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideParentBound.Builder#setSnapTo(int):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideParentBound.Builder.setSnapTo(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideParentBound.Builder#setSnapTo(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.SlideParentBound.Builder.setSnapTo(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId:
-    Removed class androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId#getAnimatedImageFormat():
-    Removed method androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.getAnimatedImageFormat() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId#getResourceId():
-    Removed method androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.getResourceId() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId#getStartTrigger():
-    Removed method androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.getStartTrigger() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder:
-    Removed class androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder#Builder():
-    Removed constructor androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder#build():
-    Removed method androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder.build() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder#setAnimatedImageFormat(int):
-    Removed method androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder.setAnimatedImageFormat(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder#setAnimatedImageFormat(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder.setAnimatedImageFormat(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder#setResourceId(int):
-    Removed method androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder.setResourceId(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder#setResourceId(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder.setResourceId(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder#setStartTrigger(androidx.wear.protolayout.TriggerBuilders.Trigger):
-    Removed method androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder.setStartTrigger(androidx.wear.protolayout.TriggerBuilders.Trigger) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder#setStartTrigger(androidx.wear.protolayout.TriggerBuilders.Trigger) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder.setStartTrigger(androidx.wear.protolayout.TriggerBuilders.Trigger arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId:
-    Removed class androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId#getAnimatedImageFormat():
-    Removed method androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.getAnimatedImageFormat() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId#getProgress():
-    Removed method androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.getProgress() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId#getResourceId():
-    Removed method androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.getResourceId() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder:
-    Removed class androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder#Builder():
-    Removed constructor androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder#build():
-    Removed method androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder.build() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder#setAnimatedImageFormat(int):
-    Removed method androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder.setAnimatedImageFormat(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder#setAnimatedImageFormat(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder.setAnimatedImageFormat(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder#setProgress(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat):
-    Removed method androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder.setProgress(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder#setProgress(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder.setProgress(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder#setResourceId(int):
-    Removed method androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder.setResourceId(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder#setResourceId(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder.setResourceId(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.ImageResource#getAndroidAnimatedResourceByResId():
-    Removed method androidx.wear.protolayout.ResourceBuilders.ImageResource.getAndroidAnimatedResourceByResId() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.ImageResource#getAndroidSeekableAnimatedResourceByResId():
-    Removed method androidx.wear.protolayout.ResourceBuilders.ImageResource.getAndroidSeekableAnimatedResourceByResId() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder#setAndroidAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId):
-    Removed method androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder.setAndroidAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder#setAndroidAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder.setAndroidAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder#setAndroidSeekableAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId):
-    Removed method androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder.setAndroidSeekableAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder#setAndroidSeekableAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder.setAndroidSeekableAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId arg1) from compatibility checked API surface
+RemovedMethod: androidx.wear.protolayout.LayoutElementBuilders.Arc#getLayoutConstraintsForDynamicAnchorAngle():
+    Removed method androidx.wear.protolayout.LayoutElementBuilders.Arc.getLayoutConstraintsForDynamicAnchorAngle()
+RemovedMethod: androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder#setLayoutConstraintsForDynamicAnchorAngle(androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint):
+    Removed method androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder.setLayoutConstraintsForDynamicAnchorAngle(androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint)
diff --git a/wear/protolayout/protolayout/api/current.txt b/wear/protolayout/protolayout/api/current.txt
index 55ef432..21ffdf4 100644
--- a/wear/protolayout/protolayout/api/current.txt
+++ b/wear/protolayout/protolayout/api/current.txt
@@ -354,7 +354,6 @@
     method public androidx.wear.protolayout.DimensionBuilders.DegreesProp? getAnchorAngle();
     method public androidx.wear.protolayout.LayoutElementBuilders.ArcAnchorTypeProp? getAnchorType();
     method public java.util.List<androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement!> getContents();
-    method public androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint? getLayoutConstraintsForDynamicAnchorAngle();
     method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
     method public androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp? getVerticalAlign();
   }
@@ -366,7 +365,6 @@
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorAngle(androidx.wear.protolayout.DimensionBuilders.DegreesProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorType(androidx.wear.protolayout.LayoutElementBuilders.ArcAnchorTypeProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorType(int);
-    method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setLayoutConstraintsForDynamicAnchorAngle(androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setVerticalAlign(androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setVerticalAlign(int);
diff --git a/wear/protolayout/protolayout/api/restricted_1.0.0-beta01.txt b/wear/protolayout/protolayout/api/restricted_1.0.0-beta01.txt
index 55ef432..21ffdf4 100644
--- a/wear/protolayout/protolayout/api/restricted_1.0.0-beta01.txt
+++ b/wear/protolayout/protolayout/api/restricted_1.0.0-beta01.txt
@@ -354,7 +354,6 @@
     method public androidx.wear.protolayout.DimensionBuilders.DegreesProp? getAnchorAngle();
     method public androidx.wear.protolayout.LayoutElementBuilders.ArcAnchorTypeProp? getAnchorType();
     method public java.util.List<androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement!> getContents();
-    method public androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint? getLayoutConstraintsForDynamicAnchorAngle();
     method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
     method public androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp? getVerticalAlign();
   }
@@ -366,7 +365,6 @@
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorAngle(androidx.wear.protolayout.DimensionBuilders.DegreesProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorType(androidx.wear.protolayout.LayoutElementBuilders.ArcAnchorTypeProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorType(int);
-    method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setLayoutConstraintsForDynamicAnchorAngle(androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setVerticalAlign(androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setVerticalAlign(int);
diff --git a/wear/protolayout/protolayout/api/restricted_current.ignore b/wear/protolayout/protolayout/api/restricted_current.ignore
index fc89635..2d69c6a 100644
--- a/wear/protolayout/protolayout/api/restricted_current.ignore
+++ b/wear/protolayout/protolayout/api/restricted_current.ignore
@@ -1,403 +1,5 @@
 // Baseline format: 1.0
-BecameUnchecked: androidx.wear.protolayout.DeviceParametersBuilders.Capabilities:
-    Removed class androidx.wear.protolayout.DeviceParametersBuilders.Capabilities from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.DeviceParametersBuilders.Capabilities#getMinimumFreshnessLimitMillis():
-    Removed method androidx.wear.protolayout.DeviceParametersBuilders.Capabilities.getMinimumFreshnessLimitMillis() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.DeviceParametersBuilders.Capabilities.Builder:
-    Removed class androidx.wear.protolayout.DeviceParametersBuilders.Capabilities.Builder from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.DeviceParametersBuilders.Capabilities.Builder#Builder():
-    Removed constructor androidx.wear.protolayout.DeviceParametersBuilders.Capabilities.Builder() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.DeviceParametersBuilders.Capabilities.Builder#build():
-    Removed method androidx.wear.protolayout.DeviceParametersBuilders.Capabilities.Builder.build() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.DeviceParametersBuilders.Capabilities.Builder#setMinimumFreshnessLimitMillis(long):
-    Removed method androidx.wear.protolayout.DeviceParametersBuilders.Capabilities.Builder.setMinimumFreshnessLimitMillis(long) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.DeviceParametersBuilders.Capabilities.Builder#setMinimumFreshnessLimitMillis(long) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.DeviceParametersBuilders.Capabilities.Builder.setMinimumFreshnessLimitMillis(long arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters#getCapabilities():
-    Removed method androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters.getCapabilities() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters.Builder#setCapabilities(androidx.wear.protolayout.DeviceParametersBuilders.Capabilities):
-    Removed method androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters.Builder.setCapabilities(androidx.wear.protolayout.DeviceParametersBuilders.Capabilities) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters.Builder#setCapabilities(androidx.wear.protolayout.DeviceParametersBuilders.Capabilities) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.DeviceParametersBuilders.DeviceParameters.Builder.setCapabilities(androidx.wear.protolayout.DeviceParametersBuilders.Capabilities arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.DimensionBuilders.ExtensionDimension:
-    Removed class androidx.wear.protolayout.DimensionBuilders.ExtensionDimension from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders#FONT_WEIGHT_MEDIUM:
-    Removed field androidx.wear.protolayout.LayoutElementBuilders.FONT_WEIGHT_MEDIUM from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders#TEXT_OVERFLOW_MARQUEE:
-    Removed field androidx.wear.protolayout.LayoutElementBuilders.TEXT_OVERFLOW_MARQUEE from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle:
-    Removed class androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle#getExcludeFontPadding():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle.getExcludeFontPadding() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle.Builder:
-    Removed class androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle.Builder from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle.Builder#Builder():
-    Removed constructor androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle.Builder() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle.Builder#build():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle.Builder.build() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle.Builder#setExcludeFontPadding(boolean):
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle.Builder.setExcludeFontPadding(boolean) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle.Builder#setExcludeFontPadding(boolean) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle.Builder.setExcludeFontPadding(boolean arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement:
-    Removed class androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement#getExtensionId():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.getExtensionId() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement#getHeight():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.getHeight() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement#getPayload():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.getPayload() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement#getWidth():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.getWidth() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder:
-    Removed class androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder#Builder():
-    Removed constructor androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder#build():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder.build() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder#setExtensionId(String):
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder.setExtensionId(String) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder#setExtensionId(String) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder.setExtensionId(String arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder#setHeight(androidx.wear.protolayout.DimensionBuilders.ExtensionDimension):
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder.setHeight(androidx.wear.protolayout.DimensionBuilders.ExtensionDimension) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder#setHeight(androidx.wear.protolayout.DimensionBuilders.ExtensionDimension) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder.setHeight(androidx.wear.protolayout.DimensionBuilders.ExtensionDimension arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder#setPayload(byte[]):
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder.setPayload(byte[]) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder#setPayload(byte[]) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder.setPayload(byte[] arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder#setWidth(androidx.wear.protolayout.DimensionBuilders.ExtensionDimension):
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder.setWidth(androidx.wear.protolayout.DimensionBuilders.ExtensionDimension) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder#setWidth(androidx.wear.protolayout.DimensionBuilders.ExtensionDimension) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.LayoutElementBuilders.ExtensionLayoutElement.Builder.setWidth(androidx.wear.protolayout.DimensionBuilders.ExtensionDimension arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.FontStyle#getVariant():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.FontStyle.getVariant() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder#setVariant(androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp):
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder.setVariant(androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder#setVariant(androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder.setVariant(androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder#setVariant(int):
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder.setVariant(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder#setVariant(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.LayoutElementBuilders.FontStyle.Builder.setVariant(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp:
-    Removed class androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp#getValue():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp.getValue() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp.Builder:
-    Removed class androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp.Builder from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp.Builder#Builder():
-    Removed constructor androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp.Builder() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp.Builder#build():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp.Builder.build() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp.Builder#setValue(int):
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp.Builder.setValue(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp.Builder#setValue(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.LayoutElementBuilders.FontVariantProp.Builder.setValue(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.Layout#fromByteArray(byte[]):
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.Layout.fromByteArray(byte[]) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.Layout#fromByteArray(byte[]) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.LayoutElementBuilders.Layout.fromByteArray(byte[] arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.Layout#toByteArray():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.Layout.toByteArray() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.SpanText#getAndroidTextStyle():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.SpanText.getAndroidTextStyle() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.SpanText.Builder#setAndroidTextStyle(androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle):
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.SpanText.Builder.setAndroidTextStyle(androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.SpanText.Builder#setAndroidTextStyle(androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.LayoutElementBuilders.SpanText.Builder.setAndroidTextStyle(androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.Spannable#getMarqueeIterations():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.Spannable.getMarqueeIterations() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder#setMarqueeIterations(int):
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder.setMarqueeIterations(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder#setMarqueeIterations(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.LayoutElementBuilders.Spannable.Builder.setMarqueeIterations(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.Text#getAndroidTextStyle():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.Text.getAndroidTextStyle() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.Text#getMarqueeIterations():
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.Text.getMarqueeIterations() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.Text.Builder#setAndroidTextStyle(androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle):
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.Text.Builder.setAndroidTextStyle(androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.Text.Builder#setAndroidTextStyle(androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.LayoutElementBuilders.Text.Builder.setAndroidTextStyle(androidx.wear.protolayout.LayoutElementBuilders.AndroidTextStyle arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.Text.Builder#setMarqueeIterations(int):
-    Removed method androidx.wear.protolayout.LayoutElementBuilders.Text.Builder.setMarqueeIterations(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.LayoutElementBuilders.Text.Builder#setMarqueeIterations(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.LayoutElementBuilders.Text.Builder.setMarqueeIterations(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders#SLIDE_DIRECTION_BOTTOM_TO_TOP:
-    Removed field androidx.wear.protolayout.ModifiersBuilders.SLIDE_DIRECTION_BOTTOM_TO_TOP from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders#SLIDE_DIRECTION_LEFT_TO_RIGHT:
-    Removed field androidx.wear.protolayout.ModifiersBuilders.SLIDE_DIRECTION_LEFT_TO_RIGHT from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders#SLIDE_DIRECTION_RIGHT_TO_LEFT:
-    Removed field androidx.wear.protolayout.ModifiersBuilders.SLIDE_DIRECTION_RIGHT_TO_LEFT from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders#SLIDE_DIRECTION_TOP_TO_BOTTOM:
-    Removed field androidx.wear.protolayout.ModifiersBuilders.SLIDE_DIRECTION_TOP_TO_BOTTOM from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders#SLIDE_DIRECTION_UNDEFINED:
-    Removed field androidx.wear.protolayout.ModifiersBuilders.SLIDE_DIRECTION_UNDEFINED from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders#SLIDE_PARENT_SNAP_TO_INSIDE:
-    Removed field androidx.wear.protolayout.ModifiersBuilders.SLIDE_PARENT_SNAP_TO_INSIDE from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders#SLIDE_PARENT_SNAP_TO_OUTSIDE:
-    Removed field androidx.wear.protolayout.ModifiersBuilders.SLIDE_PARENT_SNAP_TO_OUTSIDE from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders#SLIDE_PARENT_SNAP_UNDEFINED:
-    Removed field androidx.wear.protolayout.ModifiersBuilders.SLIDE_PARENT_SNAP_UNDEFINED from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility#getEnterTransition():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.getEnterTransition() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility#getExitTransition():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.getExitTransition() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.Builder:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.Builder from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.Builder#Builder():
-    Removed constructor androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.Builder() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.Builder#build():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.Builder.build() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.Builder#setEnterTransition(androidx.wear.protolayout.ModifiersBuilders.EnterTransition):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.Builder.setEnterTransition(androidx.wear.protolayout.ModifiersBuilders.EnterTransition) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.Builder#setEnterTransition(androidx.wear.protolayout.ModifiersBuilders.EnterTransition) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.Builder.setEnterTransition(androidx.wear.protolayout.ModifiersBuilders.EnterTransition arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.Builder#setExitTransition(androidx.wear.protolayout.ModifiersBuilders.ExitTransition):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.Builder.setExitTransition(androidx.wear.protolayout.ModifiersBuilders.ExitTransition) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.Builder#setExitTransition(androidx.wear.protolayout.ModifiersBuilders.ExitTransition) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility.Builder.setExitTransition(androidx.wear.protolayout.ModifiersBuilders.ExitTransition arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions#fadeIn():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions.fadeIn() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions#fadeInSlideIn(int):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions.fadeInSlideIn(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions#fadeInSlideIn(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions.fadeInSlideIn(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions#fadeOut():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions.fadeOut() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions#fadeOutSlideOut(int):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions.fadeOutSlideOut(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions#fadeOutSlideOut(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions.fadeOutSlideOut(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions#slideIn(int):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions.slideIn(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions#slideIn(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions.slideIn(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions#slideOut(int):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions.slideOut(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions#slideOut(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.DefaultContentTransitions.slideOut(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.EnterTransition:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.EnterTransition from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.EnterTransition#getFadeIn():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.EnterTransition.getFadeIn() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.EnterTransition#getSlideIn():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.EnterTransition.getSlideIn() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.EnterTransition.Builder:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.EnterTransition.Builder from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.EnterTransition.Builder#Builder():
-    Removed constructor androidx.wear.protolayout.ModifiersBuilders.EnterTransition.Builder() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.EnterTransition.Builder#build():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.EnterTransition.Builder.build() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.EnterTransition.Builder#setFadeIn(androidx.wear.protolayout.ModifiersBuilders.FadeInTransition):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.EnterTransition.Builder.setFadeIn(androidx.wear.protolayout.ModifiersBuilders.FadeInTransition) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.EnterTransition.Builder#setFadeIn(androidx.wear.protolayout.ModifiersBuilders.FadeInTransition) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.EnterTransition.Builder.setFadeIn(androidx.wear.protolayout.ModifiersBuilders.FadeInTransition arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.EnterTransition.Builder#setSlideIn(androidx.wear.protolayout.ModifiersBuilders.SlideInTransition):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.EnterTransition.Builder.setSlideIn(androidx.wear.protolayout.ModifiersBuilders.SlideInTransition) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.EnterTransition.Builder#setSlideIn(androidx.wear.protolayout.ModifiersBuilders.SlideInTransition) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.EnterTransition.Builder.setSlideIn(androidx.wear.protolayout.ModifiersBuilders.SlideInTransition arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.ExitTransition:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.ExitTransition from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.ExitTransition#getFadeOut():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.ExitTransition.getFadeOut() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.ExitTransition#getSlideOut():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.ExitTransition.getSlideOut() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.ExitTransition.Builder:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.ExitTransition.Builder from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.ExitTransition.Builder#Builder():
-    Removed constructor androidx.wear.protolayout.ModifiersBuilders.ExitTransition.Builder() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.ExitTransition.Builder#build():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.ExitTransition.Builder.build() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.ExitTransition.Builder#setFadeOut(androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.ExitTransition.Builder.setFadeOut(androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.ExitTransition.Builder#setFadeOut(androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.ExitTransition.Builder.setFadeOut(androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.ExitTransition.Builder#setSlideOut(androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.ExitTransition.Builder.setSlideOut(androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.ExitTransition.Builder#setSlideOut(androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.ExitTransition.Builder.setSlideOut(androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeInTransition:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.FadeInTransition from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeInTransition#getAnimationSpec():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.getAnimationSpec() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeInTransition#getInitialAlpha():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.getInitialAlpha() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.Builder:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.Builder from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.Builder#Builder():
-    Removed constructor androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.Builder() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.Builder#build():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.Builder.build() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.Builder#setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.Builder.setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.Builder#setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.Builder.setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.Builder#setInitialAlpha(float):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.Builder.setInitialAlpha(float) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.Builder#setInitialAlpha(float) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.FadeInTransition.Builder.setInitialAlpha(float arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition#getAnimationSpec():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.getAnimationSpec() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition#getTargetAlpha():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.getTargetAlpha() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.Builder:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.Builder from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.Builder#Builder():
-    Removed constructor androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.Builder() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.Builder#build():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.Builder.build() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.Builder#setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.Builder.setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.Builder#setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.Builder.setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.Builder#setTargetAlpha(float):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.Builder.setTargetAlpha(float) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.Builder#setTargetAlpha(float) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.FadeOutTransition.Builder.setTargetAlpha(float arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.Modifiers#getContentUpdateAnimation():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.Modifiers.getContentUpdateAnimation() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.Modifiers.Builder#setContentUpdateAnimation(androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.Modifiers.Builder.setContentUpdateAnimation(androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.Modifiers.Builder#setContentUpdateAnimation(androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.Modifiers.Builder.setContentUpdateAnimation(androidx.wear.protolayout.ModifiersBuilders.AnimatedVisibility arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideBound:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.SlideBound from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideInTransition:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.SlideInTransition from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideInTransition#getAnimationSpec():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.getAnimationSpec() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideInTransition#getDirection():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.getDirection() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideInTransition#getInitialSlideBound():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.getInitialSlideBound() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder#Builder():
-    Removed constructor androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder#build():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder.build() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder#setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder.setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder#setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder.setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder#setDirection(int):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder.setDirection(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder#setDirection(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder.setDirection(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder#setInitialSlideBound(androidx.wear.protolayout.ModifiersBuilders.SlideBound):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder.setInitialSlideBound(androidx.wear.protolayout.ModifiersBuilders.SlideBound) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder#setInitialSlideBound(androidx.wear.protolayout.ModifiersBuilders.SlideBound) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.SlideInTransition.Builder.setInitialSlideBound(androidx.wear.protolayout.ModifiersBuilders.SlideBound arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition#getAnimationSpec():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.getAnimationSpec() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition#getDirection():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.getDirection() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition#getTargetSlideBound():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.getTargetSlideBound() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder#Builder():
-    Removed constructor androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder#build():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder.build() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder#setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder.setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder#setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder.setAnimationSpec(androidx.wear.protolayout.expression.AnimationParameterBuilders.AnimationSpec arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder#setDirection(int):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder.setDirection(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder#setDirection(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder.setDirection(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder#setTargetSlideBound(androidx.wear.protolayout.ModifiersBuilders.SlideBound):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder.setTargetSlideBound(androidx.wear.protolayout.ModifiersBuilders.SlideBound) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder#setTargetSlideBound(androidx.wear.protolayout.ModifiersBuilders.SlideBound) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.SlideOutTransition.Builder.setTargetSlideBound(androidx.wear.protolayout.ModifiersBuilders.SlideBound arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideParentBound:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.SlideParentBound from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideParentBound#getSnapTo():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideParentBound.getSnapTo() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideParentBound.Builder:
-    Removed class androidx.wear.protolayout.ModifiersBuilders.SlideParentBound.Builder from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideParentBound.Builder#Builder():
-    Removed constructor androidx.wear.protolayout.ModifiersBuilders.SlideParentBound.Builder() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideParentBound.Builder#build():
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideParentBound.Builder.build() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideParentBound.Builder#setSnapTo(int):
-    Removed method androidx.wear.protolayout.ModifiersBuilders.SlideParentBound.Builder.setSnapTo(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ModifiersBuilders.SlideParentBound.Builder#setSnapTo(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ModifiersBuilders.SlideParentBound.Builder.setSnapTo(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId:
-    Removed class androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId#getAnimatedImageFormat():
-    Removed method androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.getAnimatedImageFormat() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId#getResourceId():
-    Removed method androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.getResourceId() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId#getStartTrigger():
-    Removed method androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.getStartTrigger() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder:
-    Removed class androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder#Builder():
-    Removed constructor androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder#build():
-    Removed method androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder.build() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder#setAnimatedImageFormat(int):
-    Removed method androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder.setAnimatedImageFormat(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder#setAnimatedImageFormat(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder.setAnimatedImageFormat(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder#setResourceId(int):
-    Removed method androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder.setResourceId(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder#setResourceId(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder.setResourceId(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder#setStartTrigger(androidx.wear.protolayout.TriggerBuilders.Trigger):
-    Removed method androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder.setStartTrigger(androidx.wear.protolayout.TriggerBuilders.Trigger) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder#setStartTrigger(androidx.wear.protolayout.TriggerBuilders.Trigger) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId.Builder.setStartTrigger(androidx.wear.protolayout.TriggerBuilders.Trigger arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId:
-    Removed class androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId#getAnimatedImageFormat():
-    Removed method androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.getAnimatedImageFormat() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId#getProgress():
-    Removed method androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.getProgress() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId#getResourceId():
-    Removed method androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.getResourceId() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder:
-    Removed class androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder#Builder():
-    Removed constructor androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder#build():
-    Removed method androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder.build() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder#setAnimatedImageFormat(int):
-    Removed method androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder.setAnimatedImageFormat(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder#setAnimatedImageFormat(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder.setAnimatedImageFormat(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder#setProgress(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat):
-    Removed method androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder.setProgress(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder#setProgress(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder.setProgress(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder#setResourceId(int):
-    Removed method androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder.setResourceId(int) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder#setResourceId(int) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId.Builder.setResourceId(int arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.ImageResource#getAndroidAnimatedResourceByResId():
-    Removed method androidx.wear.protolayout.ResourceBuilders.ImageResource.getAndroidAnimatedResourceByResId() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.ImageResource#getAndroidSeekableAnimatedResourceByResId():
-    Removed method androidx.wear.protolayout.ResourceBuilders.ImageResource.getAndroidSeekableAnimatedResourceByResId() from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder#setAndroidAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId):
-    Removed method androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder.setAndroidAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder#setAndroidAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder.setAndroidAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidAnimatedImageResourceByResId arg1) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder#setAndroidSeekableAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId):
-    Removed method androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder.setAndroidSeekableAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId) from compatibility checked API surface
-BecameUnchecked: androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder#setAndroidSeekableAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId) parameter #0:
-    Removed parameter arg1 in androidx.wear.protolayout.ResourceBuilders.ImageResource.Builder.setAndroidSeekableAnimatedResourceByResId(androidx.wear.protolayout.ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId arg1) from compatibility checked API surface
+RemovedMethod: androidx.wear.protolayout.LayoutElementBuilders.Arc#getLayoutConstraintsForDynamicAnchorAngle():
+    Removed method androidx.wear.protolayout.LayoutElementBuilders.Arc.getLayoutConstraintsForDynamicAnchorAngle()
+RemovedMethod: androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder#setLayoutConstraintsForDynamicAnchorAngle(androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint):
+    Removed method androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder.setLayoutConstraintsForDynamicAnchorAngle(androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint)
diff --git a/wear/protolayout/protolayout/api/restricted_current.txt b/wear/protolayout/protolayout/api/restricted_current.txt
index 55ef432..21ffdf4 100644
--- a/wear/protolayout/protolayout/api/restricted_current.txt
+++ b/wear/protolayout/protolayout/api/restricted_current.txt
@@ -354,7 +354,6 @@
     method public androidx.wear.protolayout.DimensionBuilders.DegreesProp? getAnchorAngle();
     method public androidx.wear.protolayout.LayoutElementBuilders.ArcAnchorTypeProp? getAnchorType();
     method public java.util.List<androidx.wear.protolayout.LayoutElementBuilders.ArcLayoutElement!> getContents();
-    method public androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint? getLayoutConstraintsForDynamicAnchorAngle();
     method public androidx.wear.protolayout.ModifiersBuilders.Modifiers? getModifiers();
     method public androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp? getVerticalAlign();
   }
@@ -366,7 +365,6 @@
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorAngle(androidx.wear.protolayout.DimensionBuilders.DegreesProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorType(androidx.wear.protolayout.LayoutElementBuilders.ArcAnchorTypeProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setAnchorType(int);
-    method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setLayoutConstraintsForDynamicAnchorAngle(androidx.wear.protolayout.DimensionBuilders.AngularLayoutConstraint);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setModifiers(androidx.wear.protolayout.ModifiersBuilders.Modifiers);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setVerticalAlign(androidx.wear.protolayout.LayoutElementBuilders.VerticalAlignmentProp);
     method public androidx.wear.protolayout.LayoutElementBuilders.Arc.Builder setVerticalAlign(int);
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java
index 7c0582a..5d13328 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/LayoutElementBuilders.java
@@ -2834,10 +2834,16 @@
              * Sets the style of font to use (size, bold etc). If not specified, defaults to the
              * platform's default body font.
              *
+             * DynamicColor is not supported for SpanText.
+             *
              * @since 1.0
              */
             @NonNull
             public Builder setFontStyle(@NonNull FontStyle fontStyle) {
+                ColorProp colorProp = fontStyle.getColor();
+                if (colorProp != null && colorProp.getDynamicValue() != null) {
+                    throw new IllegalArgumentException("SpanText does not support DynamicColor.");
+                }
                 mImpl.setFontStyle(fontStyle.toProto());
                 mFingerprint.recordPropertyUpdate(
                         2, checkNotNull(fontStyle.getFingerprint()).aggregateValueAsInt());
@@ -4065,21 +4071,6 @@
         }
 
         /**
-         * Gets the bounding constraints for the layout affected by the dynamic value from {@link
-         * #getAnchorAngle()}.
-         *
-         * @since 1.2
-         */
-        @Nullable
-        public AngularLayoutConstraint getLayoutConstraintsForDynamicAnchorAngle() {
-            if (mImpl.hasAnchorAngle()) {
-                return AngularLayoutConstraint.fromProto(mImpl.getAnchorAngle());
-            } else {
-                return null;
-            }
-        }
-
-        /**
          * Gets how to align the contents of this container relative to anchor_angle. If not
          * defined, defaults to ARC_ANCHOR_CENTER.
          *
@@ -4208,11 +4199,6 @@
              * <p>While this field is statically accessible from 1.0, it's only bindable since
              * version 1.2 and renderers supporting version 1.2 will use the dynamic value (if set).
              *
-             * <p>When using a dynamic value, make sure to specify the bounding constraints for the
-             * affected layout element through {@code
-             * setLayoutConstraintsForDynamicAnchorAngle(AngularLayoutConstraint)} otherwise {@code
-             * build()} fails.
-             *
              * @since 1.0
              */
             @NonNull
@@ -4224,23 +4210,6 @@
             }
 
             /**
-             * Sets the bounding constraints for the layout affected by the dynamic value from
-             * {@link #setAnchorAngle(DegreesProp)}}.
-             *
-             * @since 1.2
-             */
-            @NonNull
-            public Builder setLayoutConstraintsForDynamicAnchorAngle(
-                    @NonNull DimensionBuilders.AngularLayoutConstraint angularLayoutConstraint) {
-                mImpl.mergeAnchorAngle(angularLayoutConstraint.toProto());
-                mFingerprint.recordPropertyUpdate(
-                        2,
-                        checkNotNull(angularLayoutConstraint.getFingerprint())
-                                .aggregateValueAsInt());
-                return this;
-            }
-
-            /**
              * Sets how to align the contents of this container relative to anchor_angle. If not
              * defined, defaults to ARC_ANCHOR_CENTER.
              *
@@ -4311,12 +4280,6 @@
             @Override
             @NonNull
             public Arc build() {
-                DimensionProto.DegreesProp anchorAngle = mImpl.getAnchorAngle();
-                if (anchorAngle.hasDynamicValue() && !anchorAngle.hasValueForLayout()) {
-                    throw new IllegalStateException(
-                            "anchorAngle with dynamic value requires "
-                                    + "layoutConstraintsForDynamicAnchorAngle to be present.");
-                }
                 return new Arc(mImpl.build(), mFingerprint);
             }
         }
diff --git a/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/LayoutElementBuildersTest.java b/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/LayoutElementBuildersTest.java
index f30480f..a30c860 100644
--- a/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/LayoutElementBuildersTest.java
+++ b/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/LayoutElementBuildersTest.java
@@ -122,7 +122,6 @@
         LayoutElementBuilders.Arc arc =
                 new LayoutElementBuilders.Arc.Builder()
                         .setAnchorAngle(DEGREES_PROP)
-                        .setLayoutConstraintsForDynamicAnchorAngle(DEGREES_PROP_CONSTRAINT)
                         .build();
 
         DimensionProto.DegreesProp anchorAngleProto = arc.toProto().getAnchorAngle();
@@ -130,17 +129,6 @@
         assertThat(anchorAngleProto.getValue()).isEqualTo(DEGREES_PROP.getValue());
         assertThat(anchorAngleProto.getDynamicValue().getStateSource().getSourceKey())
                 .isEqualTo(STATE_KEY);
-        assertThat(anchorAngleProto.getValueForLayout())
-                .isEqualTo(DEGREES_PROP_CONSTRAINT.getValue());
-        assertThat(anchorAngleProto.getAngularAlignmentForLayoutValue())
-                .isEqualTo(DEGREES_PROP_CONSTRAINT.getAngularAlignment());
-    }
-
-    @Test
-    public void arcSetAnchorAngle_withoutLayoutConstraint_throws() {
-        assertThrows(
-                IllegalStateException.class,
-                () -> new LayoutElementBuilders.Arc.Builder().setAnchorAngle(DEGREES_PROP).build());
     }
 
     @Test
diff --git a/wear/tiles/tiles-proto/build.gradle b/wear/tiles/tiles-proto/build.gradle
index 16bb24b..6b05bec 100644
--- a/wear/tiles/tiles-proto/build.gradle
+++ b/wear/tiles/tiles-proto/build.gradle
@@ -38,7 +38,7 @@
     // For some reason not specifying the configuration causes undeclared changes to the inputs to
     // extractIncludeProto task (making it out-dated without any source change).
     compileOnly(project(path:":wear:protolayout:protolayout-proto", configuration:"archives"))
-    api(project(path:":wear:protolayout:protolayout-proto", configuration:"shadow"))
+    api(project(path:":wear:protolayout:protolayout-proto", configuration:"default"))
 }
 // HACK: Move standard JAR to have another suffix and build a shadowJar with
 // no classifier (so it's picked up as the primary artifact).
diff --git a/wear/watchface/watchface-client-guava/src/androidTest/java/androidx/wear/watchface/client/guava/ListenableWatchFaceControlClientTest.kt b/wear/watchface/watchface-client-guava/src/androidTest/java/androidx/wear/watchface/client/guava/ListenableWatchFaceControlClientTest.kt
index 4801d04..00ef1e5 100644
--- a/wear/watchface/watchface-client-guava/src/androidTest/java/androidx/wear/watchface/client/guava/ListenableWatchFaceControlClientTest.kt
+++ b/wear/watchface/watchface-client-guava/src/androidTest/java/androidx/wear/watchface/client/guava/ListenableWatchFaceControlClientTest.kt
@@ -44,11 +44,12 @@
 import org.junit.Assert
 import org.junit.Assert.assertNull
 import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.Mockito
-import org.mockito.MockitoAnnotations
+import org.mockito.junit.MockitoJUnit
 
 private const val TIMEOUT_MS = 500L
 
@@ -56,6 +57,9 @@
 @MediumTest
 public class ListenableWatchFaceControlClientTest {
 
+    @get:Rule
+    val mocks = MockitoJUnit.rule()
+
     @Mock private lateinit var surfaceHolder: SurfaceHolder
     @Mock private lateinit var surface: Surface
 
@@ -63,7 +67,6 @@
 
     @Before
     public fun setUp() {
-        MockitoAnnotations.initMocks(this)
         Mockito.`when`(surfaceHolder.surfaceFrame).thenReturn(Rect(0, 0, 400, 400))
         Mockito.`when`(surfaceHolder.surface).thenReturn(surface)
     }
diff --git a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/TestWatchFaceServices.kt b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/TestWatchFaceServices.kt
index 52e8c10..5179e06c5 100644
--- a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/TestWatchFaceServices.kt
+++ b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/TestWatchFaceServices.kt
@@ -270,6 +270,7 @@
 ) : WatchFaceRuntimeService() {
 
     lateinit var lastResourceOnlyWatchFacePackageName: String
+    val lastResourceOnlyWatchFacePackageNameLatch = CountDownLatch(1)
 
     init {
         attachBaseContext(testContext)
@@ -299,6 +300,7 @@
         resourceOnlyWatchFacePackageName: String
     ): WatchFace {
         lastResourceOnlyWatchFacePackageName = resourceOnlyWatchFacePackageName
+        lastResourceOnlyWatchFacePackageNameLatch.countDown()
 
         return WatchFace(
             WatchFaceType.DIGITAL,
diff --git a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt
index a0b5bd6..62b6eb0 100644
--- a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt
+++ b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt
@@ -119,7 +119,7 @@
 import org.mockito.Mockito
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.`when`
-import org.mockito.MockitoAnnotations
+import org.mockito.junit.MockitoJUnit
 
 private const val CONNECT_TIMEOUT_MILLIS = 500L
 private const val DESTROY_TIMEOUT_MILLIS = 500L
@@ -138,6 +138,9 @@
         )
     }
 
+    @get:Rule
+    val mocks = MockitoJUnit.rule()
+
     @Mock protected lateinit var surfaceHolder: SurfaceHolder
 
     @Mock protected lateinit var surfaceHolder2: SurfaceHolder
@@ -164,7 +167,6 @@
 
     @Before
     fun setUp() {
-        MockitoAnnotations.initMocks(this)
         WatchFaceControlTestService.apiVersionOverride = null
         Mockito.`when`(surfaceHolder.surfaceFrame).thenReturn(Rect(0, 0, 400, 400))
         Mockito.`when`(surfaceHolder.surface).thenReturn(surface)
@@ -536,7 +538,10 @@
         val interactiveInstance = awaitWithTimeout(deferredInteractiveInstance)
 
         // Make sure watch face init has completed.
-        interactiveInstance.complicationSlotsState
+        assertTrue(
+            watchFaceService.lastResourceOnlyWatchFacePackageNameLatch
+                .await(UPDATE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
+        )
 
         assertThat(watchFaceService.lastResourceOnlyWatchFacePackageName)
             .isEqualTo("com.example.watchface")
@@ -949,6 +954,33 @@
     }
 
     @Test
+    fun addWatchFaceReadyListener_alreadyReady_newInstance() {
+        val wallpaperService = TestExampleCanvasAnalogWatchFaceService(context, surfaceHolder)
+        val interactiveInstance = getOrCreateTestSubject(wallpaperService, instanceId = "abc")
+        var sysUiInterface: InteractiveWatchFaceClient? = null
+
+        try {
+            // Perform an action that will block until watch face init has completed.
+            assertThat(interactiveInstance.complicationSlotsState).isNotEmpty()
+
+            // Get the instance created above
+            sysUiInterface = service.getInteractiveWatchFaceClientInstance("abc")!!
+
+            val wfReady = CompletableDeferred<Unit>()
+            sysUiInterface.addOnWatchFaceReadyListener(
+                { runnable -> runnable.run() },
+                { wfReady.complete(Unit) }
+            )
+
+            // This should happen quickly, but it can sometimes be slow.
+            awaitWithTimeout(wfReady, 1000)
+        } finally {
+            interactiveInstance.close()
+            sysUiInterface?.close()
+        }
+    }
+
+    @Test
     fun isConnectionAlive_false_after_close() {
         val interactiveInstance = getOrCreateTestSubject()
 
diff --git a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/HeadlessWatchFaceClient.kt b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/HeadlessWatchFaceClient.kt
index 3847531..81d5f0c 100644
--- a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/HeadlessWatchFaceClient.kt
+++ b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/HeadlessWatchFaceClient.kt
@@ -117,8 +117,7 @@
     ): Bitmap
 
     /** Whether or not the watch face supports [renderWatchFaceToSurface]. */
-    public val isRenderWatchFaceToSurfaceSupported: Boolean
-        @get:JvmName("isRenderWatchFaceToSurfaceSupported") get() = false
+    public val isRenderWatchFaceToSurfaceSupported: Boolean get() = false
 
     /**
      * Renders the [androidx.wear.watchface.ComplicationSlot] to a shared memory backed [Bitmap]
diff --git a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/InteractiveWatchFaceClient.kt b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/InteractiveWatchFaceClient.kt
index 0dc3321..7602cd2 100644
--- a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/InteractiveWatchFaceClient.kt
+++ b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/InteractiveWatchFaceClient.kt
@@ -161,8 +161,7 @@
     ): Bitmap
 
     /** Whether or not the watch face supports [RemoteWatchFaceViewHost]. */
-    public val isRemoteWatchFaceViewHostSupported: Boolean
-        @get:JvmName("isRemoteWatchFaceViewHostSupported") get() = false
+    public val isRemoteWatchFaceViewHostSupported: Boolean get() = false
 
     /**
      * Constructs a [RemoteWatchFaceViewHost] whose [RemoteWatchFaceViewHost.surfacePackage] can be
diff --git a/wear/watchface/watchface-complications-data-source-samples/build.gradle b/wear/watchface/watchface-complications-data-source-samples/build.gradle
index 8fe99bd..5bc5294 100644
--- a/wear/watchface/watchface-complications-data-source-samples/build.gradle
+++ b/wear/watchface/watchface-complications-data-source-samples/build.gradle
@@ -22,7 +22,6 @@
 dependencies {
     api(project(":wear:watchface:watchface-complications-data-source"))
     api(libs.guavaAndroid)
-    api(libs.kotlinStdlib)
     implementation("androidx.core:core:1.1.0")
     implementation("androidx.wear.protolayout:protolayout-expression:1.0.0-beta01")
 }
diff --git a/wear/watchface/watchface-complications-data-source-samples/src/main/java/androidx/wear/watchface/complications/datasource/samples/dynamic/HealthDataSourceServices.kt b/wear/watchface/watchface-complications-data-source-samples/src/main/java/androidx/wear/watchface/complications/datasource/samples/dynamic/HealthDataSourceServices.kt
index ff02a33..ebb3a88 100644
--- a/wear/watchface/watchface-complications-data-source-samples/src/main/java/androidx/wear/watchface/complications/datasource/samples/dynamic/HealthDataSourceServices.kt
+++ b/wear/watchface/watchface-complications-data-source-samples/src/main/java/androidx/wear/watchface/complications/datasource/samples/dynamic/HealthDataSourceServices.kt
@@ -18,6 +18,7 @@
 
 import android.Manifest.permission
 import android.content.pm.PackageManager.PERMISSION_GRANTED
+import android.os.Build
 import androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat
 import androidx.wear.protolayout.expression.DynamicBuilders.DynamicString
 import androidx.wear.protolayout.expression.PlatformHealthSources
@@ -137,6 +138,24 @@
             request: ComplicationRequest,
             listener: ComplicationRequestListener
         ) {
+            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
+                val text =
+                    PlainComplicationText.Builder(getString(R.string.dynamic_data_not_supported))
+                        .build()
+                listener.onComplicationData(
+                    RangedValueComplicationData.Builder(
+                            value = 0f,
+                            min = 0f,
+                            max = max,
+                            contentDescription = text
+                        )
+                        .setTitle(title)
+                        .setText(text)
+                        .build()
+                )
+                return
+            }
+
             val value = value
             if (value == null) {
                 // Missing permission.
diff --git a/wear/watchface/watchface-complications-data-source-samples/src/main/java/androidx/wear/watchface/complications/datasource/samples/dynamic/TimeDataSourceService.kt b/wear/watchface/watchface-complications-data-source-samples/src/main/java/androidx/wear/watchface/complications/datasource/samples/dynamic/TimeDataSourceService.kt
index 5e9ccf8..fe284ca 100644
--- a/wear/watchface/watchface-complications-data-source-samples/src/main/java/androidx/wear/watchface/complications/datasource/samples/dynamic/TimeDataSourceService.kt
+++ b/wear/watchface/watchface-complications-data-source-samples/src/main/java/androidx/wear/watchface/complications/datasource/samples/dynamic/TimeDataSourceService.kt
@@ -16,6 +16,7 @@
 
 package androidx.wear.watchface.complications.datasource.samples.dynamic
 
+import android.os.Build
 import androidx.wear.protolayout.expression.DynamicBuilders.DynamicDuration
 import androidx.wear.protolayout.expression.DynamicBuilders.DynamicInstant
 import androidx.wear.protolayout.expression.DynamicBuilders.DynamicString
@@ -33,6 +34,21 @@
         request: ComplicationRequest,
         listener: ComplicationRequestListener
     ) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
+            val text = PlainComplicationText.Builder("--").build()
+            listener.onComplicationData(
+                RangedValueComplicationData.Builder(
+                        value = 0f,
+                        min = 0f,
+                        max = 9f,
+                        contentDescription = text
+                    )
+                    .setText(text)
+                    .build()
+            )
+            return
+        }
+
         val epochDuration: DynamicDuration =
             DynamicInstant.withSecondsPrecision(EPOCH)
                 .durationUntil(DynamicInstant.platformTimeWithSecondsPrecision())
diff --git a/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceService.kt b/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceService.kt
index dd4398b..1790ab2 100644
--- a/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceService.kt
+++ b/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceService.kt
@@ -183,11 +183,11 @@
  *
  * Manifest requirements:
  * - The manifest declaration of this service must include an intent filter for
- *   android.support.wearable.complications.ACTION_COMPLICATION_UPDATE_REQUEST.
+ *   `android.support.wearable.complications.ACTION_COMPLICATION_UPDATE_REQUEST`.
  * - A ComplicationDataSourceService must include a `meta-data` tag with
- *   android.support.wearable.complications.SUPPORTED_TYPES in its manifest entry.
+ *   `android.support.wearable.complications.SUPPORTED_TYPES` in its manifest entry.
  *
- * The value of android.support.wearable.complications.SUPPORTED_TYPES should be a comma separated
+ * The value of `android.support.wearable.complications.SUPPORTED_TYPES` should be a comma separated
  * list of types supported by the data source, from this table:
  *
  * | Androidx class                       | Tag name          |
@@ -205,47 +205,49 @@
  * multiple types in a single complication slot, the watch face will determine which types it
  * prefers.
  *
- * For example, a complication data source that supports the RANGED_VALUE, SHORT_TEXT, and ICON
- * types would include the following in its manifest entry:
+ * For example, a complication data source that supports the `RANGED_VALUE`, `SHORT_TEXT`, and
+ * `ICON` types would include the following in its manifest entry:
  * ```
- * <meta-data android:name="android.support.wearable.complications.SUPPORTED_TYPES"
- * android:value="RANGED_VALUE,SHORT_TEXT,ICON"/>
+ * <meta-data
+ *     android:name="android.support.wearable.complications.SUPPORTED_TYPES"
+ *     android:value="RANGED_VALUE,SHORT_TEXT,ICON" />
  * ```
  *
- * From android T onwards, it is recommended for Complication DataSourceServices to be direct boot
+ * From android T onwards, it is recommended for [ComplicationDataSourceService]s to be direct boot
  * aware because the system is able to fetch complications before the lock screen has been removed.
- * To do this add android:directBootAware="true" to your service tag.
+ * To do this add `android:directBootAware="true"` to your service tag.
  * - A provider can choose to trust one or more watch faces by including the following in its
  *   manifest entry:
  * ```
- * <meta-data android:name="android.support.wearable.complications.SAFE_WATCH_FACES
- * android:value="com.pkg1/com.trusted.wf1,com.pkg2/com.trusted.wf2"/>
+ * <meta-data
+ *     android:name="android.support.wearable.complications.SAFE_WATCH_FACES"
+ *     android:value="com.pkg1/com.trusted.wf1,com.pkg2/com.trusted.wf2" />
  * ```
  *
  * The listed watch faces will not need
- * 'com.google.android.wearable.permission.RECEIVE_COMPLICATION_DATA' in order to receive
+ * `com.google.android.wearable.permission.RECEIVE_COMPLICATION_DATA` in order to receive
  * complications from this provider. Also the provider may choose to serve different types to safe
  * watch faces by including the following in its manifest:
  * ```
- * <meta-data android:name=
- *     "androidx.wear.watchface.complications.datasource.SAFE_WATCH_FACE_SUPPORTED_TYPES"
- *      android:value="ICON"/>
+ * <meta-data
+ *     android:name="androidx.wear.watchface.complications.datasource.SAFE_WATCH_FACE_SUPPORTED_TYPES"
+ *     android:value="ICON" />
  * ```
  *
  * In addition the provider can learn if a request is for a safe watchface by examining
- * [ComplicationRequest.isForSafeWatchFace]. Note SAFE_WATCH_FACE_SUPPORTED_TYPES and
- * isForSafeWatchFace are gated behind the privileged permission
+ * [ComplicationRequest.isForSafeWatchFace]. Note `SAFE_WATCH_FACE_SUPPORTED_TYPES` and
+ * `isForSafeWatchFace` are gated behind the privileged permission
  * `com.google.wear.permission.GET_IS_FOR_SAFE_WATCH_FACE`.
  * - A ComplicationDataSourceService should include a `meta-data` tag with
- *   `android.support.wearable.complications.UPDATE_PERIOD_SECONDS` its manifest entry. The value of
- *   this tag is the number of seconds the complication data source would like to elapse between
+ *   `android.support.wearable.complications.UPDATE_PERIOD_SECONDS` in its manifest entry. The value
+ *   of this tag is the number of seconds the complication data source would like to elapse between
  *   update requests.
  *
  * **Note that update requests are not guaranteed to be sent with this frequency.** For
- * complications with frequent updates they can also register a separate
- * [METADATA_KEY_IMMEDIATE_UPDATE_PERIOD_MILLISECONDS] meta data tag which supports sampling at up
- * to 1Hz when the watch face is visible and non-ambient, however this also requires the
- * DataSourceService to have the privileged permission
+ * complications with frequent updates they can also register a separate `meta-data` tag with
+ * `androidx.wear.watchface.complications.data.source.IMMEDIATE_UPDATE_PERIOD_MILLISECONDS` in their
+ * manifest which supports sampling at up to 1Hz when the watch face is visible and non-ambient,
+ * however this also requires the application to have the privileged permission
  * `com.google.android.wearable.permission.USE_IMMEDIATE_COMPLICATION_UPDATE`.
  *
  * If a complication data source never needs to receive update requests beyond the one sent when a
@@ -254,37 +256,39 @@
  * For example, a complication data source that would like to update at most every hour should
  * include the following in its manifest entry:
  * ```
- * <meta-data android:name="android.support.wearable.complications.UPDATE_PERIOD_SECONDS"
- * android:value="3600"/>
+ * <meta-data
+ *     android:name="android.support.wearable.complications.UPDATE_PERIOD_SECONDS"
+ *     android:value="3600" />
  * ```
- * - A ComplicationDataSourceService can include a `meta-data` tag with
+ * - A [ComplicationDataSourceService] can include a `meta-data` tag with
  *   android.support.wearable.complications.PROVIDER_CONFIG_ACTION its manifest entry to cause a
  *   configuration activity to be shown when the complication data source is selected.
  *
  * The configuration activity must reside in the same package as the complication data source, and
  * must register an intent filter for the action specified here, including
- * android.support.wearable.complications.category.PROVIDER_CONFIG as well as
+ * `android.support.wearable.complications.category.PROVIDER_CONFIG` as well as
  * [Intent.CATEGORY_DEFAULT] as categories.
  *
  * The complication id being configured will be included in the intent that starts the config
- * activity using the extra key android.support.wearable.complications.EXTRA_CONFIG_COMPLICATION_ID.
+ * activity using the extra key
+ * `android.support.wearable.complications.EXTRA_CONFIG_COMPLICATION_ID`.
  *
  * The complication type that will be requested from the complication data source will also be
- * included, using the extra key android.support.wearable.complications
- * .EXTRA_CONFIG_COMPLICATION_TYPE.
+ * included, using the extra key
+ * `android.support.wearable.complications.EXTRA_CONFIG_COMPLICATION_TYPE`.
  *
  * The complication data source's [ComponentName] will also be included in the intent that starts
  * the config activity, using the extra key
- * android.support.wearable.complications.EXTRA_CONFIG_PROVIDER_COMPONENT.
+ * `android.support.wearable.complications.EXTRA_CONFIG_PROVIDER_COMPONENT`.
  *
  * The config activity must call [Activity.setResult] with either [Activity.RESULT_OK] or
  * [Activity.RESULT_CANCELED] before it is finished, to tell the system whether or not the
  * complication data source should be set on the given complication.
  *
- * It is possible to provide additional 'meta-data' tag
- * androidx.watchface.complications.datasource.DEFAULT_CONFIG_SUPPORTED in the service set to "true"
- * to let the system know that the data source is able to provide complication data before it is
- * configured.
+ * It is possible to provide additional `meta-data` tag
+ * `androidx.watchface.complications.datasource.DEFAULT_CONFIG_SUPPORTED` in the service set to
+ * `"true"` to let the system know that the data source is able to provide complication data before
+ * it is configured.
  * - The manifest entry for the service should also include an android:icon attribute. The icon
  *   provided there should be a single-color white icon that represents the complication data
  *   source. This icon will be shown in the complication data source chooser interface, and may also
@@ -298,8 +302,8 @@
  * limit of 100 data sources per APK. Above that the companion watchface editor won't support this
  * complication data source app.
  *
- * There's no need to call setDataSource for any the ComplicationData Builders because the system
- * will append this value on your behalf.
+ * There's no need to call `setDataSource` for any the [ComplicationData] Builders because the
+ * system will append this value on your behalf.
  */
 public abstract class ComplicationDataSourceService : Service() {
     private var wrapper: IComplicationProviderWrapper? = null
@@ -768,15 +772,18 @@
          * the [ComplicationData], but omitting the "TYPE_" prefix, e.g. `SHORT_TEXT`, `LONG_TEXT`,
          * `RANGED_VALUE`.
          *
-         * The order in which types are listed has no significance. In the case where a watch face
-         * supports multiple types in a single complication slot, the watch face will determine
-         * which types it prefers.
+         * The order of types in `METADATA_KEY_SUPPORTED_TYPES` has no significance. During
+         * complication data source selection, each item in the complication slot's supported types
+         * is checked against entries in the data source's `METADATA_KEY_SUPPORTED_TYPES` and the
+         * first matching entry from the slot's support types (if any) is chosen. If there are no
+         * matches then this data source is not eligible to be selected in that slot.
          *
          * For example, a complication data source that supports the RANGED_VALUE, SHORT_TEXT, and
          * ICON type would include the following in its manifest entry:
          * ```
-         * <meta-data android:name="android.support.wearable.complications.SUPPORTED_TYPES"
-         * android:value="RANGED_VALUE,SHORT_TEXT,ICON"/>
+         * <meta-data
+         *     android:name="android.support.wearable.complications.SUPPORTED_TYPES"
+         *     android:value="RANGED_VALUE,SHORT_TEXT,ICON" />
          * ```
          */
         // TODO(b/192233205): Migrate value to androidx.
@@ -812,8 +819,9 @@
          * For example, a complication data source that would like to update every ten minutes
          * should include the following in its manifest entry:
          * ```
-         * <meta-data android:name="android.support.wearable.complications.UPDATE_PERIOD_SECONDS"
-         * android:value="600"/>
+         * <meta-data
+         *     android:name="android.support.wearable.complications.UPDATE_PERIOD_SECONDS"
+         *     android:value="600" />
          * ```
          */
         // TODO(b/192233205): Migrate value to androidx.
diff --git a/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceServiceTest.kt b/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceServiceTest.kt
index a5bfa74..8f2a7f2 100644
--- a/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceServiceTest.kt
+++ b/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceServiceTest.kt
@@ -17,6 +17,7 @@
 
 import android.content.Intent
 import android.content.res.Resources
+import android.os.Build
 import android.os.Bundle
 import android.os.Handler
 import android.os.HandlerThread
@@ -25,6 +26,7 @@
 import android.support.wearable.complications.IComplicationManager
 import android.support.wearable.complications.IComplicationProvider
 import android.util.Log
+import androidx.annotation.RequiresApi
 import androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat
 import androidx.wear.protolayout.expression.DynamicBuilders.DynamicString
 import androidx.wear.watchface.complications.data.ComplicationData
@@ -51,6 +53,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.kotlin.argumentCaptor
 import org.mockito.kotlin.mock
+import org.robolectric.annotation.Config
 import org.robolectric.annotation.internal.DoNotInstrument
 import org.robolectric.shadows.ShadowLog
 import org.robolectric.shadows.ShadowLooper.runUiThreadTasks
@@ -308,6 +311,7 @@
             .isEqualTo("hello preview")
     }
 
+    @RequiresApi(Build.VERSION_CODES.TIRAMISU)
     enum class GetComplicationPreviewDataInvalidScenario(
         val data: ComplicationData,
         val message: String
@@ -374,6 +378,7 @@
     }
 
     @Test
+    @Config(minSdk = Build.VERSION_CODES.TIRAMISU)
     fun testGetComplicationPreviewData_invalid_fails() {
         for (scenario in GetComplicationPreviewDataInvalidScenario.values()) {
             mService.previewData = scenario.data
diff --git a/wear/watchface/watchface-complications-data/api/current.txt b/wear/watchface/watchface-complications-data/api/current.txt
index ffe8f96..80af828 100644
--- a/wear/watchface/watchface-complications-data/api/current.txt
+++ b/wear/watchface/watchface-complications-data/api/current.txt
@@ -92,7 +92,7 @@
     property public final java.time.Instant instant;
   }
 
-  public final class DynamicComplicationText implements androidx.wear.watchface.complications.data.ComplicationText {
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class DynamicComplicationText implements androidx.wear.watchface.complications.data.ComplicationText {
     ctor public DynamicComplicationText(androidx.wear.protolayout.expression.DynamicBuilders.DynamicString dynamicValue, CharSequence fallbackValue);
     method public androidx.wear.protolayout.expression.DynamicBuilders.DynamicString getDynamicValue();
     method public CharSequence getFallbackValue();
@@ -116,7 +116,7 @@
   @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class GoalProgressComplicationData extends androidx.wear.watchface.complications.data.ComplicationData {
     method public androidx.wear.watchface.complications.data.ColorRamp? getColorRamp();
     method public androidx.wear.watchface.complications.data.ComplicationText? getContentDescription();
-    method public androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? getDynamicValue();
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? getDynamicValue();
     method public androidx.wear.watchface.complications.data.MonochromaticImage? getMonochromaticImage();
     method public androidx.wear.watchface.complications.data.SmallImage? getSmallImage();
     method public float getTargetValue();
@@ -125,7 +125,7 @@
     method public float getValue();
     property public final androidx.wear.watchface.complications.data.ColorRamp? colorRamp;
     property public final androidx.wear.watchface.complications.data.ComplicationText? contentDescription;
-    property public final androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? dynamicValue;
+    property @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? dynamicValue;
     property public final androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage;
     property public final androidx.wear.watchface.complications.data.SmallImage? smallImage;
     property public final float targetValue;
@@ -138,7 +138,7 @@
   }
 
   @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public static final class GoalProgressComplicationData.Builder {
-    ctor public GoalProgressComplicationData.Builder(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat dynamicValue, float fallbackValue, float targetValue, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
+    ctor @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public GoalProgressComplicationData.Builder(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat dynamicValue, float fallbackValue, float targetValue, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     ctor public GoalProgressComplicationData.Builder(float value, float targetValue, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData build();
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setColorRamp(androidx.wear.watchface.complications.data.ColorRamp? colorRamp);
@@ -321,7 +321,7 @@
   public final class RangedValueComplicationData extends androidx.wear.watchface.complications.data.ComplicationData {
     method public androidx.wear.watchface.complications.data.ColorRamp? getColorRamp();
     method public androidx.wear.watchface.complications.data.ComplicationText? getContentDescription();
-    method public androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? getDynamicValue();
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? getDynamicValue();
     method public float getMax();
     method public float getMin();
     method public androidx.wear.watchface.complications.data.MonochromaticImage? getMonochromaticImage();
@@ -332,7 +332,7 @@
     method public int getValueType();
     property public final androidx.wear.watchface.complications.data.ColorRamp? colorRamp;
     property public final androidx.wear.watchface.complications.data.ComplicationText? contentDescription;
-    property public final androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? dynamicValue;
+    property @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? dynamicValue;
     property public final float max;
     property public final float min;
     property public final androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage;
@@ -350,7 +350,7 @@
   }
 
   public static final class RangedValueComplicationData.Builder {
-    ctor public RangedValueComplicationData.Builder(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat dynamicValue, float fallbackValue, float min, float max, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
+    ctor @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public RangedValueComplicationData.Builder(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat dynamicValue, float fallbackValue, float min, float max, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     ctor public RangedValueComplicationData.Builder(float value, float min, float max, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData build();
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setColorRamp(androidx.wear.watchface.complications.data.ColorRamp? colorRamp);
diff --git a/wear/watchface/watchface-complications-data/api/restricted_current.txt b/wear/watchface/watchface-complications-data/api/restricted_current.txt
index ffe8f96..80af828 100644
--- a/wear/watchface/watchface-complications-data/api/restricted_current.txt
+++ b/wear/watchface/watchface-complications-data/api/restricted_current.txt
@@ -92,7 +92,7 @@
     property public final java.time.Instant instant;
   }
 
-  public final class DynamicComplicationText implements androidx.wear.watchface.complications.data.ComplicationText {
+  @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class DynamicComplicationText implements androidx.wear.watchface.complications.data.ComplicationText {
     ctor public DynamicComplicationText(androidx.wear.protolayout.expression.DynamicBuilders.DynamicString dynamicValue, CharSequence fallbackValue);
     method public androidx.wear.protolayout.expression.DynamicBuilders.DynamicString getDynamicValue();
     method public CharSequence getFallbackValue();
@@ -116,7 +116,7 @@
   @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final class GoalProgressComplicationData extends androidx.wear.watchface.complications.data.ComplicationData {
     method public androidx.wear.watchface.complications.data.ColorRamp? getColorRamp();
     method public androidx.wear.watchface.complications.data.ComplicationText? getContentDescription();
-    method public androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? getDynamicValue();
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? getDynamicValue();
     method public androidx.wear.watchface.complications.data.MonochromaticImage? getMonochromaticImage();
     method public androidx.wear.watchface.complications.data.SmallImage? getSmallImage();
     method public float getTargetValue();
@@ -125,7 +125,7 @@
     method public float getValue();
     property public final androidx.wear.watchface.complications.data.ColorRamp? colorRamp;
     property public final androidx.wear.watchface.complications.data.ComplicationText? contentDescription;
-    property public final androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? dynamicValue;
+    property @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? dynamicValue;
     property public final androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage;
     property public final androidx.wear.watchface.complications.data.SmallImage? smallImage;
     property public final float targetValue;
@@ -138,7 +138,7 @@
   }
 
   @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public static final class GoalProgressComplicationData.Builder {
-    ctor public GoalProgressComplicationData.Builder(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat dynamicValue, float fallbackValue, float targetValue, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
+    ctor @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public GoalProgressComplicationData.Builder(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat dynamicValue, float fallbackValue, float targetValue, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     ctor public GoalProgressComplicationData.Builder(float value, float targetValue, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData build();
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setColorRamp(androidx.wear.watchface.complications.data.ColorRamp? colorRamp);
@@ -321,7 +321,7 @@
   public final class RangedValueComplicationData extends androidx.wear.watchface.complications.data.ComplicationData {
     method public androidx.wear.watchface.complications.data.ColorRamp? getColorRamp();
     method public androidx.wear.watchface.complications.data.ComplicationText? getContentDescription();
-    method public androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? getDynamicValue();
+    method @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? getDynamicValue();
     method public float getMax();
     method public float getMin();
     method public androidx.wear.watchface.complications.data.MonochromaticImage? getMonochromaticImage();
@@ -332,7 +332,7 @@
     method public int getValueType();
     property public final androidx.wear.watchface.complications.data.ColorRamp? colorRamp;
     property public final androidx.wear.watchface.complications.data.ComplicationText? contentDescription;
-    property public final androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? dynamicValue;
+    property @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public final androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? dynamicValue;
     property public final float max;
     property public final float min;
     property public final androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage;
@@ -350,7 +350,7 @@
   }
 
   public static final class RangedValueComplicationData.Builder {
-    ctor public RangedValueComplicationData.Builder(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat dynamicValue, float fallbackValue, float min, float max, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
+    ctor @RequiresApi(android.os.Build.VERSION_CODES.TIRAMISU) public RangedValueComplicationData.Builder(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat dynamicValue, float fallbackValue, float min, float max, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     ctor public RangedValueComplicationData.Builder(float value, float min, float max, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData build();
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setColorRamp(androidx.wear.watchface.complications.data.ColorRamp? colorRamp);
diff --git a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/ComplicationDataEvaluator.kt b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/ComplicationDataEvaluator.kt
index 830ced78..619c2c7 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/ComplicationDataEvaluator.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/ComplicationDataEvaluator.kt
@@ -51,6 +51,7 @@
 import kotlinx.coroutines.flow.conflate
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.invoke
 import kotlinx.coroutines.launch
 
@@ -148,21 +149,34 @@
      */
     private fun WireComplicationData.topLevelSetterFlows(): List<Flow<WireComplicationDataSetter>> =
         buildList {
-            if (hasRangedDynamicValue()) {
+            if (hasRangedDynamicValue() && rangedDynamicValue != null) {
                 add(
-                    bindDynamicFloat(
-                        rangedDynamicValue,
-                        dynamicValueTrimmer = { setRangedDynamicValue(null) },
-                        floatSetter = { setRangedValue(it) },
-                    )
+                    rangedDynamicValue!!
+                        .evaluate()
+                        .toDataSetter(
+                            setter = { setRangedValue(it) },
+                            dynamicValueTrimmer = { setRangedDynamicValue(null) },
+                        )
                 )
             }
-            if (hasLongText()) add(bindDynamicText(longText) { setLongText(it) })
-            if (hasLongTitle()) add(bindDynamicText(longTitle) { setLongTitle(it) })
-            if (hasShortText()) add(bindDynamicText(shortText) { setShortText(it) })
-            if (hasShortTitle()) add(bindDynamicText(shortTitle) { setShortTitle(it) })
-            if (hasContentDescription()) {
-                add(bindDynamicText(contentDescription) { setContentDescription(it) })
+            if (hasLongText() && longText?.dynamicValue != null) {
+                add(longText!!.dynamicValue!!.evaluateToTextSetter { setLongText(it) })
+            }
+            if (hasLongTitle() && longTitle?.dynamicValue != null) {
+                add(longTitle!!.dynamicValue!!.evaluateToTextSetter { setLongTitle(it) })
+            }
+            if (hasShortText() && shortText?.dynamicValue != null) {
+                add(shortText!!.dynamicValue!!.evaluateToTextSetter { setShortText(it) })
+            }
+            if (hasShortTitle() && shortTitle?.dynamicValue != null) {
+                add(shortTitle!!.dynamicValue!!.evaluateToTextSetter { setShortTitle(it) })
+            }
+            if (hasContentDescription() && contentDescription?.dynamicValue != null) {
+                add(
+                    contentDescription!!.dynamicValue!!.evaluateToTextSetter {
+                        setContentDescription(it)
+                    }
+                )
             }
         }
 
@@ -231,91 +245,92 @@
         }
     }
 
-    /**
-     * Returns a [Flow] of [WireComplicationDataSetter] based on [DynamicFloat] evaluation.
-     *
-     * Uses the generic [bindDynamicType] that provides a default [Executor] and
-     * [DynamicTypeValueReceiver] based on the generated [Flow].
-     */
-    private fun bindDynamicFloat(
-        dynamicFloat: DynamicFloat?,
+    /** Converts a generic flow to a [WireComplicationDataSetter]. */
+    private fun <T : Any> Flow<T?>.toDataSetter(
+        setter: WireComplicationData.Builder.(T) -> WireComplicationData.Builder,
         dynamicValueTrimmer: WireComplicationData.Builder.() -> WireComplicationData.Builder,
-        floatSetter: WireComplicationData.Builder.(Float) -> WireComplicationData.Builder,
     ): Flow<WireComplicationDataSetter> {
-        // If there's no dynamic value, return a no-op setter.
-        dynamicFloat ?: return flowOf { it }
-        return bindDynamicType(
+        return map { value ->
+            if (value == null) return@map { null } // emit invalidating setter.
+            { builder ->
+                var newBuilder = setter(builder, value)
+                if (!keepDynamicValues) newBuilder = dynamicValueTrimmer(newBuilder)
+                newBuilder
+            }
+        }
+    }
+
+    /**
+     * Evaluates a [DynamicString] and converts it to a [WireComplicationDataSetter].
+     *
+     * This combines [DynamicString.evaluate] and [toDataSetter] because the trimming requires the
+     * evaluated [DynamicString], so combining it avoids mentioning it twice, i.e.:
+     * ```
+     * dynamicString.evaluate().toTextSetter(dynamicString) { ... }
+     * ```
+     */
+    private fun DynamicString.evaluateToTextSetter(
+        setter: WireComplicationData.Builder.(WireComplicationText) -> WireComplicationData.Builder,
+    ): Flow<WireComplicationDataSetter> =
+        evaluate()
+            .toDataSetter(
+                setter = { value ->
+                    if (keepDynamicValues) {
+                        setter(WireComplicationText(value, this@evaluateToTextSetter))
+                    } else {
+                        setter(WireComplicationText(value))
+                    }
+                },
+                dynamicValueTrimmer = { this }, // Trimming is done in setter.
+            )
+
+    /**
+     * Binds a [DynamicFloat], returning a [Flow] of [Float] or `null` if the binding is
+     * invalidated.
+     */
+    private fun DynamicFloat.evaluate(): Flow<Float?> {
+        return evaluateDynamicType(
             bindingRequest = { executor, receiver ->
-                DynamicTypeBindingRequest.forDynamicFloat(dynamicFloat, executor, receiver)
-            },
-            builderSetter = { builder, value ->
-                val trimmed = if (keepDynamicValues) builder else dynamicValueTrimmer(builder)
-                floatSetter(trimmed, value)
+                DynamicTypeBindingRequest.forDynamicFloat(this@evaluate, executor, receiver)
             }
         )
     }
 
     /**
-     * Returns a [Flow] of [WireComplicationDataSetter] based on [DynamicString] evaluation (within
-     * a [WireComplicationText].
-     *
-     * Uses the generic [bindDynamicType] that provides a default [Executor] and
-     * [DynamicTypeValueReceiver] based on the generated [Flow].
+     * Binds a [DynamicString], returning a [Flow] of [String] or `null` if the binding is
+     * invalidated.
      */
-    private fun bindDynamicText(
-        unevaluatedText: WireComplicationText?,
-        textSetter:
-            WireComplicationData.Builder.(WireComplicationText) -> WireComplicationData.Builder,
-    ): Flow<WireComplicationDataSetter> {
-        // If there's no dynamic value, return a no-op setter.
-        val dynamicString: DynamicString = unevaluatedText?.dynamicValue ?: return flowOf { it }
-        return bindDynamicType(
+    private fun DynamicString.evaluate(): Flow<String?> {
+        return evaluateDynamicType(
             bindingRequest = { executor, receiver ->
                 DynamicTypeBindingRequest.forDynamicString(
-                    dynamicString,
+                    this@evaluate,
                     ULocale.getDefault(),
                     executor,
                     receiver,
                 )
-            },
-            builderSetter = { builder, value ->
-                val evaluatedText =
-                    if (keepDynamicValues) {
-                        WireComplicationText(value, dynamicString)
-                    } else {
-                        WireComplicationText(value)
-                    }
-                textSetter(builder, evaluatedText)
             }
         )
     }
 
     /**
-     * Returns a [Flow] of [WireComplicationDataSetter] based on a [DynamicTypeBindingRequest] and a
-     * [builderSetter] that takes the evaluated raw value and sets the relevant builder field..
-     *
-     * In high-level terms, this converts the [DynamicTypeValueReceiver] callback given to
-     * [DynamicTypeEvaluator.bind] into a Kotlin [Flow], for easier use (e.g. to [combine] binding
-     * of multiple fields). The [Flow] is conflated (ignoring emissions that we didn't have time to
-     * process), as only the latest evaluation matters.
+     * Converts [DynamicTypeEvaluator.bind] to [Flow], emitting `null` when the binding is
+     * invalidated.
      *
      * The actual implementation of [DynamicTypeValueReceiver] is separated to the helper class
-     * [DynamicTypeValueReceiverToChannelConverter].
+     * [DynamicTypeValueReceiverToChannel].
      */
-    private fun <T : Any> bindDynamicType(
+    private fun <T : Any> evaluateDynamicType(
         bindingRequest: (Executor, DynamicTypeValueReceiver<T>) -> DynamicTypeBindingRequest,
-        builderSetter: (WireComplicationData.Builder, T) -> WireComplicationData.Builder,
-    ): Flow<WireComplicationDataSetter> =
+    ): Flow<T?> =
         callbackFlow {
                 // Binding DynamicTypeEvaluator to the provided binding request.
                 val boundDynamicType: BoundDynamicType =
                     evaluator.bind(
                         bindingRequest(
                             currentCoroutineContext().asExecutor(),
-                            DynamicTypeValueReceiverToChannelConverter(
-                                /* callbackFlow */ channel,
-                                builderSetter
-                            )
+                            // Emitting values to the callbackFlow's channel.
+                            DynamicTypeValueReceiverToChannel(channel)
                         )
                     )
                 // Start evaluation.
@@ -331,26 +346,21 @@
     /**
      * Converts [DynamicTypeValueReceiver] into a [SendChannel] (from a [callbackFlow]).
      *
-     * When [onData] is invoked, emits a method that applies the [builderSetter] on the data. When
-     * [onInvalidated] is invoked, emits a method that returns `null`.
+     * [onData] emits the value, [onInvalidated] emits `null`.
      */
-    private class DynamicTypeValueReceiverToChannelConverter<T : Any>(
-        private val channel: SendChannel<WireComplicationDataSetter>,
-        private val builderSetter:
-            (WireComplicationData.Builder, T) -> WireComplicationData.Builder,
+    private class DynamicTypeValueReceiverToChannel<T : Any>(
+        private val channel: SendChannel<T?>,
     ) : DynamicTypeValueReceiver<T> {
         override fun onData(newData: T) {
             channel
-                // Setter method that applies the builderSetter.
-                .trySend { builder -> builderSetter(builder, newData) }
+                .trySend(newData)
                 // Shouldn't fail for overflow as we conflate the flow.
                 .onFailure { e -> Log.e(TAG, "Failed sending dynamic update.", e) }
         }
 
         override fun onInvalidated() {
             channel
-                // Setter method that returns null.
-                .trySend { null }
+                .trySend(null)
                 // Shouldn't fail for overflow as we conflate the flow.
                 .onFailure { e -> Log.e(TAG, "Failed sending dynamic update.", e) }
         }
diff --git a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Data.kt b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Data.kt
index 8986f09..0244924 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Data.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Data.kt
@@ -246,7 +246,8 @@
         }
 
         /**
-         * Sets the complication's fallback, used in case any dynamic value has been invalidated.
+         * Sets the complication's fallback, used in case any dynamic value cannot be evaluated,
+         * e.g. when a data source is not available.
          *
          * IMPORTANT: This is only used when the system supports dynamic values. See each dynamic
          * value field's fallback companion field for the situation where the system does not
@@ -881,7 +882,7 @@
 public class RangedValueComplicationData
 internal constructor(
     public val value: Float,
-    public val dynamicValue: DynamicFloat?,
+    @get:RequiresApi(Build.VERSION_CODES.TIRAMISU) public val dynamicValue: DynamicFloat?,
     public val min: Float,
     public val max: Float,
     public val monochromaticImage: MonochromaticImage?,
@@ -976,8 +977,8 @@
          *   semantic meaning of value can be specified via [setValueType].
          *
          *   IMPORTANT: This is only used when the system does not support dynamic values _at all_.
-         *   See [setDynamicValueInvalidationFallback] for the situation where the dynamic value has
-         *   been invalidated.
+         *   See [setDynamicValueInvalidationFallback] for the situation where the dynamic value
+         *   cannot be evaluated, e.g. when a data source is not available.
          *
          * @param min The minimum value. For [TYPE_PERCENTAGE] this must be 0f.
          * @param max The maximum value. This must be less than [Float.MAX_VALUE]. For
@@ -987,6 +988,7 @@
          *   complications do not have textual representation this attribute can be used for
          *   providing such. Please do not include the word 'complication' in the description.
          */
+        @RequiresApi(Build.VERSION_CODES.TIRAMISU)
         public constructor(
             dynamicValue: DynamicFloat,
             fallbackValue: Float,
@@ -1244,7 +1246,7 @@
 public class GoalProgressComplicationData
 internal constructor(
     public val value: Float,
-    public val dynamicValue: DynamicFloat?,
+    @get:RequiresApi(Build.VERSION_CODES.TIRAMISU) public val dynamicValue: DynamicFloat?,
     public val targetValue: Float,
     public val monochromaticImage: MonochromaticImage?,
     public val smallImage: SmallImage?,
@@ -1323,8 +1325,8 @@
          *   systems that don't support dynamic values, and should be >= 0.
          *
          *   IMPORTANT: This is only used when the system does not support dynamic values _at all_.
-         *   See [setDynamicValueInvalidationFallback] for the situation where the dynamic value has
-         *   been invalidated.
+         *   See [setDynamicValueInvalidationFallback] for the situation where the dynamic value
+         *   cannot be evaluated, e.g. when a data source is not available.
          *
          * @param targetValue The target value. This must be less than [Float.MAX_VALUE].
          * @param contentDescription Defines localized text that briefly describes content of the
@@ -1332,6 +1334,7 @@
          *   complications do not have textual representation this attribute can be used for
          *   providing such. Please do not include the word 'complication' in the description.
          */
+        @RequiresApi(Build.VERSION_CODES.TIRAMISU)
         public constructor(
             dynamicValue: DynamicFloat,
             fallbackValue: Float,
diff --git a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Text.kt b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Text.kt
index 89fb62e..2652d18 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Text.kt
+++ b/wear/watchface/watchface-complications-data/src/main/java/androidx/wear/watchface/complications/data/Text.kt
@@ -16,8 +16,10 @@
 
 package androidx.wear.watchface.complications.data
 
+import android.annotation.SuppressLint
 import android.content.res.Resources
 import android.icu.util.TimeZone
+import android.os.Build
 import android.support.wearable.complications.ComplicationData as WireComplicationData
 import android.support.wearable.complications.ComplicationText as WireComplicationText
 import android.support.wearable.complications.ComplicationText.TimeDifferenceBuilder as WireComplicationTextTimeDifferenceBuilder
@@ -32,6 +34,7 @@
 import android.text.style.SuperscriptSpan
 import android.text.style.TypefaceSpan
 import android.text.style.UnderlineSpan
+import androidx.annotation.RequiresApi
 import androidx.annotation.RestrictTo
 import androidx.wear.protolayout.expression.DynamicBuilders.DynamicString
 import java.time.Instant
@@ -542,6 +545,7 @@
 }
 
 /** Converts a [WireComplicationText] into an equivalent [ComplicationText] instead. */
+@SuppressLint("NewApi") // This is what's in the wire format, regardless of whether it's supported.
 internal fun WireComplicationText.toApiComplicationText(
     placeholderAware: Boolean = false
 ): ComplicationText =
@@ -611,9 +615,10 @@
  * @param fallbackValue Used when the system does not support dynamic values.
  *
  *   IMPORTANT: This is only used when the system does not support dynamic values _at all_. See
- *   [ComplicationData.BaseBuilder.setDynamicValueInvalidationFallback] for the situation where the
- *   dynamic value has been invalidated.
+ *   [ComplicationData.dynamicValueInvalidationFallback] for the situation where the dynamic value
+ *   cannot be evaluated, e.g. when a data source is not available.
  */
+@RequiresApi(Build.VERSION_CODES.TIRAMISU)
 public class DynamicComplicationText(
     public val dynamicValue: DynamicString,
     public val fallbackValue: CharSequence,
diff --git a/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/TextTest.kt b/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/TextTest.kt
index 0af4fd4..6acc27c 100644
--- a/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/TextTest.kt
+++ b/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/TextTest.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.icu.util.TimeZone
+import android.os.Build
 import android.support.wearable.complications.ComplicationText as WireComplicationText
 import android.support.wearable.complications.ComplicationText.TimeDifferenceBuilder as WireTimeDifferenceBuilder
 import android.support.wearable.complications.ComplicationText.TimeFormatBuilder as WireTimeFormatBuilder
@@ -31,6 +32,7 @@
 import org.junit.Assert.assertNull
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.robolectric.annotation.Config
 
 @RunWith(SharedRobolectricTestRunner::class)
 public class AsWireComplicationTextTest {
@@ -124,6 +126,7 @@
     }
 
     @Test
+    @Config(minSdk = Build.VERSION_CODES.TIRAMISU)
     public fun dynamicText() {
         val text = DynamicComplicationText(DynamicString.constant("dynamic"), "fallback")
 
@@ -208,6 +211,7 @@
     }
 
     @Test
+    @Config(minSdk = Build.VERSION_CODES.TIRAMISU)
     public fun dynamicText() {
         val wireText = WireComplicationText("fallback", DynamicString.constant("dynamic"))
 
diff --git a/wear/watchface/watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/ComplicationDrawableTest.java b/wear/watchface/watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/ComplicationDrawableTest.java
index 5baf25b..1241de7 100644
--- a/wear/watchface/watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/ComplicationDrawableTest.java
+++ b/wear/watchface/watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/ComplicationDrawableTest.java
@@ -65,10 +65,12 @@
 import org.jetbrains.annotations.Nullable;
 import org.junit.Before;
 import org.junit.Ignore;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 import org.robolectric.Robolectric;
 import org.robolectric.annotation.internal.DoNotInstrument;
 
@@ -89,16 +91,16 @@
     private androidx.wear.watchface.complications.data.ComplicationData mComplicationData;
     private int mDefaultTextSize;
 
+    @Rule public final MockitoRule mocks = MockitoJUnit.rule();
+
     @Mock Canvas mMockCanvas;
     @Mock Drawable mMockDrawableActive;
     @Mock Drawable mMockDrawableAmbient;
     @Mock PendingIntent mMockPendingIntent;
     @Mock Drawable.Callback mMockDrawableCallback;
 
-    @SuppressWarnings("deprecation") // b/251211092
     @Before
     public void setUp() {
-        MockitoAnnotations.openMocks(this);
         mComplicationDrawable = new ComplicationDrawable();
         mComplicationDrawable.setCallback(mMockDrawableCallback);
 
diff --git a/wear/watchface/watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/ComplicationRendererTest.java b/wear/watchface/watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/ComplicationRendererTest.java
index 47dead1..5dfbd55 100644
--- a/wear/watchface/watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/ComplicationRendererTest.java
+++ b/wear/watchface/watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/ComplicationRendererTest.java
@@ -66,15 +66,16 @@
 import com.google.common.truth.Truth;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 import org.robolectric.annotation.internal.DoNotInstrument;
 
 import java.time.Instant;
-import java.util.ArrayList;
 
 /** Tests for {@link ComplicationRenderer}. */
 @RunWith(ComplicationsTestRunner.class)
@@ -93,6 +94,8 @@
     private ComplicationRenderer mComplicationRenderer;
     private Rect mComplicationBounds;
 
+    @Rule public final MockitoRule mocks = MockitoJUnit.rule();
+
     @Mock private Icon mMockIcon;
     @Mock private Icon mMockBurnInProtectionIcon;
     @Mock private Icon mMockSmallImage;
@@ -101,10 +104,8 @@
     @Mock private OnInvalidateListener mMockInvalidateListener;
     private final Resources mResurces = ApplicationProvider.getApplicationContext().getResources();
 
-    @SuppressWarnings("deprecation") // b/251211092
     @Before
     public void setUp() {
-        MockitoAnnotations.openMocks(this);
         mComplicationBounds = new Rect(0, 0, BOUNDS_WIDTH, BOUNDS_HEIGHT);
 
         mComplicationRenderer = createRendererWithBounds(mComplicationBounds);
@@ -443,51 +444,84 @@
     }
 
     @Test
-    public void rangedValueArcsAreDrawnCorrectly() {
+    public void rangedValueArcsAreDrawnCorrectly_middle() {
         float gap = ComplicationRenderer.STROKE_GAP_IN_DEGREES;
+        checkRangedValueArcsAreDrawnCorrectly(
+                new RangedArcsTestData(0, 100, 50, 180.0f - gap, 180.0f - gap, gap));
+    }
 
-        ArrayList<RangedArcsTestData> testDataSet = new ArrayList<RangedArcsTestData>();
-        testDataSet.add(new RangedArcsTestData(0, 100, 50, 180.0f - gap, 180.0f - gap, gap));
-        testDataSet.add(new RangedArcsTestData(0, 100, 0, 0.0f, 360.0f, 0.0f));
-        testDataSet.add(new RangedArcsTestData(0, 100, 100, 360.0f, 0.0f, 0.0f));
-        testDataSet.add(new RangedArcsTestData(0, 100, 25, 90.0f - gap, 270.0f - gap, gap));
-        testDataSet.add(new RangedArcsTestData(0, 100, 99, 356.4f - gap, 0.0f, gap));
-        testDataSet.add(new RangedArcsTestData(0, 100, 1, 0.0f, 356.4f - gap, gap));
-        testDataSet.add(new RangedArcsTestData(50, 100, 0, 0.0f, 360.0f, 0.0f));
-        testDataSet.add(new RangedArcsTestData(0, 50, 100, 360.0f, 0.0f, 0.0f));
-        testDataSet.add(new RangedArcsTestData(100, 200, 125, 90.0f - gap, 270.0f - gap, gap));
+    public void rangedValueArcsAreDrawnCorrectly_minimum() {
+        checkRangedValueArcsAreDrawnCorrectly(
+                new RangedArcsTestData(0, 100, 0, 0.0f, 360.0f, 0.0f));
+    }
 
-        for (RangedArcsTestData data : testDataSet) {
-            setUp();
+    public void rangedValueArcsAreDrawnCorrectly_maximum() {
+        checkRangedValueArcsAreDrawnCorrectly(
+                new RangedArcsTestData(0, 100, 100, 360.0f, 0.0f, 0.0f));
+    }
 
-            mComplicationRenderer.setComplicationData(
-                    new ComplicationData.Builder(TYPE_RANGED_VALUE)
-                            .setRangedValue(data.value)
-                            .setRangedMinValue(data.min)
-                            .setRangedMaxValue(data.max)
-                            .build(),
-                    true);
+    public void rangedValueArcsAreDrawnCorrectly_oneFourth() {
+        float gap = ComplicationRenderer.STROKE_GAP_IN_DEGREES;
+        checkRangedValueArcsAreDrawnCorrectly(
+                new RangedArcsTestData(0, 100, 25, 90.0f - gap, 270.0f - gap, gap));
+    }
 
-            mComplicationRenderer.draw(mMockCanvas, REFERENCE_TIME, false, false, false, false);
+    public void rangedValueArcsAreDrawnCorrectly_almostMaximum() {
+        float gap = ComplicationRenderer.STROKE_GAP_IN_DEGREES;
+        checkRangedValueArcsAreDrawnCorrectly(
+                new RangedArcsTestData(0, 100, 99, 356.4f - gap, 0.0f, gap));
+    }
 
-            float start = ComplicationRenderer.RANGED_VALUE_START_ANGLE;
+    public void rangedValueArcsAreDrawnCorrectly_almostMinimum() {
+        float gap = ComplicationRenderer.STROKE_GAP_IN_DEGREES;
+        checkRangedValueArcsAreDrawnCorrectly(
+                new RangedArcsTestData(0, 100, 1, 0.0f, 356.4f - gap, gap));
+    }
 
-            verify(mMockCanvas)
-                    .drawArc(
-                            any(),
-                            eq(start + data.gap / 2.0f),
-                            eq(data.progress),
-                            eq(false),
-                            any());
+    public void rangedValueArcsAreDrawnCorrectly_belowRange() {
+        checkRangedValueArcsAreDrawnCorrectly(
+                new RangedArcsTestData(50, 100, 0, 0.0f, 360.0f, 0.0f));
+    }
 
-            verify(mMockCanvas)
-                    .drawArc(
-                            any(),
-                            eq(start + data.gap / 2.0f + data.progress + data.gap),
-                            eq(data.remaining),
-                            eq(false),
-                            any());
-        }
+    public void rangedValueArcsAreDrawnCorrectly_aboveRange() {
+        checkRangedValueArcsAreDrawnCorrectly(
+                new RangedArcsTestData(0, 50, 100, 360.0f, 0.0f, 0.0f));
+    }
+
+    public void rangedValueArcsAreDrawnCorrectly_nonZeroBased() {
+        float gap = ComplicationRenderer.STROKE_GAP_IN_DEGREES;
+        checkRangedValueArcsAreDrawnCorrectly(
+                new RangedArcsTestData(100, 200, 125, 90.0f - gap, 270.0f - gap, gap));
+    }
+
+    private void checkRangedValueArcsAreDrawnCorrectly(RangedArcsTestData data) {
+        mComplicationRenderer.setComplicationData(
+                new ComplicationData.Builder(TYPE_RANGED_VALUE)
+                .setRangedValue(data.value)
+                .setRangedMinValue(data.min)
+                .setRangedMaxValue(data.max)
+                .build(),
+                true);
+
+        mComplicationRenderer.draw(mMockCanvas, REFERENCE_TIME, false, false, false, false);
+
+        float start = ComplicationRenderer.RANGED_VALUE_START_ANGLE;
+
+        verify(mMockCanvas)
+                .drawArc(
+                        any(),
+                        eq(start + data.gap / 2.0f),
+                        eq(data.progress),
+                        eq(false),
+                        any());
+
+        verify(mMockCanvas)
+                .drawArc(
+                        any(),
+                        eq(start + data.gap / 2.0f + data.progress + data.gap),
+                        eq(data.remaining),
+                        eq(false),
+                        any());
     }
 
     @Test
diff --git a/wear/watchface/watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/RoundedDrawableTest.java b/wear/watchface/watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/RoundedDrawableTest.java
index 3ffc761..d4d5393 100644
--- a/wear/watchface/watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/RoundedDrawableTest.java
+++ b/wear/watchface/watchface-complications-rendering/src/test/java/androidx/wear/watchface/complications/rendering/RoundedDrawableTest.java
@@ -33,10 +33,12 @@
 import androidx.test.core.app.ApplicationProvider;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 import org.robolectric.annotation.internal.DoNotInstrument;
 
 /** Tests for {@link RoundedDrawable} */
@@ -50,12 +52,12 @@
     private RoundedDrawable mRoundedDrawable;
     private BitmapDrawable mBitmapDrawable;
 
+    @Rule public final MockitoRule mocks = MockitoJUnit.rule();
+
     @Mock Canvas mMockCanvas;
 
-    @SuppressWarnings("deprecation") // b/251211092
     @Before
     public void setUp() {
-        MockitoAnnotations.openMocks(this);
         mRoundedDrawable = new RoundedDrawable();
         mBitmapDrawable =
                 new BitmapDrawable(
diff --git a/wear/watchface/watchface-complications/api/current.txt b/wear/watchface/watchface-complications/api/current.txt
index cb7f48e..ebc68f2 100644
--- a/wear/watchface/watchface-complications/api/current.txt
+++ b/wear/watchface/watchface-complications/api/current.txt
@@ -90,6 +90,7 @@
     field public static final int DATA_SOURCE_TIME_AND_DATE = 3; // 0x3
     field public static final int DATA_SOURCE_UNREAD_NOTIFICATION_COUNT = 7; // 0x7
     field public static final int DATA_SOURCE_WATCH_BATTERY = 1; // 0x1
+    field @RequiresApi(android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public static final int DATA_SOURCE_WEATHER = 17; // 0x11
     field public static final int DATA_SOURCE_WORLD_CLOCK = 5; // 0x5
     field public static final int NO_DATA_SOURCE = -1; // 0xffffffff
   }
diff --git a/wear/watchface/watchface-complications/api/restricted_current.txt b/wear/watchface/watchface-complications/api/restricted_current.txt
index cb7f48e..ebc68f2 100644
--- a/wear/watchface/watchface-complications/api/restricted_current.txt
+++ b/wear/watchface/watchface-complications/api/restricted_current.txt
@@ -90,6 +90,7 @@
     field public static final int DATA_SOURCE_TIME_AND_DATE = 3; // 0x3
     field public static final int DATA_SOURCE_UNREAD_NOTIFICATION_COUNT = 7; // 0x7
     field public static final int DATA_SOURCE_WATCH_BATTERY = 1; // 0x1
+    field @RequiresApi(android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public static final int DATA_SOURCE_WEATHER = 17; // 0x11
     field public static final int DATA_SOURCE_WORLD_CLOCK = 5; // 0x5
     field public static final int NO_DATA_SOURCE = -1; // 0xffffffff
   }
diff --git a/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/DefaultComplicationDataSourcePolicy.kt b/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/DefaultComplicationDataSourcePolicy.kt
index 1ae59f1..c142a0d2 100644
--- a/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/DefaultComplicationDataSourcePolicy.kt
+++ b/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/DefaultComplicationDataSourcePolicy.kt
@@ -355,6 +355,11 @@
             }
             val systemDataSourceFallback =
                 parser.getAttributeIntValue(NAMESPACE_APP, "systemDataSourceFallback", 0)
+            require(SystemDataSources.isAllowedOnDevice(systemDataSourceFallback)) {
+                "$nodeName at line ${parser.lineNumber} cannot have the supplied " +
+                    "systemDataSourceFallback value at the current API level."
+            }
+
             require(parser.hasValue("systemDataSourceFallbackDefaultType")) {
                 "A $nodeName must have a systemDataSourceFallbackDefaultType attribute"
             }
diff --git a/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/SystemDataSources.kt b/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/SystemDataSources.kt
index b3d620f..d0053b3 100644
--- a/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/SystemDataSources.kt
+++ b/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/SystemDataSources.kt
@@ -15,7 +15,9 @@
  */
 package androidx.wear.watchface.complications
 
+import android.os.Build
 import androidx.annotation.IntDef
+import androidx.annotation.RequiresApi
 import androidx.annotation.RestrictTo
 import androidx.wear.watchface.complications.data.ComplicationType
 
@@ -25,7 +27,7 @@
  */
 public class SystemDataSources private constructor() {
     public companion object {
-        // NEXT AVAILABLE DATA SOURCE ID: 17
+        // NEXT AVAILABLE DATA SOURCE ID: 18
 
         /** Specifies that no complication data source should be used. */
         public const val NO_DATA_SOURCE: Int = -1
@@ -177,6 +179,29 @@
          * This complication data source supports only [ComplicationType.SHORT_TEXT].
          */
         public const val DATA_SOURCE_DAY_AND_DATE: Int = 16
+
+        /**
+         * Id for the 'weather' complication complication data source.
+         *
+         * This is a safe complication data source, so if a watch face uses this as a default it
+         * will be able to receive data from it even before the RECEIVE_COMPLICATION_DATA permission
+         * has been granted.
+         *
+         * This complication data source supports the following types:
+         * [ComplicationType.SHORT_TEXT], [ComplicationType.LONG_TEXT],
+         * [ComplicationType.SMALL_IMAGE].
+         */
+        @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+        public const val DATA_SOURCE_WEATHER: Int = 17
+
+        /** Checks if the given data source is implemented by the device. */
+        internal fun isAllowedOnDevice(@DataSourceId systemDataSourceFallback: Int): Boolean {
+            return when {
+                systemDataSourceFallback == DATA_SOURCE_WEATHER &&
+                    Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE -> false
+                else -> true
+            }
+        }
     }
 
     /** System complication data source id as defined in [SystemDataSources]. */
@@ -193,7 +218,8 @@
         DATA_SOURCE_SUNRISE_SUNSET,
         DATA_SOURCE_DAY_OF_WEEK,
         DATA_SOURCE_FAVORITE_CONTACT,
-        DATA_SOURCE_DAY_AND_DATE
+        DATA_SOURCE_DAY_AND_DATE,
+        DATA_SOURCE_WEATHER,
     )
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     @Retention(AnnotationRetention.SOURCE)
diff --git a/wear/watchface/watchface/api/1.0.0-beta01.txt b/wear/watchface/watchface/api/1.0.0-beta01.txt
index 6f3baa0..c1c137e 100644
--- a/wear/watchface/watchface/api/1.0.0-beta01.txt
+++ b/wear/watchface/watchface/api/1.0.0-beta01.txt
@@ -95,9 +95,6 @@
     method public default void onComplicationSlotTapped(int complicationSlotId);
   }
 
-  public final class ComplicationSlotsManagerKt {
-  }
-
   public interface ComplicationTapFilter {
     method public boolean hitTest(androidx.wear.watchface.ComplicationSlot complicationSlot, android.graphics.Rect screenBounds, @Px int x, @Px int y);
   }
@@ -114,19 +111,18 @@
   }
 
   public enum DrawMode {
+    method public static androidx.wear.watchface.DrawMode valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.wear.watchface.DrawMode[] values();
     enum_constant public static final androidx.wear.watchface.DrawMode AMBIENT;
     enum_constant public static final androidx.wear.watchface.DrawMode INTERACTIVE;
     enum_constant public static final androidx.wear.watchface.DrawMode LOW_BATTERY_INTERACTIVE;
     enum_constant public static final androidx.wear.watchface.DrawMode MUTE;
   }
 
-  public final class RenderBufferTextureKt {
-  }
-
   public final class RenderParameters {
-    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers, optional androidx.wear.watchface.RenderParameters.HighlightLayer? highlightLayer, optional java.util.Map<java.lang.Integer,androidx.wear.watchface.TapEvent> lastComplicationTapDownEvents);
-    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers, optional androidx.wear.watchface.RenderParameters.HighlightLayer? highlightLayer);
     ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers);
+    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers, optional androidx.wear.watchface.RenderParameters.HighlightLayer? highlightLayer);
+    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers, optional androidx.wear.watchface.RenderParameters.HighlightLayer? highlightLayer, optional java.util.Map<java.lang.Integer,androidx.wear.watchface.TapEvent> lastComplicationTapDownEvents);
     method public androidx.wear.watchface.DrawMode getDrawMode();
     method public androidx.wear.watchface.RenderParameters.HighlightLayer? getHighlightLayer();
     method public java.util.Map<java.lang.Integer,androidx.wear.watchface.TapEvent> getLastComplicationTapDownEvents();
@@ -185,8 +181,8 @@
     method @UiThread public abstract void onDump(java.io.PrintWriter writer);
     method @UiThread protected void onRenderParametersChanged(androidx.wear.watchface.RenderParameters renderParameters);
     method public final void postInvalidate();
-    method public final void setAdditionalContentDescriptionLabels(java.util.Collection<kotlin.Pair<java.lang.Integer,androidx.wear.watchface.ContentDescriptionLabel>> value);
-    method public final void setInteractiveDrawModeUpdateDelayMillis(long interactiveDrawModeUpdateDelayMillis);
+    method public final void setAdditionalContentDescriptionLabels(java.util.Collection<kotlin.Pair<java.lang.Integer,androidx.wear.watchface.ContentDescriptionLabel>>);
+    method public final void setInteractiveDrawModeUpdateDelayMillis(long);
     method @UiThread public boolean shouldAnimate();
     property public final java.util.Collection<kotlin.Pair<java.lang.Integer,androidx.wear.watchface.ContentDescriptionLabel>> additionalContentDescriptionLabels;
     property public final float centerX;
@@ -198,30 +194,30 @@
   }
 
   public abstract static class Renderer.CanvasRenderer extends androidx.wear.watchface.Renderer {
-    ctor @WorkerThread public Renderer.CanvasRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, int canvasType, @IntRange(from=0, to=60000) long interactiveDrawModeUpdateDelayMillis);
-    method @UiThread public suspend Object? init(kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    ctor @WorkerThread public Renderer.CanvasRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, int canvasType, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis);
+    method @UiThread public suspend Object? init(kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public void onDump(java.io.PrintWriter writer);
     method @UiThread public abstract void render(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime);
     method @UiThread public abstract void renderHighlightLayer(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime);
   }
 
   public abstract static class Renderer.GlesRenderer extends androidx.wear.watchface.Renderer {
-    ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0, to=60000) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList, optional int[] eglSurfaceAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
-    ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0, to=60000) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
-    ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0, to=60000) long interactiveDrawModeUpdateDelayMillis) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
+    ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
+    ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
+    ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList, optional int[] eglSurfaceAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
     method public final android.opengl.EGLContext getEglBackgroundThreadContext();
     method public final android.opengl.EGLConfig getEglConfig();
     method public final android.opengl.EGLDisplay getEglDisplay();
     method public final android.opengl.EGLContext getEglUiThreadContext();
-    method @WorkerThread public suspend Object? onBackgroundThreadGlContextCreated(kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method @WorkerThread public suspend Object? onBackgroundThreadGlContextCreated(kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public void onDump(java.io.PrintWriter writer);
-    method @UiThread public suspend Object? onUiThreadGlSurfaceCreated(@Px int width, @Px int height, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method @UiThread public suspend Object? onUiThreadGlSurfaceCreated(@Px int width, @Px int height, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @UiThread public abstract void render(java.time.ZonedDateTime zonedDateTime);
     method @UiThread public abstract void renderHighlightLayer(java.time.ZonedDateTime zonedDateTime);
-    method @WorkerThread public final suspend Object? runBackgroundThreadGlCommands(kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> commands, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
-    method public final suspend Object? runUiThreadGlCommands(kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> commands, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
-    method public final void setEglConfig(android.opengl.EGLConfig eglConfig);
-    method public final void setEglDisplay(android.opengl.EGLDisplay eglDisplay);
+    method @WorkerThread public final suspend Object? runBackgroundThreadGlCommands(kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> commands, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public final suspend Object? runUiThreadGlCommands(kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> commands, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public final void setEglConfig(android.opengl.EGLConfig);
+    method public final void setEglDisplay(android.opengl.EGLDisplay);
     property public final android.opengl.EGLContext eglBackgroundThreadContext;
     property public final android.opengl.EGLConfig eglConfig;
     property public final android.opengl.EGLDisplay eglDisplay;
@@ -232,9 +228,6 @@
     ctor public Renderer.GlesRenderer.GlesException(String message);
   }
 
-  public final class RendererKt {
-  }
-
   public final class RoundRectComplicationTapFilter implements androidx.wear.watchface.ComplicationTapFilter {
     ctor public RoundRectComplicationTapFilter();
     method public boolean hitTest(androidx.wear.watchface.ComplicationSlot complicationSlot, android.graphics.Rect screenBounds, @Px int x, @Px int y);
@@ -260,7 +253,7 @@
     method public androidx.wear.watchface.WatchFace setLegacyWatchFaceStyle(androidx.wear.watchface.WatchFace.LegacyWatchFaceOverlayStyle legacyWatchFaceStyle);
     method public androidx.wear.watchface.WatchFace setOverridePreviewReferenceInstant(java.time.Instant previewReferenceTimeMillis);
     method public androidx.wear.watchface.WatchFace setTapListener(androidx.wear.watchface.WatchFace.TapListener? tapListener);
-    method public void setWatchFaceType(int watchFaceType);
+    method public void setWatchFaceType(int);
     property public final androidx.wear.watchface.WatchFace.LegacyWatchFaceOverlayStyle legacyWatchFaceStyle;
     property public final java.time.Instant? overridePreviewReferenceInstant;
     property public final androidx.wear.watchface.Renderer renderer;
@@ -273,8 +266,8 @@
   }
 
   public static final class WatchFace.LegacyWatchFaceOverlayStyle {
-    ctor public WatchFace.LegacyWatchFaceOverlayStyle(int viewProtectionMode, int statusBarGravity, boolean tapEventsAccepted, optional @ColorInt int accentColor);
     ctor public WatchFace.LegacyWatchFaceOverlayStyle(int viewProtectionMode, int statusBarGravity, boolean tapEventsAccepted);
+    ctor public WatchFace.LegacyWatchFaceOverlayStyle(int viewProtectionMode, int statusBarGravity, boolean tapEventsAccepted, optional @ColorInt int accentColor);
     method public int getAccentColor();
     method public int getStatusBarGravity();
     method public boolean getTapEventsAccepted();
@@ -289,14 +282,11 @@
     method @UiThread public void onTapEvent(int tapType, androidx.wear.watchface.TapEvent tapEvent, androidx.wear.watchface.ComplicationSlot? complicationSlot);
   }
 
-  public final class WatchFaceKt {
-  }
-
   public abstract class WatchFaceService extends android.service.wallpaper.WallpaperService {
     ctor public WatchFaceService();
     method @WorkerThread protected androidx.wear.watchface.ComplicationSlotsManager createComplicationSlotsManager(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository);
     method @WorkerThread protected androidx.wear.watchface.style.UserStyleSchema createUserStyleSchema();
-    method @WorkerThread protected abstract suspend Object? createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, kotlin.coroutines.Continuation<? super androidx.wear.watchface.WatchFace> p);
+    method @WorkerThread protected abstract suspend Object? createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, kotlin.coroutines.Continuation<? super androidx.wear.watchface.WatchFace>);
     method public final android.os.Handler getBackgroundThreadHandler();
     method public final android.os.Handler getUiThreadHandler();
     method public final android.service.wallpaper.WallpaperService.Engine onCreateEngine();
@@ -307,9 +297,6 @@
   public static final class WatchFaceService.Companion {
   }
 
-  public final class WatchFaceServiceKt {
-  }
-
   public final class WatchState {
     ctor public WatchState(kotlinx.coroutines.flow.StateFlow<java.lang.Integer> interruptionFilter, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isAmbient, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isBatteryLowAndNotCharging, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isVisible, boolean hasLowBitAmbient, boolean hasBurnInProtection, long analogPreviewReferenceTimeMillis, long digitalPreviewReferenceTimeMillis, @Px int chinHeight, boolean isHeadless);
     method public long getAnalogPreviewReferenceTimeMillis();
diff --git a/wear/watchface/watchface/api/1.1.0-beta01.txt b/wear/watchface/watchface/api/1.1.0-beta01.txt
index 027672b..40b1561 100644
--- a/wear/watchface/watchface/api/1.1.0-beta01.txt
+++ b/wear/watchface/watchface/api/1.1.0-beta01.txt
@@ -108,9 +108,6 @@
     method public default void onComplicationSlotTapped(int complicationSlotId);
   }
 
-  public final class ComplicationSlotsManagerKt {
-  }
-
   public interface ComplicationTapFilter {
     method public boolean hitTest(androidx.wear.watchface.ComplicationSlot complicationSlot, android.graphics.Rect screenBounds, @Px int x, @Px int y);
   }
@@ -127,19 +124,18 @@
   }
 
   public enum DrawMode {
+    method public static androidx.wear.watchface.DrawMode valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.wear.watchface.DrawMode[] values();
     enum_constant public static final androidx.wear.watchface.DrawMode AMBIENT;
     enum_constant public static final androidx.wear.watchface.DrawMode INTERACTIVE;
     enum_constant public static final androidx.wear.watchface.DrawMode LOW_BATTERY_INTERACTIVE;
     enum_constant public static final androidx.wear.watchface.DrawMode MUTE;
   }
 
-  public final class RenderBufferTextureKt {
-  }
-
   public final class RenderParameters {
-    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers, optional androidx.wear.watchface.RenderParameters.HighlightLayer? highlightLayer, optional java.util.Map<java.lang.Integer,androidx.wear.watchface.TapEvent> lastComplicationTapDownEvents);
-    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers, optional androidx.wear.watchface.RenderParameters.HighlightLayer? highlightLayer);
     ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers);
+    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers, optional androidx.wear.watchface.RenderParameters.HighlightLayer? highlightLayer);
+    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers, optional androidx.wear.watchface.RenderParameters.HighlightLayer? highlightLayer, optional java.util.Map<java.lang.Integer,androidx.wear.watchface.TapEvent> lastComplicationTapDownEvents);
     method public androidx.wear.watchface.DrawMode getDrawMode();
     method public androidx.wear.watchface.RenderParameters.HighlightLayer? getHighlightLayer();
     method public java.util.Map<java.lang.Integer,androidx.wear.watchface.TapEvent> getLastComplicationTapDownEvents();
@@ -213,29 +209,29 @@
   }
 
   @Deprecated public abstract static class Renderer.CanvasRenderer extends androidx.wear.watchface.Renderer {
-    ctor @Deprecated @WorkerThread public Renderer.CanvasRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, int canvasType, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional boolean clearWithBackgroundTintBeforeRenderingHighlightLayer);
     ctor @Deprecated @WorkerThread public Renderer.CanvasRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, int canvasType, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis);
+    ctor @Deprecated @WorkerThread public Renderer.CanvasRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, int canvasType, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional boolean clearWithBackgroundTintBeforeRenderingHighlightLayer);
     method @Deprecated public final boolean getClearWithBackgroundTintBeforeRenderingHighlightLayer();
     method @Deprecated @UiThread public suspend Object? init(kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @Deprecated public void onDump(java.io.PrintWriter writer);
     method @Deprecated @UiThread public abstract void render(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime);
     method @Deprecated @UiThread public abstract void renderHighlightLayer(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime);
-    property public final boolean clearWithBackgroundTintBeforeRenderingHighlightLayer;
+    property @Deprecated public final boolean clearWithBackgroundTintBeforeRenderingHighlightLayer;
   }
 
   public abstract static class Renderer.CanvasRenderer2<SharedAssetsT extends androidx.wear.watchface.Renderer.SharedAssets> extends androidx.wear.watchface.Renderer.CanvasRenderer {
     ctor @WorkerThread public Renderer.CanvasRenderer2(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, int canvasType, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, boolean clearWithBackgroundTintBeforeRenderingHighlightLayer);
     method @WorkerThread protected abstract suspend Object? createSharedAssets(kotlin.coroutines.Continuation<? super SharedAssetsT>);
-    method @UiThread public abstract void render(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
     method public final void render(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime);
-    method @UiThread public abstract void renderHighlightLayer(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
+    method @UiThread public abstract void render(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
     method public final void renderHighlightLayer(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime);
+    method @UiThread public abstract void renderHighlightLayer(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
   }
 
   @Deprecated public abstract static class Renderer.GlesRenderer extends androidx.wear.watchface.Renderer {
-    ctor @Deprecated @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList, optional int[] eglSurfaceAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
-    ctor @Deprecated @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
     ctor @Deprecated @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
+    ctor @Deprecated @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
+    ctor @Deprecated @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList, optional int[] eglSurfaceAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
     method @Deprecated public final android.opengl.EGLContext getEglBackgroundThreadContext();
     method @Deprecated public final android.opengl.EGLConfig getEglConfig();
     method @Deprecated public final android.opengl.EGLDisplay getEglDisplay();
@@ -249,10 +245,10 @@
     method @Deprecated public final suspend Object? runUiThreadGlCommands(kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> commands, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @Deprecated public final void setEglConfig(android.opengl.EGLConfig);
     method @Deprecated public final void setEglDisplay(android.opengl.EGLDisplay);
-    property public final android.opengl.EGLContext eglBackgroundThreadContext;
-    property public final android.opengl.EGLConfig eglConfig;
-    property public final android.opengl.EGLDisplay eglDisplay;
-    property public final android.opengl.EGLContext eglUiThreadContext;
+    property @Deprecated public final android.opengl.EGLContext eglBackgroundThreadContext;
+    property @Deprecated public final android.opengl.EGLConfig eglConfig;
+    property @Deprecated public final android.opengl.EGLDisplay eglDisplay;
+    property @Deprecated public final android.opengl.EGLContext eglUiThreadContext;
   }
 
   @Deprecated public static final class Renderer.GlesRenderer.GlesException extends java.lang.Exception {
@@ -260,23 +256,20 @@
   }
 
   public abstract static class Renderer.GlesRenderer2<SharedAssetsT extends androidx.wear.watchface.Renderer.SharedAssets> extends androidx.wear.watchface.Renderer.GlesRenderer {
-    ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer2(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList, optional int[] eglSurfaceAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
-    ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer2(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
     ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer2(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
+    ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer2(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
+    ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer2(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList, optional int[] eglSurfaceAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
     method @WorkerThread protected abstract suspend Object? createSharedAssets(kotlin.coroutines.Continuation<? super SharedAssetsT>);
-    method @UiThread public abstract void render(java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
     method public final void render(java.time.ZonedDateTime zonedDateTime);
-    method @UiThread public abstract void renderHighlightLayer(java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
+    method @UiThread public abstract void render(java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
     method public final void renderHighlightLayer(java.time.ZonedDateTime zonedDateTime);
+    method @UiThread public abstract void renderHighlightLayer(java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
   }
 
   public static interface Renderer.SharedAssets {
     method @UiThread public void onDestroy();
   }
 
-  public final class RendererKt {
-  }
-
   public final class RoundRectComplicationTapFilter implements androidx.wear.watchface.ComplicationTapFilter {
     ctor public RoundRectComplicationTapFilter();
     method public boolean hitTest(androidx.wear.watchface.ComplicationSlot complicationSlot, android.graphics.Rect screenBounds, @Px int x, @Px int y);
@@ -292,6 +285,24 @@
     property public final int yPos;
   }
 
+  @SuppressCompatibility @androidx.wear.watchface.WatchFaceFlavorsExperimental public final class UserStyleFlavor {
+    ctor public UserStyleFlavor(String id, androidx.wear.watchface.style.UserStyle style, java.util.Map<java.lang.Integer,androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy> complications);
+    ctor public UserStyleFlavor(String id, androidx.wear.watchface.style.UserStyleData style, java.util.Map<java.lang.Integer,androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy> complications);
+    method public java.util.Map<java.lang.Integer,androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy> getComplications();
+    method public String getId();
+    method public androidx.wear.watchface.style.UserStyleData getStyle();
+    property public final java.util.Map<java.lang.Integer,androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy> complications;
+    property public final String id;
+    property public final androidx.wear.watchface.style.UserStyleData style;
+  }
+
+  @SuppressCompatibility @androidx.wear.watchface.WatchFaceFlavorsExperimental public final class UserStyleFlavors {
+    ctor public UserStyleFlavors();
+    ctor public UserStyleFlavors(java.util.List<androidx.wear.watchface.UserStyleFlavor> flavors);
+    method public java.util.List<androidx.wear.watchface.UserStyleFlavor> getFlavors();
+    property public final java.util.List<androidx.wear.watchface.UserStyleFlavor> flavors;
+  }
+
   public final class WatchFace {
     ctor public WatchFace(int watchFaceType, androidx.wear.watchface.Renderer renderer);
     method public androidx.wear.watchface.WatchFace.LegacyWatchFaceOverlayStyle getLegacyWatchFaceStyle();
@@ -320,8 +331,8 @@
   }
 
   public static final class WatchFace.LegacyWatchFaceOverlayStyle {
-    ctor public WatchFace.LegacyWatchFaceOverlayStyle(int viewProtectionMode, int statusBarGravity, boolean tapEventsAccepted, optional @ColorInt int accentColor);
     ctor public WatchFace.LegacyWatchFaceOverlayStyle(int viewProtectionMode, int statusBarGravity, boolean tapEventsAccepted);
+    ctor public WatchFace.LegacyWatchFaceOverlayStyle(int viewProtectionMode, int statusBarGravity, boolean tapEventsAccepted, optional @ColorInt int accentColor);
     method public int getAccentColor();
     method public int getStatusBarGravity();
     method public boolean getTapEventsAccepted();
@@ -333,8 +344,8 @@
   }
 
   public static final class WatchFace.OverlayStyle {
-    ctor public WatchFace.OverlayStyle(android.graphics.Color? backgroundColor, android.graphics.Color? foregroundColor);
     ctor public WatchFace.OverlayStyle();
+    ctor public WatchFace.OverlayStyle(android.graphics.Color? backgroundColor, android.graphics.Color? foregroundColor);
     method public android.graphics.Color? getBackgroundColor();
     method public android.graphics.Color? getForegroundColor();
     property public final android.graphics.Color? backgroundColor;
@@ -345,12 +356,13 @@
     method @UiThread public void onTapEvent(int tapType, androidx.wear.watchface.TapEvent tapEvent, androidx.wear.watchface.ComplicationSlot? complicationSlot);
   }
 
-  public final class WatchFaceKt {
+  @SuppressCompatibility @kotlin.RequiresOptIn(message="This is an experimental API that may change or be removed without warning.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface WatchFaceFlavorsExperimental {
   }
 
   public abstract class WatchFaceService extends android.service.wallpaper.WallpaperService {
     ctor public WatchFaceService();
     method @WorkerThread protected androidx.wear.watchface.ComplicationSlotsManager createComplicationSlotsManager(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository);
+    method @SuppressCompatibility @WorkerThread @androidx.wear.watchface.WatchFaceFlavorsExperimental protected androidx.wear.watchface.UserStyleFlavors createUserStyleFlavors(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager);
     method @WorkerThread protected androidx.wear.watchface.style.UserStyleSchema createUserStyleSchema();
     method @WorkerThread protected abstract suspend Object? createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, kotlin.coroutines.Continuation<? super androidx.wear.watchface.WatchFace>);
     method public final android.os.Handler getBackgroundThreadHandler();
@@ -364,12 +376,9 @@
   public static final class WatchFaceService.Companion {
   }
 
-  public final class WatchFaceServiceKt {
-  }
-
   public final class WatchState {
-    ctor public WatchState(kotlinx.coroutines.flow.StateFlow<java.lang.Integer> interruptionFilter, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isAmbient, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isBatteryLowAndNotCharging, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isVisible, boolean hasLowBitAmbient, boolean hasBurnInProtection, long analogPreviewReferenceTimeMillis, long digitalPreviewReferenceTimeMillis, @Px int chinHeight, boolean isHeadless, kotlinx.coroutines.flow.StateFlow<java.lang.String> watchFaceInstanceId);
     ctor @Deprecated public WatchState(kotlinx.coroutines.flow.StateFlow<java.lang.Integer> interruptionFilter, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isAmbient, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isBatteryLowAndNotCharging, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isVisible, boolean hasLowBitAmbient, boolean hasBurnInProtection, long analogPreviewReferenceTimeMillis, long digitalPreviewReferenceTimeMillis, int chinHeight, boolean isHeadless);
+    ctor public WatchState(kotlinx.coroutines.flow.StateFlow<java.lang.Integer> interruptionFilter, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isAmbient, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isBatteryLowAndNotCharging, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isVisible, boolean hasLowBitAmbient, boolean hasBurnInProtection, long analogPreviewReferenceTimeMillis, long digitalPreviewReferenceTimeMillis, @Px int chinHeight, boolean isHeadless, kotlinx.coroutines.flow.StateFlow<java.lang.String> watchFaceInstanceId);
     method public long getAnalogPreviewReferenceTimeMillis();
     method @Px public int getChinHeight();
     method public long getDigitalPreviewReferenceTimeMillis();
diff --git a/wear/watchface/watchface/api/1.1.0-beta02.txt b/wear/watchface/watchface/api/1.1.0-beta02.txt
index 027672b..250bbf5 100644
--- a/wear/watchface/watchface/api/1.1.0-beta02.txt
+++ b/wear/watchface/watchface/api/1.1.0-beta02.txt
@@ -6,6 +6,20 @@
     method public boolean hitTest(androidx.wear.watchface.ComplicationSlot complicationSlot, android.graphics.Rect screenBounds, @Px int x, @Px int y);
   }
 
+  @SuppressCompatibility @androidx.wear.watchface.complications.data.ComplicationExperimental public final class BoundingArc {
+    ctor public BoundingArc(float startAngle, float totalAngle, @Px float thickness);
+    method public float getStartAngle();
+    method public float getThickness();
+    method public float getTotalAngle();
+    method public boolean hitTest(android.graphics.Rect rect, @Px float x, @Px float y);
+    method public void setStartAngle(float);
+    method public void setThickness(float);
+    method public void setTotalAngle(float);
+    property public final float startAngle;
+    property public final float thickness;
+    property public final float totalAngle;
+  }
+
   public interface CanvasComplication {
     method public void drawHighlight(android.graphics.Canvas canvas, android.graphics.Rect bounds, int boundsType, java.time.ZonedDateTime zonedDateTime, @ColorInt int color);
     method public androidx.wear.watchface.complications.data.ComplicationData getData();
@@ -25,6 +39,8 @@
   public final class ComplicationSlot {
     method public android.graphics.Rect computeBounds(android.graphics.Rect screen);
     method public static androidx.wear.watchface.ComplicationSlot.Builder createBackgroundComplicationSlotBuilder(int id, androidx.wear.watchface.CanvasComplicationFactory canvasComplicationFactory, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy);
+    method @SuppressCompatibility @androidx.wear.watchface.complications.data.ComplicationExperimental public static androidx.wear.watchface.ComplicationSlot.Builder createEdgeComplicationSlotBuilder(int id, androidx.wear.watchface.CanvasComplicationFactory canvasComplicationFactory, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, androidx.wear.watchface.complications.ComplicationSlotBounds bounds, androidx.wear.watchface.BoundingArc boundingArc);
+    method @SuppressCompatibility @androidx.wear.watchface.complications.data.ComplicationExperimental public static androidx.wear.watchface.ComplicationSlot.Builder createEdgeComplicationSlotBuilder(int id, androidx.wear.watchface.CanvasComplicationFactory canvasComplicationFactory, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, androidx.wear.watchface.complications.ComplicationSlotBounds bounds, androidx.wear.watchface.BoundingArc boundingArc, optional androidx.wear.watchface.ComplicationTapFilter complicationTapFilter);
     method public static androidx.wear.watchface.ComplicationSlot.Builder createEdgeComplicationSlotBuilder(int id, androidx.wear.watchface.CanvasComplicationFactory canvasComplicationFactory, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, androidx.wear.watchface.complications.ComplicationSlotBounds bounds, androidx.wear.watchface.ComplicationTapFilter complicationTapFilter);
     method public static androidx.wear.watchface.ComplicationSlot.Builder createRoundRectComplicationSlotBuilder(int id, androidx.wear.watchface.CanvasComplicationFactory canvasComplicationFactory, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, androidx.wear.watchface.complications.ComplicationSlotBounds bounds);
     method @UiThread public int getAccessibilityTraversalIndex();
@@ -81,6 +97,8 @@
 
   public static final class ComplicationSlot.Companion {
     method public androidx.wear.watchface.ComplicationSlot.Builder createBackgroundComplicationSlotBuilder(int id, androidx.wear.watchface.CanvasComplicationFactory canvasComplicationFactory, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy);
+    method @SuppressCompatibility @androidx.wear.watchface.complications.data.ComplicationExperimental public androidx.wear.watchface.ComplicationSlot.Builder createEdgeComplicationSlotBuilder(int id, androidx.wear.watchface.CanvasComplicationFactory canvasComplicationFactory, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, androidx.wear.watchface.complications.ComplicationSlotBounds bounds, androidx.wear.watchface.BoundingArc boundingArc);
+    method @SuppressCompatibility @androidx.wear.watchface.complications.data.ComplicationExperimental public androidx.wear.watchface.ComplicationSlot.Builder createEdgeComplicationSlotBuilder(int id, androidx.wear.watchface.CanvasComplicationFactory canvasComplicationFactory, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, androidx.wear.watchface.complications.ComplicationSlotBounds bounds, androidx.wear.watchface.BoundingArc boundingArc, optional androidx.wear.watchface.ComplicationTapFilter complicationTapFilter);
     method public androidx.wear.watchface.ComplicationSlot.Builder createEdgeComplicationSlotBuilder(int id, androidx.wear.watchface.CanvasComplicationFactory canvasComplicationFactory, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, androidx.wear.watchface.complications.ComplicationSlotBounds bounds, androidx.wear.watchface.ComplicationTapFilter complicationTapFilter);
     method public androidx.wear.watchface.ComplicationSlot.Builder createRoundRectComplicationSlotBuilder(int id, androidx.wear.watchface.CanvasComplicationFactory canvasComplicationFactory, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, androidx.wear.watchface.complications.ComplicationSlotBounds bounds);
   }
@@ -108,9 +126,6 @@
     method public default void onComplicationSlotTapped(int complicationSlotId);
   }
 
-  public final class ComplicationSlotsManagerKt {
-  }
-
   public interface ComplicationTapFilter {
     method public boolean hitTest(androidx.wear.watchface.ComplicationSlot complicationSlot, android.graphics.Rect screenBounds, @Px int x, @Px int y);
   }
@@ -127,19 +142,18 @@
   }
 
   public enum DrawMode {
+    method public static androidx.wear.watchface.DrawMode valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.wear.watchface.DrawMode[] values();
     enum_constant public static final androidx.wear.watchface.DrawMode AMBIENT;
     enum_constant public static final androidx.wear.watchface.DrawMode INTERACTIVE;
     enum_constant public static final androidx.wear.watchface.DrawMode LOW_BATTERY_INTERACTIVE;
     enum_constant public static final androidx.wear.watchface.DrawMode MUTE;
   }
 
-  public final class RenderBufferTextureKt {
-  }
-
   public final class RenderParameters {
-    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers, optional androidx.wear.watchface.RenderParameters.HighlightLayer? highlightLayer, optional java.util.Map<java.lang.Integer,androidx.wear.watchface.TapEvent> lastComplicationTapDownEvents);
-    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers, optional androidx.wear.watchface.RenderParameters.HighlightLayer? highlightLayer);
     ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers);
+    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers, optional androidx.wear.watchface.RenderParameters.HighlightLayer? highlightLayer);
+    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers, optional androidx.wear.watchface.RenderParameters.HighlightLayer? highlightLayer, optional java.util.Map<java.lang.Integer,androidx.wear.watchface.TapEvent> lastComplicationTapDownEvents);
     method public androidx.wear.watchface.DrawMode getDrawMode();
     method public androidx.wear.watchface.RenderParameters.HighlightLayer? getHighlightLayer();
     method public java.util.Map<java.lang.Integer,androidx.wear.watchface.TapEvent> getLastComplicationTapDownEvents();
@@ -213,29 +227,29 @@
   }
 
   @Deprecated public abstract static class Renderer.CanvasRenderer extends androidx.wear.watchface.Renderer {
-    ctor @Deprecated @WorkerThread public Renderer.CanvasRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, int canvasType, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional boolean clearWithBackgroundTintBeforeRenderingHighlightLayer);
     ctor @Deprecated @WorkerThread public Renderer.CanvasRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, int canvasType, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis);
+    ctor @Deprecated @WorkerThread public Renderer.CanvasRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, int canvasType, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional boolean clearWithBackgroundTintBeforeRenderingHighlightLayer);
     method @Deprecated public final boolean getClearWithBackgroundTintBeforeRenderingHighlightLayer();
     method @Deprecated @UiThread public suspend Object? init(kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @Deprecated public void onDump(java.io.PrintWriter writer);
     method @Deprecated @UiThread public abstract void render(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime);
     method @Deprecated @UiThread public abstract void renderHighlightLayer(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime);
-    property public final boolean clearWithBackgroundTintBeforeRenderingHighlightLayer;
+    property @Deprecated public final boolean clearWithBackgroundTintBeforeRenderingHighlightLayer;
   }
 
   public abstract static class Renderer.CanvasRenderer2<SharedAssetsT extends androidx.wear.watchface.Renderer.SharedAssets> extends androidx.wear.watchface.Renderer.CanvasRenderer {
     ctor @WorkerThread public Renderer.CanvasRenderer2(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, int canvasType, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, boolean clearWithBackgroundTintBeforeRenderingHighlightLayer);
     method @WorkerThread protected abstract suspend Object? createSharedAssets(kotlin.coroutines.Continuation<? super SharedAssetsT>);
-    method @UiThread public abstract void render(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
     method public final void render(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime);
-    method @UiThread public abstract void renderHighlightLayer(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
+    method @UiThread public abstract void render(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
     method public final void renderHighlightLayer(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime);
+    method @UiThread public abstract void renderHighlightLayer(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
   }
 
   @Deprecated public abstract static class Renderer.GlesRenderer extends androidx.wear.watchface.Renderer {
-    ctor @Deprecated @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList, optional int[] eglSurfaceAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
-    ctor @Deprecated @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
     ctor @Deprecated @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
+    ctor @Deprecated @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
+    ctor @Deprecated @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList, optional int[] eglSurfaceAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
     method @Deprecated public final android.opengl.EGLContext getEglBackgroundThreadContext();
     method @Deprecated public final android.opengl.EGLConfig getEglConfig();
     method @Deprecated public final android.opengl.EGLDisplay getEglDisplay();
@@ -249,10 +263,10 @@
     method @Deprecated public final suspend Object? runUiThreadGlCommands(kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> commands, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @Deprecated public final void setEglConfig(android.opengl.EGLConfig);
     method @Deprecated public final void setEglDisplay(android.opengl.EGLDisplay);
-    property public final android.opengl.EGLContext eglBackgroundThreadContext;
-    property public final android.opengl.EGLConfig eglConfig;
-    property public final android.opengl.EGLDisplay eglDisplay;
-    property public final android.opengl.EGLContext eglUiThreadContext;
+    property @Deprecated public final android.opengl.EGLContext eglBackgroundThreadContext;
+    property @Deprecated public final android.opengl.EGLConfig eglConfig;
+    property @Deprecated public final android.opengl.EGLDisplay eglDisplay;
+    property @Deprecated public final android.opengl.EGLContext eglUiThreadContext;
   }
 
   @Deprecated public static final class Renderer.GlesRenderer.GlesException extends java.lang.Exception {
@@ -260,23 +274,20 @@
   }
 
   public abstract static class Renderer.GlesRenderer2<SharedAssetsT extends androidx.wear.watchface.Renderer.SharedAssets> extends androidx.wear.watchface.Renderer.GlesRenderer {
-    ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer2(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList, optional int[] eglSurfaceAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
-    ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer2(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
     ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer2(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
+    ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer2(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
+    ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer2(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList, optional int[] eglSurfaceAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
     method @WorkerThread protected abstract suspend Object? createSharedAssets(kotlin.coroutines.Continuation<? super SharedAssetsT>);
-    method @UiThread public abstract void render(java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
     method public final void render(java.time.ZonedDateTime zonedDateTime);
-    method @UiThread public abstract void renderHighlightLayer(java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
+    method @UiThread public abstract void render(java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
     method public final void renderHighlightLayer(java.time.ZonedDateTime zonedDateTime);
+    method @UiThread public abstract void renderHighlightLayer(java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
   }
 
   public static interface Renderer.SharedAssets {
     method @UiThread public void onDestroy();
   }
 
-  public final class RendererKt {
-  }
-
   public final class RoundRectComplicationTapFilter implements androidx.wear.watchface.ComplicationTapFilter {
     ctor public RoundRectComplicationTapFilter();
     method public boolean hitTest(androidx.wear.watchface.ComplicationSlot complicationSlot, android.graphics.Rect screenBounds, @Px int x, @Px int y);
@@ -292,6 +303,24 @@
     property public final int yPos;
   }
 
+  @SuppressCompatibility @androidx.wear.watchface.WatchFaceFlavorsExperimental public final class UserStyleFlavor {
+    ctor public UserStyleFlavor(String id, androidx.wear.watchface.style.UserStyle style, java.util.Map<java.lang.Integer,androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy> complications);
+    ctor public UserStyleFlavor(String id, androidx.wear.watchface.style.UserStyleData style, java.util.Map<java.lang.Integer,androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy> complications);
+    method public java.util.Map<java.lang.Integer,androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy> getComplications();
+    method public String getId();
+    method public androidx.wear.watchface.style.UserStyleData getStyle();
+    property public final java.util.Map<java.lang.Integer,androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy> complications;
+    property public final String id;
+    property public final androidx.wear.watchface.style.UserStyleData style;
+  }
+
+  @SuppressCompatibility @androidx.wear.watchface.WatchFaceFlavorsExperimental public final class UserStyleFlavors {
+    ctor public UserStyleFlavors();
+    ctor public UserStyleFlavors(java.util.List<androidx.wear.watchface.UserStyleFlavor> flavors);
+    method public java.util.List<androidx.wear.watchface.UserStyleFlavor> getFlavors();
+    property public final java.util.List<androidx.wear.watchface.UserStyleFlavor> flavors;
+  }
+
   public final class WatchFace {
     ctor public WatchFace(int watchFaceType, androidx.wear.watchface.Renderer renderer);
     method public androidx.wear.watchface.WatchFace.LegacyWatchFaceOverlayStyle getLegacyWatchFaceStyle();
@@ -320,8 +349,8 @@
   }
 
   public static final class WatchFace.LegacyWatchFaceOverlayStyle {
-    ctor public WatchFace.LegacyWatchFaceOverlayStyle(int viewProtectionMode, int statusBarGravity, boolean tapEventsAccepted, optional @ColorInt int accentColor);
     ctor public WatchFace.LegacyWatchFaceOverlayStyle(int viewProtectionMode, int statusBarGravity, boolean tapEventsAccepted);
+    ctor public WatchFace.LegacyWatchFaceOverlayStyle(int viewProtectionMode, int statusBarGravity, boolean tapEventsAccepted, optional @ColorInt int accentColor);
     method public int getAccentColor();
     method public int getStatusBarGravity();
     method public boolean getTapEventsAccepted();
@@ -333,8 +362,8 @@
   }
 
   public static final class WatchFace.OverlayStyle {
-    ctor public WatchFace.OverlayStyle(android.graphics.Color? backgroundColor, android.graphics.Color? foregroundColor);
     ctor public WatchFace.OverlayStyle();
+    ctor public WatchFace.OverlayStyle(android.graphics.Color? backgroundColor, android.graphics.Color? foregroundColor);
     method public android.graphics.Color? getBackgroundColor();
     method public android.graphics.Color? getForegroundColor();
     property public final android.graphics.Color? backgroundColor;
@@ -345,12 +374,13 @@
     method @UiThread public void onTapEvent(int tapType, androidx.wear.watchface.TapEvent tapEvent, androidx.wear.watchface.ComplicationSlot? complicationSlot);
   }
 
-  public final class WatchFaceKt {
+  @SuppressCompatibility @kotlin.RequiresOptIn(message="This is an experimental API that may change or be removed without warning.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface WatchFaceFlavorsExperimental {
   }
 
   public abstract class WatchFaceService extends android.service.wallpaper.WallpaperService {
     ctor public WatchFaceService();
     method @WorkerThread protected androidx.wear.watchface.ComplicationSlotsManager createComplicationSlotsManager(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository);
+    method @SuppressCompatibility @WorkerThread @androidx.wear.watchface.WatchFaceFlavorsExperimental protected androidx.wear.watchface.UserStyleFlavors createUserStyleFlavors(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager);
     method @WorkerThread protected androidx.wear.watchface.style.UserStyleSchema createUserStyleSchema();
     method @WorkerThread protected abstract suspend Object? createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, kotlin.coroutines.Continuation<? super androidx.wear.watchface.WatchFace>);
     method public final android.os.Handler getBackgroundThreadHandler();
@@ -364,12 +394,9 @@
   public static final class WatchFaceService.Companion {
   }
 
-  public final class WatchFaceServiceKt {
-  }
-
   public final class WatchState {
-    ctor public WatchState(kotlinx.coroutines.flow.StateFlow<java.lang.Integer> interruptionFilter, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isAmbient, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isBatteryLowAndNotCharging, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isVisible, boolean hasLowBitAmbient, boolean hasBurnInProtection, long analogPreviewReferenceTimeMillis, long digitalPreviewReferenceTimeMillis, @Px int chinHeight, boolean isHeadless, kotlinx.coroutines.flow.StateFlow<java.lang.String> watchFaceInstanceId);
     ctor @Deprecated public WatchState(kotlinx.coroutines.flow.StateFlow<java.lang.Integer> interruptionFilter, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isAmbient, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isBatteryLowAndNotCharging, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isVisible, boolean hasLowBitAmbient, boolean hasBurnInProtection, long analogPreviewReferenceTimeMillis, long digitalPreviewReferenceTimeMillis, int chinHeight, boolean isHeadless);
+    ctor public WatchState(kotlinx.coroutines.flow.StateFlow<java.lang.Integer> interruptionFilter, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isAmbient, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isBatteryLowAndNotCharging, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isVisible, boolean hasLowBitAmbient, boolean hasBurnInProtection, long analogPreviewReferenceTimeMillis, long digitalPreviewReferenceTimeMillis, @Px int chinHeight, boolean isHeadless, kotlinx.coroutines.flow.StateFlow<java.lang.String> watchFaceInstanceId);
     method public long getAnalogPreviewReferenceTimeMillis();
     method @Px public int getChinHeight();
     method public long getDigitalPreviewReferenceTimeMillis();
diff --git a/wear/watchface/watchface/api/restricted_1.0.0-beta01.txt b/wear/watchface/watchface/api/restricted_1.0.0-beta01.txt
index d6a7a5c..25a8925 100644
--- a/wear/watchface/watchface/api/restricted_1.0.0-beta01.txt
+++ b/wear/watchface/watchface/api/restricted_1.0.0-beta01.txt
@@ -95,9 +95,6 @@
     method public default void onComplicationSlotTapped(int complicationSlotId);
   }
 
-  public final class ComplicationSlotsManagerKt {
-  }
-
   public interface ComplicationTapFilter {
     method public boolean hitTest(androidx.wear.watchface.ComplicationSlot complicationSlot, android.graphics.Rect screenBounds, @Px int x, @Px int y);
   }
@@ -114,20 +111,19 @@
   }
 
   public enum DrawMode {
+    method public static androidx.wear.watchface.DrawMode valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.wear.watchface.DrawMode[] values();
     enum_constant public static final androidx.wear.watchface.DrawMode AMBIENT;
     enum_constant public static final androidx.wear.watchface.DrawMode INTERACTIVE;
     enum_constant public static final androidx.wear.watchface.DrawMode LOW_BATTERY_INTERACTIVE;
     enum_constant public static final androidx.wear.watchface.DrawMode MUTE;
   }
 
-  public final class RenderBufferTextureKt {
-  }
-
   public final class RenderParameters {
-    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers, optional androidx.wear.watchface.RenderParameters.HighlightLayer? highlightLayer, optional java.util.Map<java.lang.Integer,androidx.wear.watchface.TapEvent> lastComplicationTapDownEvents);
-    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers, optional androidx.wear.watchface.RenderParameters.HighlightLayer? highlightLayer);
-    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers);
     ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public RenderParameters(androidx.wear.watchface.data.RenderParametersWireFormat wireFormat);
+    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers);
+    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers, optional androidx.wear.watchface.RenderParameters.HighlightLayer? highlightLayer);
+    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers, optional androidx.wear.watchface.RenderParameters.HighlightLayer? highlightLayer, optional java.util.Map<java.lang.Integer,androidx.wear.watchface.TapEvent> lastComplicationTapDownEvents);
     method public androidx.wear.watchface.DrawMode getDrawMode();
     method public androidx.wear.watchface.RenderParameters.HighlightLayer? getHighlightLayer();
     method public java.util.Map<java.lang.Integer,androidx.wear.watchface.TapEvent> getLastComplicationTapDownEvents();
@@ -187,8 +183,8 @@
     method @UiThread public abstract void onDump(java.io.PrintWriter writer);
     method @UiThread protected void onRenderParametersChanged(androidx.wear.watchface.RenderParameters renderParameters);
     method public final void postInvalidate();
-    method public final void setAdditionalContentDescriptionLabels(java.util.Collection<kotlin.Pair<java.lang.Integer,androidx.wear.watchface.ContentDescriptionLabel>> value);
-    method public final void setInteractiveDrawModeUpdateDelayMillis(long interactiveDrawModeUpdateDelayMillis);
+    method public final void setAdditionalContentDescriptionLabels(java.util.Collection<kotlin.Pair<java.lang.Integer,androidx.wear.watchface.ContentDescriptionLabel>>);
+    method public final void setInteractiveDrawModeUpdateDelayMillis(long);
     method @UiThread public boolean shouldAnimate();
     property public final java.util.Collection<kotlin.Pair<java.lang.Integer,androidx.wear.watchface.ContentDescriptionLabel>> additionalContentDescriptionLabels;
     property public final float centerX;
@@ -200,30 +196,30 @@
   }
 
   public abstract static class Renderer.CanvasRenderer extends androidx.wear.watchface.Renderer {
-    ctor @WorkerThread public Renderer.CanvasRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, int canvasType, @IntRange(from=0, to=60000) long interactiveDrawModeUpdateDelayMillis);
-    method @UiThread public suspend Object? init(kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    ctor @WorkerThread public Renderer.CanvasRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, int canvasType, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis);
+    method @UiThread public suspend Object? init(kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public void onDump(java.io.PrintWriter writer);
     method @UiThread public abstract void render(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime);
     method @UiThread public abstract void renderHighlightLayer(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime);
   }
 
   public abstract static class Renderer.GlesRenderer extends androidx.wear.watchface.Renderer {
-    ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0, to=60000) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList, optional int[] eglSurfaceAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
-    ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0, to=60000) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
-    ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0, to=60000) long interactiveDrawModeUpdateDelayMillis) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
+    ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
+    ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
+    ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList, optional int[] eglSurfaceAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
     method public final android.opengl.EGLContext getEglBackgroundThreadContext();
     method public final android.opengl.EGLConfig getEglConfig();
     method public final android.opengl.EGLDisplay getEglDisplay();
     method public final android.opengl.EGLContext getEglUiThreadContext();
-    method @WorkerThread public suspend Object? onBackgroundThreadGlContextCreated(kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method @WorkerThread public suspend Object? onBackgroundThreadGlContextCreated(kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method public void onDump(java.io.PrintWriter writer);
-    method @UiThread public suspend Object? onUiThreadGlSurfaceCreated(@Px int width, @Px int height, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+    method @UiThread public suspend Object? onUiThreadGlSurfaceCreated(@Px int width, @Px int height, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @UiThread public abstract void render(java.time.ZonedDateTime zonedDateTime);
     method @UiThread public abstract void renderHighlightLayer(java.time.ZonedDateTime zonedDateTime);
-    method @WorkerThread public final suspend Object? runBackgroundThreadGlCommands(kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> commands, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
-    method public final suspend Object? runUiThreadGlCommands(kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> commands, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
-    method public final void setEglConfig(android.opengl.EGLConfig eglConfig);
-    method public final void setEglDisplay(android.opengl.EGLDisplay eglDisplay);
+    method @WorkerThread public final suspend Object? runBackgroundThreadGlCommands(kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> commands, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public final suspend Object? runUiThreadGlCommands(kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> commands, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+    method public final void setEglConfig(android.opengl.EGLConfig);
+    method public final void setEglDisplay(android.opengl.EGLDisplay);
     property public final android.opengl.EGLContext eglBackgroundThreadContext;
     property public final android.opengl.EGLConfig eglConfig;
     property public final android.opengl.EGLDisplay eglDisplay;
@@ -234,9 +230,6 @@
     ctor public Renderer.GlesRenderer.GlesException(String message);
   }
 
-  public final class RendererKt {
-  }
-
   public final class RoundRectComplicationTapFilter implements androidx.wear.watchface.ComplicationTapFilter {
     ctor public RoundRectComplicationTapFilter();
     method public boolean hitTest(androidx.wear.watchface.ComplicationSlot complicationSlot, android.graphics.Rect screenBounds, @Px int x, @Px int y);
@@ -262,7 +255,7 @@
     method public androidx.wear.watchface.WatchFace setLegacyWatchFaceStyle(androidx.wear.watchface.WatchFace.LegacyWatchFaceOverlayStyle legacyWatchFaceStyle);
     method public androidx.wear.watchface.WatchFace setOverridePreviewReferenceInstant(java.time.Instant previewReferenceTimeMillis);
     method public androidx.wear.watchface.WatchFace setTapListener(androidx.wear.watchface.WatchFace.TapListener? tapListener);
-    method public void setWatchFaceType(int watchFaceType);
+    method public void setWatchFaceType(int);
     property public final androidx.wear.watchface.WatchFace.LegacyWatchFaceOverlayStyle legacyWatchFaceStyle;
     property public final java.time.Instant? overridePreviewReferenceInstant;
     property public final androidx.wear.watchface.Renderer renderer;
@@ -275,8 +268,8 @@
   }
 
   public static final class WatchFace.LegacyWatchFaceOverlayStyle {
-    ctor public WatchFace.LegacyWatchFaceOverlayStyle(int viewProtectionMode, int statusBarGravity, boolean tapEventsAccepted, optional @ColorInt int accentColor);
     ctor public WatchFace.LegacyWatchFaceOverlayStyle(int viewProtectionMode, int statusBarGravity, boolean tapEventsAccepted);
+    ctor public WatchFace.LegacyWatchFaceOverlayStyle(int viewProtectionMode, int statusBarGravity, boolean tapEventsAccepted, optional @ColorInt int accentColor);
     method public int getAccentColor();
     method public int getStatusBarGravity();
     method public boolean getTapEventsAccepted();
@@ -291,14 +284,11 @@
     method @UiThread public void onTapEvent(int tapType, androidx.wear.watchface.TapEvent tapEvent, androidx.wear.watchface.ComplicationSlot? complicationSlot);
   }
 
-  public final class WatchFaceKt {
-  }
-
   public abstract class WatchFaceService extends android.service.wallpaper.WallpaperService {
     ctor public WatchFaceService();
     method @WorkerThread protected androidx.wear.watchface.ComplicationSlotsManager createComplicationSlotsManager(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository);
     method @WorkerThread protected androidx.wear.watchface.style.UserStyleSchema createUserStyleSchema();
-    method @WorkerThread protected abstract suspend Object? createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, kotlin.coroutines.Continuation<? super androidx.wear.watchface.WatchFace> p);
+    method @WorkerThread protected abstract suspend Object? createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, kotlin.coroutines.Continuation<? super androidx.wear.watchface.WatchFace>);
     method public final android.os.Handler getBackgroundThreadHandler();
     method public final android.os.Handler getUiThreadHandler();
     method public final android.service.wallpaper.WallpaperService.Engine onCreateEngine();
@@ -309,9 +299,6 @@
   public static final class WatchFaceService.Companion {
   }
 
-  public final class WatchFaceServiceKt {
-  }
-
   public final class WatchState {
     ctor public WatchState(kotlinx.coroutines.flow.StateFlow<java.lang.Integer> interruptionFilter, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isAmbient, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isBatteryLowAndNotCharging, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isVisible, boolean hasLowBitAmbient, boolean hasBurnInProtection, long analogPreviewReferenceTimeMillis, long digitalPreviewReferenceTimeMillis, @Px int chinHeight, boolean isHeadless);
     method public long getAnalogPreviewReferenceTimeMillis();
diff --git a/wear/watchface/watchface/api/restricted_1.1.0-beta01.txt b/wear/watchface/watchface/api/restricted_1.1.0-beta01.txt
index be6af4f..d3ac33a 100644
--- a/wear/watchface/watchface/api/restricted_1.1.0-beta01.txt
+++ b/wear/watchface/watchface/api/restricted_1.1.0-beta01.txt
@@ -108,9 +108,6 @@
     method public default void onComplicationSlotTapped(int complicationSlotId);
   }
 
-  public final class ComplicationSlotsManagerKt {
-  }
-
   public interface ComplicationTapFilter {
     method public boolean hitTest(androidx.wear.watchface.ComplicationSlot complicationSlot, android.graphics.Rect screenBounds, @Px int x, @Px int y);
   }
@@ -127,20 +124,19 @@
   }
 
   public enum DrawMode {
+    method public static androidx.wear.watchface.DrawMode valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.wear.watchface.DrawMode[] values();
     enum_constant public static final androidx.wear.watchface.DrawMode AMBIENT;
     enum_constant public static final androidx.wear.watchface.DrawMode INTERACTIVE;
     enum_constant public static final androidx.wear.watchface.DrawMode LOW_BATTERY_INTERACTIVE;
     enum_constant public static final androidx.wear.watchface.DrawMode MUTE;
   }
 
-  public final class RenderBufferTextureKt {
-  }
-
   public final class RenderParameters {
-    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers, optional androidx.wear.watchface.RenderParameters.HighlightLayer? highlightLayer, optional java.util.Map<java.lang.Integer,androidx.wear.watchface.TapEvent> lastComplicationTapDownEvents);
-    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers, optional androidx.wear.watchface.RenderParameters.HighlightLayer? highlightLayer);
-    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers);
     ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public RenderParameters(androidx.wear.watchface.data.RenderParametersWireFormat wireFormat);
+    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers);
+    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers, optional androidx.wear.watchface.RenderParameters.HighlightLayer? highlightLayer);
+    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers, optional androidx.wear.watchface.RenderParameters.HighlightLayer? highlightLayer, optional java.util.Map<java.lang.Integer,androidx.wear.watchface.TapEvent> lastComplicationTapDownEvents);
     method public androidx.wear.watchface.DrawMode getDrawMode();
     method public androidx.wear.watchface.RenderParameters.HighlightLayer? getHighlightLayer();
     method public java.util.Map<java.lang.Integer,androidx.wear.watchface.TapEvent> getLastComplicationTapDownEvents();
@@ -215,29 +211,29 @@
   }
 
   @Deprecated public abstract static class Renderer.CanvasRenderer extends androidx.wear.watchface.Renderer {
-    ctor @Deprecated @WorkerThread public Renderer.CanvasRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, int canvasType, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional boolean clearWithBackgroundTintBeforeRenderingHighlightLayer);
     ctor @Deprecated @WorkerThread public Renderer.CanvasRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, int canvasType, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis);
+    ctor @Deprecated @WorkerThread public Renderer.CanvasRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, int canvasType, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional boolean clearWithBackgroundTintBeforeRenderingHighlightLayer);
     method @Deprecated public final boolean getClearWithBackgroundTintBeforeRenderingHighlightLayer();
     method @Deprecated @UiThread public suspend Object? init(kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @Deprecated public void onDump(java.io.PrintWriter writer);
     method @Deprecated @UiThread public abstract void render(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime);
     method @Deprecated @UiThread public abstract void renderHighlightLayer(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime);
-    property public final boolean clearWithBackgroundTintBeforeRenderingHighlightLayer;
+    property @Deprecated public final boolean clearWithBackgroundTintBeforeRenderingHighlightLayer;
   }
 
   public abstract static class Renderer.CanvasRenderer2<SharedAssetsT extends androidx.wear.watchface.Renderer.SharedAssets> extends androidx.wear.watchface.Renderer.CanvasRenderer {
     ctor @WorkerThread public Renderer.CanvasRenderer2(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, int canvasType, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, boolean clearWithBackgroundTintBeforeRenderingHighlightLayer);
     method @WorkerThread protected abstract suspend Object? createSharedAssets(kotlin.coroutines.Continuation<? super SharedAssetsT>);
-    method @UiThread public abstract void render(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
     method public final void render(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime);
-    method @UiThread public abstract void renderHighlightLayer(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
+    method @UiThread public abstract void render(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
     method public final void renderHighlightLayer(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime);
+    method @UiThread public abstract void renderHighlightLayer(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
   }
 
   @Deprecated public abstract static class Renderer.GlesRenderer extends androidx.wear.watchface.Renderer {
-    ctor @Deprecated @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList, optional int[] eglSurfaceAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
-    ctor @Deprecated @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
     ctor @Deprecated @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
+    ctor @Deprecated @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
+    ctor @Deprecated @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList, optional int[] eglSurfaceAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
     method @Deprecated public final android.opengl.EGLContext getEglBackgroundThreadContext();
     method @Deprecated public final android.opengl.EGLConfig getEglConfig();
     method @Deprecated public final android.opengl.EGLDisplay getEglDisplay();
@@ -251,10 +247,10 @@
     method @Deprecated public final suspend Object? runUiThreadGlCommands(kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> commands, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @Deprecated public final void setEglConfig(android.opengl.EGLConfig);
     method @Deprecated public final void setEglDisplay(android.opengl.EGLDisplay);
-    property public final android.opengl.EGLContext eglBackgroundThreadContext;
-    property public final android.opengl.EGLConfig eglConfig;
-    property public final android.opengl.EGLDisplay eglDisplay;
-    property public final android.opengl.EGLContext eglUiThreadContext;
+    property @Deprecated public final android.opengl.EGLContext eglBackgroundThreadContext;
+    property @Deprecated public final android.opengl.EGLConfig eglConfig;
+    property @Deprecated public final android.opengl.EGLDisplay eglDisplay;
+    property @Deprecated public final android.opengl.EGLContext eglUiThreadContext;
   }
 
   @Deprecated public static final class Renderer.GlesRenderer.GlesException extends java.lang.Exception {
@@ -262,23 +258,20 @@
   }
 
   public abstract static class Renderer.GlesRenderer2<SharedAssetsT extends androidx.wear.watchface.Renderer.SharedAssets> extends androidx.wear.watchface.Renderer.GlesRenderer {
-    ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer2(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList, optional int[] eglSurfaceAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
-    ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer2(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
     ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer2(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
+    ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer2(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
+    ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer2(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList, optional int[] eglSurfaceAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
     method @WorkerThread protected abstract suspend Object? createSharedAssets(kotlin.coroutines.Continuation<? super SharedAssetsT>);
-    method @UiThread public abstract void render(java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
     method public final void render(java.time.ZonedDateTime zonedDateTime);
-    method @UiThread public abstract void renderHighlightLayer(java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
+    method @UiThread public abstract void render(java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
     method public final void renderHighlightLayer(java.time.ZonedDateTime zonedDateTime);
+    method @UiThread public abstract void renderHighlightLayer(java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
   }
 
   public static interface Renderer.SharedAssets {
     method @UiThread public void onDestroy();
   }
 
-  public final class RendererKt {
-  }
-
   public final class RoundRectComplicationTapFilter implements androidx.wear.watchface.ComplicationTapFilter {
     ctor public RoundRectComplicationTapFilter();
     method public boolean hitTest(androidx.wear.watchface.ComplicationSlot complicationSlot, android.graphics.Rect screenBounds, @Px int x, @Px int y);
@@ -294,6 +287,28 @@
     property public final int yPos;
   }
 
+  @SuppressCompatibility @androidx.wear.watchface.WatchFaceFlavorsExperimental public final class UserStyleFlavor {
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public UserStyleFlavor(androidx.wear.watchface.style.data.UserStyleFlavorWireFormat wireFormat);
+    ctor public UserStyleFlavor(String id, androidx.wear.watchface.style.UserStyle style, java.util.Map<java.lang.Integer,androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy> complications);
+    ctor public UserStyleFlavor(String id, androidx.wear.watchface.style.UserStyleData style, java.util.Map<java.lang.Integer,androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy> complications);
+    method public java.util.Map<java.lang.Integer,androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy> getComplications();
+    method public String getId();
+    method public androidx.wear.watchface.style.UserStyleData getStyle();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.wear.watchface.style.data.UserStyleFlavorWireFormat toWireFormat();
+    property public final java.util.Map<java.lang.Integer,androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy> complications;
+    property public final String id;
+    property public final androidx.wear.watchface.style.UserStyleData style;
+  }
+
+  @SuppressCompatibility @androidx.wear.watchface.WatchFaceFlavorsExperimental public final class UserStyleFlavors {
+    ctor public UserStyleFlavors();
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public UserStyleFlavors(androidx.wear.watchface.style.data.UserStyleFlavorsWireFormat wireFormat);
+    ctor public UserStyleFlavors(java.util.List<androidx.wear.watchface.UserStyleFlavor> flavors);
+    method public java.util.List<androidx.wear.watchface.UserStyleFlavor> getFlavors();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.wear.watchface.style.data.UserStyleFlavorsWireFormat toWireFormat();
+    property public final java.util.List<androidx.wear.watchface.UserStyleFlavor> flavors;
+  }
+
   public final class WatchFace {
     ctor public WatchFace(int watchFaceType, androidx.wear.watchface.Renderer renderer);
     method public androidx.wear.watchface.WatchFace.LegacyWatchFaceOverlayStyle getLegacyWatchFaceStyle();
@@ -322,8 +337,8 @@
   }
 
   public static final class WatchFace.LegacyWatchFaceOverlayStyle {
-    ctor public WatchFace.LegacyWatchFaceOverlayStyle(int viewProtectionMode, int statusBarGravity, boolean tapEventsAccepted, optional @ColorInt int accentColor);
     ctor public WatchFace.LegacyWatchFaceOverlayStyle(int viewProtectionMode, int statusBarGravity, boolean tapEventsAccepted);
+    ctor public WatchFace.LegacyWatchFaceOverlayStyle(int viewProtectionMode, int statusBarGravity, boolean tapEventsAccepted, optional @ColorInt int accentColor);
     method public int getAccentColor();
     method public int getStatusBarGravity();
     method public boolean getTapEventsAccepted();
@@ -335,8 +350,8 @@
   }
 
   public static final class WatchFace.OverlayStyle {
-    ctor public WatchFace.OverlayStyle(android.graphics.Color? backgroundColor, android.graphics.Color? foregroundColor);
     ctor public WatchFace.OverlayStyle();
+    ctor public WatchFace.OverlayStyle(android.graphics.Color? backgroundColor, android.graphics.Color? foregroundColor);
     method public android.graphics.Color? getBackgroundColor();
     method public android.graphics.Color? getForegroundColor();
     property public final android.graphics.Color? backgroundColor;
@@ -347,12 +362,13 @@
     method @UiThread public void onTapEvent(int tapType, androidx.wear.watchface.TapEvent tapEvent, androidx.wear.watchface.ComplicationSlot? complicationSlot);
   }
 
-  public final class WatchFaceKt {
+  @SuppressCompatibility @kotlin.RequiresOptIn(message="This is an experimental API that may change or be removed without warning.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface WatchFaceFlavorsExperimental {
   }
 
   public abstract class WatchFaceService extends android.service.wallpaper.WallpaperService {
     ctor public WatchFaceService();
     method @WorkerThread protected androidx.wear.watchface.ComplicationSlotsManager createComplicationSlotsManager(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository);
+    method @SuppressCompatibility @WorkerThread @androidx.wear.watchface.WatchFaceFlavorsExperimental protected androidx.wear.watchface.UserStyleFlavors createUserStyleFlavors(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager);
     method @WorkerThread protected androidx.wear.watchface.style.UserStyleSchema createUserStyleSchema();
     method @WorkerThread protected abstract suspend Object? createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, kotlin.coroutines.Continuation<? super androidx.wear.watchface.WatchFace>);
     method public final android.os.Handler getBackgroundThreadHandler();
@@ -366,12 +382,9 @@
   public static final class WatchFaceService.Companion {
   }
 
-  public final class WatchFaceServiceKt {
-  }
-
   public final class WatchState {
-    ctor public WatchState(kotlinx.coroutines.flow.StateFlow<java.lang.Integer> interruptionFilter, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isAmbient, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isBatteryLowAndNotCharging, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isVisible, boolean hasLowBitAmbient, boolean hasBurnInProtection, long analogPreviewReferenceTimeMillis, long digitalPreviewReferenceTimeMillis, @Px int chinHeight, boolean isHeadless, kotlinx.coroutines.flow.StateFlow<java.lang.String> watchFaceInstanceId);
     ctor @Deprecated public WatchState(kotlinx.coroutines.flow.StateFlow<java.lang.Integer> interruptionFilter, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isAmbient, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isBatteryLowAndNotCharging, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isVisible, boolean hasLowBitAmbient, boolean hasBurnInProtection, long analogPreviewReferenceTimeMillis, long digitalPreviewReferenceTimeMillis, int chinHeight, boolean isHeadless);
+    ctor public WatchState(kotlinx.coroutines.flow.StateFlow<java.lang.Integer> interruptionFilter, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isAmbient, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isBatteryLowAndNotCharging, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isVisible, boolean hasLowBitAmbient, boolean hasBurnInProtection, long analogPreviewReferenceTimeMillis, long digitalPreviewReferenceTimeMillis, @Px int chinHeight, boolean isHeadless, kotlinx.coroutines.flow.StateFlow<java.lang.String> watchFaceInstanceId);
     method public long getAnalogPreviewReferenceTimeMillis();
     method @Px public int getChinHeight();
     method public long getDigitalPreviewReferenceTimeMillis();
diff --git a/wear/watchface/watchface/api/restricted_1.1.0-beta02.txt b/wear/watchface/watchface/api/restricted_1.1.0-beta02.txt
index be6af4f..893768e 100644
--- a/wear/watchface/watchface/api/restricted_1.1.0-beta02.txt
+++ b/wear/watchface/watchface/api/restricted_1.1.0-beta02.txt
@@ -6,6 +6,20 @@
     method public boolean hitTest(androidx.wear.watchface.ComplicationSlot complicationSlot, android.graphics.Rect screenBounds, @Px int x, @Px int y);
   }
 
+  @SuppressCompatibility @androidx.wear.watchface.complications.data.ComplicationExperimental public final class BoundingArc {
+    ctor public BoundingArc(float startAngle, float totalAngle, @Px float thickness);
+    method public float getStartAngle();
+    method public float getThickness();
+    method public float getTotalAngle();
+    method public boolean hitTest(android.graphics.Rect rect, @Px float x, @Px float y);
+    method public void setStartAngle(float);
+    method public void setThickness(float);
+    method public void setTotalAngle(float);
+    property public final float startAngle;
+    property public final float thickness;
+    property public final float totalAngle;
+  }
+
   public interface CanvasComplication {
     method public void drawHighlight(android.graphics.Canvas canvas, android.graphics.Rect bounds, int boundsType, java.time.ZonedDateTime zonedDateTime, @ColorInt int color);
     method public androidx.wear.watchface.complications.data.ComplicationData getData();
@@ -25,6 +39,8 @@
   public final class ComplicationSlot {
     method public android.graphics.Rect computeBounds(android.graphics.Rect screen);
     method public static androidx.wear.watchface.ComplicationSlot.Builder createBackgroundComplicationSlotBuilder(int id, androidx.wear.watchface.CanvasComplicationFactory canvasComplicationFactory, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy);
+    method @SuppressCompatibility @androidx.wear.watchface.complications.data.ComplicationExperimental public static androidx.wear.watchface.ComplicationSlot.Builder createEdgeComplicationSlotBuilder(int id, androidx.wear.watchface.CanvasComplicationFactory canvasComplicationFactory, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, androidx.wear.watchface.complications.ComplicationSlotBounds bounds, androidx.wear.watchface.BoundingArc boundingArc);
+    method @SuppressCompatibility @androidx.wear.watchface.complications.data.ComplicationExperimental public static androidx.wear.watchface.ComplicationSlot.Builder createEdgeComplicationSlotBuilder(int id, androidx.wear.watchface.CanvasComplicationFactory canvasComplicationFactory, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, androidx.wear.watchface.complications.ComplicationSlotBounds bounds, androidx.wear.watchface.BoundingArc boundingArc, optional androidx.wear.watchface.ComplicationTapFilter complicationTapFilter);
     method public static androidx.wear.watchface.ComplicationSlot.Builder createEdgeComplicationSlotBuilder(int id, androidx.wear.watchface.CanvasComplicationFactory canvasComplicationFactory, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, androidx.wear.watchface.complications.ComplicationSlotBounds bounds, androidx.wear.watchface.ComplicationTapFilter complicationTapFilter);
     method public static androidx.wear.watchface.ComplicationSlot.Builder createRoundRectComplicationSlotBuilder(int id, androidx.wear.watchface.CanvasComplicationFactory canvasComplicationFactory, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, androidx.wear.watchface.complications.ComplicationSlotBounds bounds);
     method @UiThread public int getAccessibilityTraversalIndex();
@@ -81,6 +97,8 @@
 
   public static final class ComplicationSlot.Companion {
     method public androidx.wear.watchface.ComplicationSlot.Builder createBackgroundComplicationSlotBuilder(int id, androidx.wear.watchface.CanvasComplicationFactory canvasComplicationFactory, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy);
+    method @SuppressCompatibility @androidx.wear.watchface.complications.data.ComplicationExperimental public androidx.wear.watchface.ComplicationSlot.Builder createEdgeComplicationSlotBuilder(int id, androidx.wear.watchface.CanvasComplicationFactory canvasComplicationFactory, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, androidx.wear.watchface.complications.ComplicationSlotBounds bounds, androidx.wear.watchface.BoundingArc boundingArc);
+    method @SuppressCompatibility @androidx.wear.watchface.complications.data.ComplicationExperimental public androidx.wear.watchface.ComplicationSlot.Builder createEdgeComplicationSlotBuilder(int id, androidx.wear.watchface.CanvasComplicationFactory canvasComplicationFactory, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, androidx.wear.watchface.complications.ComplicationSlotBounds bounds, androidx.wear.watchface.BoundingArc boundingArc, optional androidx.wear.watchface.ComplicationTapFilter complicationTapFilter);
     method public androidx.wear.watchface.ComplicationSlot.Builder createEdgeComplicationSlotBuilder(int id, androidx.wear.watchface.CanvasComplicationFactory canvasComplicationFactory, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, androidx.wear.watchface.complications.ComplicationSlotBounds bounds, androidx.wear.watchface.ComplicationTapFilter complicationTapFilter);
     method public androidx.wear.watchface.ComplicationSlot.Builder createRoundRectComplicationSlotBuilder(int id, androidx.wear.watchface.CanvasComplicationFactory canvasComplicationFactory, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, androidx.wear.watchface.complications.ComplicationSlotBounds bounds);
   }
@@ -108,9 +126,6 @@
     method public default void onComplicationSlotTapped(int complicationSlotId);
   }
 
-  public final class ComplicationSlotsManagerKt {
-  }
-
   public interface ComplicationTapFilter {
     method public boolean hitTest(androidx.wear.watchface.ComplicationSlot complicationSlot, android.graphics.Rect screenBounds, @Px int x, @Px int y);
   }
@@ -127,20 +142,19 @@
   }
 
   public enum DrawMode {
+    method public static androidx.wear.watchface.DrawMode valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+    method public static androidx.wear.watchface.DrawMode[] values();
     enum_constant public static final androidx.wear.watchface.DrawMode AMBIENT;
     enum_constant public static final androidx.wear.watchface.DrawMode INTERACTIVE;
     enum_constant public static final androidx.wear.watchface.DrawMode LOW_BATTERY_INTERACTIVE;
     enum_constant public static final androidx.wear.watchface.DrawMode MUTE;
   }
 
-  public final class RenderBufferTextureKt {
-  }
-
   public final class RenderParameters {
-    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers, optional androidx.wear.watchface.RenderParameters.HighlightLayer? highlightLayer, optional java.util.Map<java.lang.Integer,androidx.wear.watchface.TapEvent> lastComplicationTapDownEvents);
-    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers, optional androidx.wear.watchface.RenderParameters.HighlightLayer? highlightLayer);
-    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers);
     ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public RenderParameters(androidx.wear.watchface.data.RenderParametersWireFormat wireFormat);
+    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers);
+    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers, optional androidx.wear.watchface.RenderParameters.HighlightLayer? highlightLayer);
+    ctor public RenderParameters(androidx.wear.watchface.DrawMode drawMode, java.util.Set<? extends androidx.wear.watchface.style.WatchFaceLayer> watchFaceLayers, optional androidx.wear.watchface.RenderParameters.HighlightLayer? highlightLayer, optional java.util.Map<java.lang.Integer,androidx.wear.watchface.TapEvent> lastComplicationTapDownEvents);
     method public androidx.wear.watchface.DrawMode getDrawMode();
     method public androidx.wear.watchface.RenderParameters.HighlightLayer? getHighlightLayer();
     method public java.util.Map<java.lang.Integer,androidx.wear.watchface.TapEvent> getLastComplicationTapDownEvents();
@@ -215,29 +229,29 @@
   }
 
   @Deprecated public abstract static class Renderer.CanvasRenderer extends androidx.wear.watchface.Renderer {
-    ctor @Deprecated @WorkerThread public Renderer.CanvasRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, int canvasType, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional boolean clearWithBackgroundTintBeforeRenderingHighlightLayer);
     ctor @Deprecated @WorkerThread public Renderer.CanvasRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, int canvasType, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis);
+    ctor @Deprecated @WorkerThread public Renderer.CanvasRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, int canvasType, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional boolean clearWithBackgroundTintBeforeRenderingHighlightLayer);
     method @Deprecated public final boolean getClearWithBackgroundTintBeforeRenderingHighlightLayer();
     method @Deprecated @UiThread public suspend Object? init(kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @Deprecated public void onDump(java.io.PrintWriter writer);
     method @Deprecated @UiThread public abstract void render(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime);
     method @Deprecated @UiThread public abstract void renderHighlightLayer(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime);
-    property public final boolean clearWithBackgroundTintBeforeRenderingHighlightLayer;
+    property @Deprecated public final boolean clearWithBackgroundTintBeforeRenderingHighlightLayer;
   }
 
   public abstract static class Renderer.CanvasRenderer2<SharedAssetsT extends androidx.wear.watchface.Renderer.SharedAssets> extends androidx.wear.watchface.Renderer.CanvasRenderer {
     ctor @WorkerThread public Renderer.CanvasRenderer2(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, int canvasType, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, boolean clearWithBackgroundTintBeforeRenderingHighlightLayer);
     method @WorkerThread protected abstract suspend Object? createSharedAssets(kotlin.coroutines.Continuation<? super SharedAssetsT>);
-    method @UiThread public abstract void render(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
     method public final void render(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime);
-    method @UiThread public abstract void renderHighlightLayer(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
+    method @UiThread public abstract void render(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
     method public final void renderHighlightLayer(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime);
+    method @UiThread public abstract void renderHighlightLayer(android.graphics.Canvas canvas, android.graphics.Rect bounds, java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
   }
 
   @Deprecated public abstract static class Renderer.GlesRenderer extends androidx.wear.watchface.Renderer {
-    ctor @Deprecated @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList, optional int[] eglSurfaceAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
-    ctor @Deprecated @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
     ctor @Deprecated @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
+    ctor @Deprecated @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
+    ctor @Deprecated @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList, optional int[] eglSurfaceAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
     method @Deprecated public final android.opengl.EGLContext getEglBackgroundThreadContext();
     method @Deprecated public final android.opengl.EGLConfig getEglConfig();
     method @Deprecated public final android.opengl.EGLDisplay getEglDisplay();
@@ -251,10 +265,10 @@
     method @Deprecated public final suspend Object? runUiThreadGlCommands(kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super kotlin.Unit>,?> commands, kotlin.coroutines.Continuation<? super kotlin.Unit>);
     method @Deprecated public final void setEglConfig(android.opengl.EGLConfig);
     method @Deprecated public final void setEglDisplay(android.opengl.EGLDisplay);
-    property public final android.opengl.EGLContext eglBackgroundThreadContext;
-    property public final android.opengl.EGLConfig eglConfig;
-    property public final android.opengl.EGLDisplay eglDisplay;
-    property public final android.opengl.EGLContext eglUiThreadContext;
+    property @Deprecated public final android.opengl.EGLContext eglBackgroundThreadContext;
+    property @Deprecated public final android.opengl.EGLConfig eglConfig;
+    property @Deprecated public final android.opengl.EGLDisplay eglDisplay;
+    property @Deprecated public final android.opengl.EGLContext eglUiThreadContext;
   }
 
   @Deprecated public static final class Renderer.GlesRenderer.GlesException extends java.lang.Exception {
@@ -262,23 +276,20 @@
   }
 
   public abstract static class Renderer.GlesRenderer2<SharedAssetsT extends androidx.wear.watchface.Renderer.SharedAssets> extends androidx.wear.watchface.Renderer.GlesRenderer {
-    ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer2(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList, optional int[] eglSurfaceAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
-    ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer2(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
     ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer2(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
+    ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer2(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
+    ctor @WorkerThread @kotlin.jvm.Throws(exceptionClasses=GlesException::class) public Renderer.GlesRenderer2(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.WatchState watchState, @IntRange(from=0L, to=60000L) long interactiveDrawModeUpdateDelayMillis, optional int[] eglConfigAttribList, optional int[] eglSurfaceAttribList) throws androidx.wear.watchface.Renderer.GlesRenderer.GlesException;
     method @WorkerThread protected abstract suspend Object? createSharedAssets(kotlin.coroutines.Continuation<? super SharedAssetsT>);
-    method @UiThread public abstract void render(java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
     method public final void render(java.time.ZonedDateTime zonedDateTime);
-    method @UiThread public abstract void renderHighlightLayer(java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
+    method @UiThread public abstract void render(java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
     method public final void renderHighlightLayer(java.time.ZonedDateTime zonedDateTime);
+    method @UiThread public abstract void renderHighlightLayer(java.time.ZonedDateTime zonedDateTime, SharedAssetsT sharedAssets);
   }
 
   public static interface Renderer.SharedAssets {
     method @UiThread public void onDestroy();
   }
 
-  public final class RendererKt {
-  }
-
   public final class RoundRectComplicationTapFilter implements androidx.wear.watchface.ComplicationTapFilter {
     ctor public RoundRectComplicationTapFilter();
     method public boolean hitTest(androidx.wear.watchface.ComplicationSlot complicationSlot, android.graphics.Rect screenBounds, @Px int x, @Px int y);
@@ -294,6 +305,28 @@
     property public final int yPos;
   }
 
+  @SuppressCompatibility @androidx.wear.watchface.WatchFaceFlavorsExperimental public final class UserStyleFlavor {
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public UserStyleFlavor(androidx.wear.watchface.style.data.UserStyleFlavorWireFormat wireFormat);
+    ctor public UserStyleFlavor(String id, androidx.wear.watchface.style.UserStyle style, java.util.Map<java.lang.Integer,androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy> complications);
+    ctor public UserStyleFlavor(String id, androidx.wear.watchface.style.UserStyleData style, java.util.Map<java.lang.Integer,androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy> complications);
+    method public java.util.Map<java.lang.Integer,androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy> getComplications();
+    method public String getId();
+    method public androidx.wear.watchface.style.UserStyleData getStyle();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.wear.watchface.style.data.UserStyleFlavorWireFormat toWireFormat();
+    property public final java.util.Map<java.lang.Integer,androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy> complications;
+    property public final String id;
+    property public final androidx.wear.watchface.style.UserStyleData style;
+  }
+
+  @SuppressCompatibility @androidx.wear.watchface.WatchFaceFlavorsExperimental public final class UserStyleFlavors {
+    ctor public UserStyleFlavors();
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public UserStyleFlavors(androidx.wear.watchface.style.data.UserStyleFlavorsWireFormat wireFormat);
+    ctor public UserStyleFlavors(java.util.List<androidx.wear.watchface.UserStyleFlavor> flavors);
+    method public java.util.List<androidx.wear.watchface.UserStyleFlavor> getFlavors();
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.wear.watchface.style.data.UserStyleFlavorsWireFormat toWireFormat();
+    property public final java.util.List<androidx.wear.watchface.UserStyleFlavor> flavors;
+  }
+
   public final class WatchFace {
     ctor public WatchFace(int watchFaceType, androidx.wear.watchface.Renderer renderer);
     method public androidx.wear.watchface.WatchFace.LegacyWatchFaceOverlayStyle getLegacyWatchFaceStyle();
@@ -322,8 +355,8 @@
   }
 
   public static final class WatchFace.LegacyWatchFaceOverlayStyle {
-    ctor public WatchFace.LegacyWatchFaceOverlayStyle(int viewProtectionMode, int statusBarGravity, boolean tapEventsAccepted, optional @ColorInt int accentColor);
     ctor public WatchFace.LegacyWatchFaceOverlayStyle(int viewProtectionMode, int statusBarGravity, boolean tapEventsAccepted);
+    ctor public WatchFace.LegacyWatchFaceOverlayStyle(int viewProtectionMode, int statusBarGravity, boolean tapEventsAccepted, optional @ColorInt int accentColor);
     method public int getAccentColor();
     method public int getStatusBarGravity();
     method public boolean getTapEventsAccepted();
@@ -335,8 +368,8 @@
   }
 
   public static final class WatchFace.OverlayStyle {
-    ctor public WatchFace.OverlayStyle(android.graphics.Color? backgroundColor, android.graphics.Color? foregroundColor);
     ctor public WatchFace.OverlayStyle();
+    ctor public WatchFace.OverlayStyle(android.graphics.Color? backgroundColor, android.graphics.Color? foregroundColor);
     method public android.graphics.Color? getBackgroundColor();
     method public android.graphics.Color? getForegroundColor();
     property public final android.graphics.Color? backgroundColor;
@@ -347,12 +380,13 @@
     method @UiThread public void onTapEvent(int tapType, androidx.wear.watchface.TapEvent tapEvent, androidx.wear.watchface.ComplicationSlot? complicationSlot);
   }
 
-  public final class WatchFaceKt {
+  @SuppressCompatibility @kotlin.RequiresOptIn(message="This is an experimental API that may change or be removed without warning.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface WatchFaceFlavorsExperimental {
   }
 
   public abstract class WatchFaceService extends android.service.wallpaper.WallpaperService {
     ctor public WatchFaceService();
     method @WorkerThread protected androidx.wear.watchface.ComplicationSlotsManager createComplicationSlotsManager(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository);
+    method @SuppressCompatibility @WorkerThread @androidx.wear.watchface.WatchFaceFlavorsExperimental protected androidx.wear.watchface.UserStyleFlavors createUserStyleFlavors(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager);
     method @WorkerThread protected androidx.wear.watchface.style.UserStyleSchema createUserStyleSchema();
     method @WorkerThread protected abstract suspend Object? createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, kotlin.coroutines.Continuation<? super androidx.wear.watchface.WatchFace>);
     method public final android.os.Handler getBackgroundThreadHandler();
@@ -366,12 +400,9 @@
   public static final class WatchFaceService.Companion {
   }
 
-  public final class WatchFaceServiceKt {
-  }
-
   public final class WatchState {
-    ctor public WatchState(kotlinx.coroutines.flow.StateFlow<java.lang.Integer> interruptionFilter, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isAmbient, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isBatteryLowAndNotCharging, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isVisible, boolean hasLowBitAmbient, boolean hasBurnInProtection, long analogPreviewReferenceTimeMillis, long digitalPreviewReferenceTimeMillis, @Px int chinHeight, boolean isHeadless, kotlinx.coroutines.flow.StateFlow<java.lang.String> watchFaceInstanceId);
     ctor @Deprecated public WatchState(kotlinx.coroutines.flow.StateFlow<java.lang.Integer> interruptionFilter, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isAmbient, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isBatteryLowAndNotCharging, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isVisible, boolean hasLowBitAmbient, boolean hasBurnInProtection, long analogPreviewReferenceTimeMillis, long digitalPreviewReferenceTimeMillis, int chinHeight, boolean isHeadless);
+    ctor public WatchState(kotlinx.coroutines.flow.StateFlow<java.lang.Integer> interruptionFilter, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isAmbient, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isBatteryLowAndNotCharging, kotlinx.coroutines.flow.StateFlow<java.lang.Boolean> isVisible, boolean hasLowBitAmbient, boolean hasBurnInProtection, long analogPreviewReferenceTimeMillis, long digitalPreviewReferenceTimeMillis, @Px int chinHeight, boolean isHeadless, kotlinx.coroutines.flow.StateFlow<java.lang.String> watchFaceInstanceId);
     method public long getAnalogPreviewReferenceTimeMillis();
     method @Px public int getChinHeight();
     method public long getDigitalPreviewReferenceTimeMillis();
diff --git a/wear/watchface/watchface/build.gradle b/wear/watchface/watchface/build.gradle
index c67c751..9807b57 100644
--- a/wear/watchface/watchface/build.gradle
+++ b/wear/watchface/watchface/build.gradle
@@ -45,6 +45,7 @@
     androidTestImplementation(libs.mockitoKotlin)
     androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(libs.truth)
+    androidTestImplementation(libs.kotlinTest)
 
     testImplementation(project(":wear:watchface:watchface-complications-rendering"))
     testImplementation(libs.testExtJunit)
diff --git a/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceServiceImageTest.kt b/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceServiceImageTest.kt
index 53dddff..6d2a482 100644
--- a/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceServiceImageTest.kt
+++ b/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/WatchFaceServiceImageTest.kt
@@ -92,7 +92,7 @@
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.Mockito
-import org.mockito.MockitoAnnotations
+import org.mockito.junit.MockitoJUnit
 
 private const val BITMAP_WIDTH = 400
 private const val BITMAP_HEIGHT = 400
@@ -133,6 +133,9 @@
 @RequiresApi(Build.VERSION_CODES.O_MR1)
 public class WatchFaceServiceImageTest {
 
+    @get:Rule
+    val mocks = MockitoJUnit.rule()
+
     @Mock private lateinit var surfaceHolder: SurfaceHolder
 
     @Mock private lateinit var surface: Surface
@@ -194,11 +197,9 @@
     private lateinit var engineWrapper: WatchFaceService.EngineWrapper
     private lateinit var interactiveWatchFaceInstance: IInteractiveWatchFace
 
-    @Suppress("DEPRECATION") // b/251211092
     @Before
     public fun setUp() {
         Assume.assumeTrue("This test suite assumes API 27", Build.VERSION.SDK_INT >= 27)
-        MockitoAnnotations.initMocks(this)
 
         pretendBinderThread.start()
         pretendBinderHandler = Handler(pretendBinderThread.looper)
diff --git a/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/XmlDefinedUserStyleSchemaAndComplicationSlotsTest.kt b/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/XmlDefinedUserStyleSchemaAndComplicationSlotsTest.kt
index cc04564..e47d35f 100644
--- a/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/XmlDefinedUserStyleSchemaAndComplicationSlotsTest.kt
+++ b/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/XmlDefinedUserStyleSchemaAndComplicationSlotsTest.kt
@@ -59,19 +59,22 @@
 import androidx.wear.watchface.style.UserStyleSetting.LongRangeUserStyleSetting.LongRangeOption
 import androidx.wear.watchface.style.data.UserStyleWireFormat
 import com.google.common.truth.Truth.assertThat
+import java.lang.IllegalArgumentException
 import java.time.ZonedDateTime
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.TimeUnit
+import kotlin.test.assertFailsWith
 import kotlinx.coroutines.runBlocking
 import org.junit.After
 import org.junit.Assert
 import org.junit.Assume
 import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.Mockito
-import org.mockito.MockitoAnnotations
+import org.mockito.junit.MockitoJUnit
 
 private const val BITMAP_WIDTH = 400
 private const val BITMAP_HEIGHT = 400
@@ -81,13 +84,14 @@
 
 class TestXmlWatchFaceService(
     testContext: Context,
-    private var surfaceHolderOverride: SurfaceHolder
+    private var surfaceHolderOverride: SurfaceHolder,
+    private var xmlWatchFaceResourceId: Int,
 ) : WatchFaceService() {
     init {
         attachBaseContext(testContext)
     }
 
-    override fun getXmlWatchFaceResourceId() = R.xml.xml_watchface
+    override fun getXmlWatchFaceResourceId() = xmlWatchFaceResourceId
 
     override fun getWallpaperSurfaceHolderOverride() = surfaceHolderOverride
 
@@ -155,7 +159,10 @@
 
 @RunWith(AndroidJUnit4::class)
 @MediumTest
-public class XmlDefinedUserStyleSchemaAndComplicationSlotsTest {
+class XmlDefinedUserStyleSchemaAndComplicationSlotsTest {
+
+    @get:Rule
+    val mocks = MockitoJUnit.rule()
 
     @Mock private lateinit var surfaceHolder: SurfaceHolder
 
@@ -166,28 +173,26 @@
     private var initLatch = CountDownLatch(1)
     private lateinit var interactiveWatchFaceInstance: IInteractiveWatchFace
 
-    @Suppress("DEPRECATION") // b/251211092
     @Before
-    public fun setUp() {
+    fun setUp() {
         Assume.assumeTrue("This test suite assumes API 29", Build.VERSION.SDK_INT >= 29)
-        MockitoAnnotations.initMocks(this)
     }
 
     @After
-    public fun tearDown() {
+    fun tearDown() {
         InteractiveInstanceManager.setParameterlessEngine(null)
         if (this::interactiveWatchFaceInstance.isInitialized) {
             interactiveWatchFaceInstance.release()
         }
     }
 
-    private fun setPendingWallpaperInteractiveWatchFaceInstance() {
+    private fun setPendingWallpaperInteractiveWatchFaceInstance(instanceId: String) {
         val existingInstance =
             InteractiveInstanceManager
                 .getExistingInstanceOrSetPendingWallpaperInteractiveWatchFaceInstance(
                     InteractiveInstanceManager.PendingWallpaperInteractiveWatchFaceInstance(
                         WallpaperInteractiveWatchFaceInstanceParams(
-                            INTERACTIVE_INSTANCE_ID,
+                            instanceId,
                             DeviceConfig(false, false, 0, 0),
                             WatchUiState(false, 0),
                             UserStyleWireFormat(emptyMap()),
@@ -216,13 +221,14 @@
         assertThat(existingInstance).isNull()
     }
 
-    @Test
-    @Suppress("Deprecation", "NewApi") // userStyleSettings
-    public fun staticSchemaAndComplicationsRead() {
+    private fun createAndMountTestService(
+        xmlWatchFaceResourceId: Int = R.xml.xml_watchface,
+    ): WatchFaceService.EngineWrapper {
         val service =
             TestXmlWatchFaceService(
-                ApplicationProvider.getApplicationContext<Context>(),
-                surfaceHolder
+                ApplicationProvider.getApplicationContext(),
+                surfaceHolder,
+                xmlWatchFaceResourceId
             )
 
         Mockito.`when`(surfaceHolder.surfaceFrame)
@@ -231,11 +237,21 @@
         Mockito.`when`(surfaceHolder.surface).thenReturn(surface)
         Mockito.`when`(surface.isValid).thenReturn(false)
 
-        setPendingWallpaperInteractiveWatchFaceInstance()
+        setPendingWallpaperInteractiveWatchFaceInstance(
+            "${INTERACTIVE_INSTANCE_ID}_$xmlWatchFaceResourceId"
+        )
 
         val wrapper = service.onCreateEngine() as WatchFaceService.EngineWrapper
         assertThat(initLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue()
 
+        return wrapper
+    }
+
+    @Test
+    @Suppress("Deprecation", "NewApi") // userStyleSettings
+    fun staticSchemaAndComplicationsRead() {
+        val wrapper = createAndMountTestService()
+
         runBlocking {
             val watchFaceImpl = wrapper.deferredWatchFaceImpl.await()
             val schema = watchFaceImpl.currentUserStyleRepository.schema
@@ -393,4 +409,42 @@
                 )
         }
     }
+
+    @Test
+    fun staticSchemaAndComplicationsRead_invalidXml() {
+        // test that when the xml cannot be parsed, the error is propagated and that
+        // the deferred values of the engine wrapper do not hang indefinitely
+        val wrapper = createAndMountTestService(R.xml.xml_watchface_invalid)
+        runBlocking {
+            val exception =
+                assertFailsWith<IllegalArgumentException> { wrapper.deferredValidation.await() }
+            assertThat(exception.message).contains("must have a systemDataSourceFallback attribute")
+            assertThat(wrapper.deferredWatchFaceImpl.isCancelled)
+        }
+    }
+
+    @Test
+    fun readsComplicationWithWeatherDefaultOnApi34() {
+        Assume.assumeTrue("This test runs only on API >= 34", Build.VERSION.SDK_INT >= 34)
+        val wrapper = createAndMountTestService(R.xml.xml_watchface_weather)
+        runBlocking {
+            val watchFaceImpl = wrapper.deferredWatchFaceImpl.await()
+            val complicationSlot = watchFaceImpl.complicationSlotsManager.complicationSlots[10]!!
+            assertThat(complicationSlot.defaultDataSourcePolicy.systemDataSourceFallback)
+                .isEqualTo(SystemDataSources.DATA_SOURCE_WEATHER)
+        }
+    }
+
+    @Test
+    fun throwsExceptionOnReadingComplicationWithWeatherDefaultOnApiBelow34() {
+        Assume.assumeTrue("This test runs only on API < 34", Build.VERSION.SDK_INT < 34)
+        val wrapper = createAndMountTestService(R.xml.xml_watchface_weather)
+
+        runBlocking {
+            val exception =
+                assertFailsWith<IllegalArgumentException> { wrapper.deferredValidation.await() }
+            assertThat(exception.message)
+                .contains("cannot have the supplied systemDataSourceFallback value")
+        }
+    }
 }
diff --git a/wear/watchface/watchface/src/androidTest/res/xml/xml_watchface_invalid.xml b/wear/watchface/watchface/src/androidTest/res/xml/xml_watchface_invalid.xml
new file mode 100644
index 0000000..7a1fe31
--- /dev/null
+++ b/wear/watchface/watchface/src/androidTest/res/xml/xml_watchface_invalid.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<XmlWatchFace xmlns:app="http://schemas.android.com/apk/res-auto"
+    app:complicationScaleX="10.0"
+    app:complicationScaleY="100.0">
+    <ComplicationSlot
+        app:slotId="@integer/complication_slot_10"
+        app:name="@string/complication_name_one"
+        app:screenReaderName="@string/complication_screen_reader_name_one"
+        app:boundsType="ROUND_RECT"
+        app:supportedTypes="RANGED_VALUE|SHORT_TEXT|SMALL_IMAGE">
+        <ComplicationSlotBounds app:left="3" app:top="70" app:right="7" app:bottom="90"/>
+    </ComplicationSlot>
+</XmlWatchFace>
diff --git a/wear/watchface/watchface/src/androidTest/res/xml/xml_watchface_weather.xml b/wear/watchface/watchface/src/androidTest/res/xml/xml_watchface_weather.xml
new file mode 100644
index 0000000..fdcf220
--- /dev/null
+++ b/wear/watchface/watchface/src/androidTest/res/xml/xml_watchface_weather.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<XmlWatchFace xmlns:app="http://schemas.android.com/apk/res-auto"
+    app:complicationScaleX="10.0"
+    app:complicationScaleY="100.0">
+    <ComplicationSlot
+        app:slotId="@integer/complication_slot_10"
+        app:name="@string/complication_name_one"
+        app:screenReaderName="@string/complication_screen_reader_name_one"
+        app:boundsType="ROUND_RECT"
+        app:supportedTypes="RANGED_VALUE|SHORT_TEXT|SMALL_IMAGE"
+        app:systemDataSourceFallback="DATA_SOURCE_WEATHER"
+        app:systemDataSourceFallbackDefaultType="SHORT_TEXT">
+        <ComplicationSlotBounds app:left="3" app:top="70" app:right="7" app:bottom="90"/>
+    </ComplicationSlot>
+</XmlWatchFace>
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlot.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlot.kt
index 5f14a6f..c36b80e 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlot.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlot.kt
@@ -345,8 +345,11 @@
  * @param accessibilityTraversalIndex Used to sort Complications when generating accessibility
  *   content description labels.
  * @param bounds The complication slot's [ComplicationSlotBounds].
- * @param supportedTypes The list of [ComplicationType]s accepted by this complication slot. Used
- *   during complication data source selection, this list should be non-empty.
+ * @param supportedTypes The list of [ComplicationType]s accepted by this complication slot, must be
+ *   non-empty. During complication data source selection, each item in this list is compared in
+ *   turn with entries from a data source's data source's supported types. The first matching entry
+ *   from `supportedTypes` is chosen. If there are no matches then that data source is not eligible
+ *   to be selected in this slot.
  * @param defaultPolicy The [DefaultComplicationDataSourcePolicy] which controls the initial
  *   complication data source when the watch face is first installed.
  * @param defaultDataSourceType The default [ComplicationType] for the default complication data
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
index fd8f179..5274c7c 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
@@ -406,8 +406,7 @@
      * This class allows the watch face to configure the status overlay which is rendered by the
      * system on top of the watch face.
      *
-     * Note: While the plumbing was built, the System UI side of the this feature was never
-     * implemented, and this method will be removed.
+     * Note: This is not a supported API and will be removed.
      */
     @Deprecated("OverlayStyle will be removed in a future release.")
     public class OverlayStyle(
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
index 9f2b9f2..c32fa26 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
@@ -293,6 +293,13 @@
  * not be inflated from XML.
  *
  * Note it is an error to define a XmlSchemaAndComplicationSlotsDefinition and not use it.
+ *
+ * As of Wear OS 4, complications can provide data using dynamic values, that the platform evaluates
+ * continuously and sends the evaluated results to the watch face. Privileged watch faces that can
+ * utilize the unevaluated dynamic values, can include the privileged permission
+ * `com.google.wear.permission.GET_COMPLICATION_DYNAMIC_VALUE` in their manifest, which will tell
+ * the system to avoid pruning them from the [ComplicationData], where they will show up in the
+ * relevant fields next to the evaluated values.
  */
 public abstract class WatchFaceService : WallpaperService() {
 
@@ -514,9 +521,7 @@
     internal open fun createComplicationSlotsManagerInternal(
         currentUserStyleRepository: CurrentUserStyleRepository,
         resourceOnlyWatchFacePackageName: String?
-    ): ComplicationSlotsManager = createComplicationSlotsManager(
-        currentUserStyleRepository
-    )
+    ): ComplicationSlotsManager = createComplicationSlotsManager(currentUserStyleRepository)
 
     /**
      * Used when inflating [ComplicationSlot]s from XML to provide a
@@ -587,10 +592,8 @@
         currentUserStyleRepository: CurrentUserStyleRepository,
         complicationSlotsManager: ComplicationSlotsManager,
         resourceOnlyWatchFacePackageName: String?
-    ): UserStyleFlavors = createUserStyleFlavors(
-        currentUserStyleRepository,
-        complicationSlotsManager
-    )
+    ): UserStyleFlavors =
+        createUserStyleFlavors(currentUserStyleRepository, complicationSlotsManager)
 
     /**
      * Override this factory method to create your WatchFaceImpl. This method will be called by the
@@ -624,12 +627,13 @@
         complicationSlotsManager: ComplicationSlotsManager,
         currentUserStyleRepository: CurrentUserStyleRepository,
         resourceOnlyWatchFacePackageName: String?
-    ): WatchFace = createWatchFace(
-        surfaceHolder,
-        watchState,
-        complicationSlotsManager,
-        currentUserStyleRepository,
-    )
+    ): WatchFace =
+        createWatchFace(
+            surfaceHolder,
+            watchState,
+            complicationSlotsManager,
+            currentUserStyleRepository,
+        )
 
     /** Creates an interactive engine for WallpaperService. */
     final override fun onCreateEngine(): Engine =
@@ -1316,7 +1320,12 @@
         internal var immutableSystemStateDone = false
         internal var immutableChinHeightDone = false
         internal var systemHasSentWatchUiState = false
-        internal var resourceOnlyWatchFacePackageName: String? = headlessComponentName?.packageName
+        internal var resourceOnlyWatchFacePackageName: String? =
+            if (this@WatchFaceService is WatchFaceRuntimeService) {
+                headlessComponentName?.packageName
+            } else {
+                null
+            }
 
         private var asyncWatchFaceConstructionPending = false
 
@@ -1962,8 +1971,9 @@
         @WorkerThread
         internal fun getComplicationSlotMetadataWireFormats() =
             createComplicationSlotsManagerInternal(
-                CurrentUserStyleRepository(
-                    createUserStyleSchemaInternal(resourceOnlyWatchFacePackageName)),
+                    CurrentUserStyleRepository(
+                        createUserStyleSchemaInternal(resourceOnlyWatchFacePackageName)
+                    ),
                     resourceOnlyWatchFacePackageName
                 )
                 .complicationSlots
@@ -2098,7 +2108,10 @@
                 setWatchUiState(params.watchUiState, fromSysUi = false)
                 initialUserStyle = params.userStyle
 
-                resourceOnlyWatchFacePackageName = params.auxiliaryComponentPackageName
+                // For a resource only watch face, the auxiliaryComponentPackageName will be null.
+                if (this@WatchFaceService is WatchFaceRuntimeService) {
+                    resourceOnlyWatchFacePackageName = params.auxiliaryComponentPackageName
+                }
 
                 mutableWatchState.watchFaceInstanceId.value = sanitizeWatchFaceId(params.instanceId)
                 val watchState = mutableWatchState.asWatchState()
@@ -2162,68 +2175,81 @@
                 }
 
             backgroundThreadCoroutineScope.launch {
-                val timeBefore = System.currentTimeMillis()
-                val currentUserStyleRepository =
-                    TraceEvent("WatchFaceService.createUserStyleSchema").use {
-                        CurrentUserStyleRepository(
-                            createUserStyleSchemaInternal(resourceOnlyWatchFacePackageName)
-                        )
-                    }
-                initStyle(currentUserStyleRepository)
-
-                val complicationSlotsManager =
-                    TraceEvent("WatchFaceService.createComplicationsManager").use {
-                        createComplicationSlotsManagerInternal(
-                            currentUserStyleRepository,
-                            resourceOnlyWatchFacePackageName
-                        )
-                    }
-                complicationSlotsManager.watchFaceHostApi = this@EngineWrapper
-                complicationSlotsManager.watchState = watchState
-                complicationSlotsManager.listenForStyleChanges(uiThreadCoroutineScope)
-                listenForComplicationChanges(complicationSlotsManager)
-                if (!watchState.isHeadless) {
-                    periodicallyWriteComplicationDataCache(
-                        _context,
-                        watchState.watchFaceInstanceId.value,
-                        complicationsFlow
-                    )
-                }
-
-                val userStyleFlavors =
-                    TraceEvent("WatchFaceService.createUserStyleFlavors").use {
-                        createUserStyleFlavorsInternal(
-                            currentUserStyleRepository,
-                            complicationSlotsManager,
-                            resourceOnlyWatchFacePackageName
-                        )
-                    }
-
-                deferredEarlyInitDetails.complete(
-                    EarlyInitDetails(
-                        complicationSlotsManager,
-                        currentUserStyleRepository,
-                        userStyleFlavors
-                    )
-                )
-
+                // deferred objects used to signal to the UI thread that some init steps have
+                // finished
                 val deferredWatchFace = CompletableDeferred<WatchFace>()
                 val initComplicationsDone = CompletableDeferred<Unit>()
 
-                // WatchFaceImpl (which registers broadcast observers) needs to be constructed
-                // on the UIThread. Part of this process can be done in parallel with
-                // createWatchFace.
-                uiThreadCoroutineScope.launch {
-                    createWatchFaceImpl(
-                        complicationSlotsManager,
-                        currentUserStyleRepository,
+                // add here all the deferred values completed in the background thread
+                val futuresToCancelOnError =
+                    listOf(
                         deferredWatchFace,
                         initComplicationsDone,
-                        watchState
+                        deferredEarlyInitDetails,
+                        [email protected],
+                        deferredWatchFaceImpl,
+                        deferredValidation,
                     )
-                }
 
                 try {
+                    val timeBefore = System.currentTimeMillis()
+                    val currentUserStyleRepository =
+                        TraceEvent("WatchFaceService.createUserStyleSchema").use {
+                            CurrentUserStyleRepository(
+                                createUserStyleSchemaInternal(resourceOnlyWatchFacePackageName)
+                            )
+                        }
+                    initStyle(currentUserStyleRepository)
+
+                    val complicationSlotsManager =
+                        TraceEvent("WatchFaceService.createComplicationsManager").use {
+                            createComplicationSlotsManagerInternal(
+                                currentUserStyleRepository,
+                                resourceOnlyWatchFacePackageName
+                            )
+                        }
+                    complicationSlotsManager.watchFaceHostApi = this@EngineWrapper
+                    complicationSlotsManager.watchState = watchState
+                    complicationSlotsManager.listenForStyleChanges(uiThreadCoroutineScope)
+                    listenForComplicationChanges(complicationSlotsManager)
+                    if (!watchState.isHeadless) {
+                        periodicallyWriteComplicationDataCache(
+                            _context,
+                            watchState.watchFaceInstanceId.value,
+                            complicationsFlow
+                        )
+                    }
+
+                    val userStyleFlavors =
+                        TraceEvent("WatchFaceService.createUserStyleFlavors").use {
+                            createUserStyleFlavorsInternal(
+                                currentUserStyleRepository,
+                                complicationSlotsManager,
+                                resourceOnlyWatchFacePackageName
+                            )
+                        }
+
+                    deferredEarlyInitDetails.complete(
+                        EarlyInitDetails(
+                            complicationSlotsManager,
+                            currentUserStyleRepository,
+                            userStyleFlavors
+                        )
+                    )
+
+                    // WatchFaceImpl (which registers broadcast observers) needs to be constructed
+                    // on the UIThread. Part of this process can be done in parallel with
+                    // createWatchFace.
+                    uiThreadCoroutineScope.launch {
+                        createWatchFaceImpl(
+                            complicationSlotsManager,
+                            currentUserStyleRepository,
+                            deferredWatchFace,
+                            initComplicationsDone,
+                            watchState
+                        )
+                    }
+
                     val surfaceHolder = overrideSurfaceHolder ?: deferredSurfaceHolder.await()
 
                     val watchFace =
@@ -2274,7 +2300,11 @@
                     throw e
                 } catch (e: Exception) {
                     Log.e(TAG, "WatchFace crashed during init", e)
-                    deferredValidation.completeExceptionally(e)
+                    futuresToCancelOnError.forEach {
+                        if (!it.isCompleted) {
+                            it.completeExceptionally(e)
+                        }
+                    }
                 }
 
                 deferredValidation.complete(Unit)
@@ -2583,7 +2613,7 @@
 
         internal fun onEngineDetached() {
             synchronized(lock) {
-                forEachListener("onWatchFaceColorsChanged") {
+                forEachListener("onEngineDetached") {
                     if (it.apiVersion >= 2) {
                         it.onEngineDetached()
                     }
@@ -2902,12 +2932,10 @@
  * WatchFaceRuntimeService is a special kind of [WatchFaceService], which loads the watch face
  * definition from another resource only watch face package (see the
  * `resourceOnlyWatchFacePackageName` parameter passed to [createUserStyleSchema],
- * [createComplicationSlotsManager], [createUserStyleFlavors] and
- * [createWatchFace]).
+ * [createComplicationSlotsManager], [createUserStyleFlavors] and [createWatchFace]).
  *
  * Note because a WatchFaceRuntimeService loads it's resources from another package, it will need
  * the following permission:
- *
  * ```
  *     <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
  *         tools:ignore="QueryAllPackagesPermission" />
@@ -2968,10 +2996,11 @@
     internal override fun createComplicationSlotsManagerInternal(
         currentUserStyleRepository: CurrentUserStyleRepository,
         resourceOnlyWatchFacePackageName: String?
-    ): ComplicationSlotsManager = createComplicationSlotsManager(
-        currentUserStyleRepository,
-        resourceOnlyWatchFacePackageName!!
-    )
+    ): ComplicationSlotsManager =
+        createComplicationSlotsManager(
+            currentUserStyleRepository,
+            resourceOnlyWatchFacePackageName!!
+        )
 
     @Suppress("DocumentExceptions") // NB this method isn't expected to be called from user code.
     final override fun createComplicationSlotsManager(
@@ -3007,11 +3036,12 @@
         currentUserStyleRepository: CurrentUserStyleRepository,
         complicationSlotsManager: ComplicationSlotsManager,
         resourceOnlyWatchFacePackageName: String?
-    ): UserStyleFlavors = createUserStyleFlavors(
-        currentUserStyleRepository,
-        complicationSlotsManager,
-        resourceOnlyWatchFacePackageName!!
-    )
+    ): UserStyleFlavors =
+        createUserStyleFlavors(
+            currentUserStyleRepository,
+            complicationSlotsManager,
+            resourceOnlyWatchFacePackageName!!
+        )
 
     @Suppress("DocumentExceptions") // NB this method isn't expected to be called from user code.
     final override fun createUserStyleFlavors(
@@ -3056,13 +3086,14 @@
         complicationSlotsManager: ComplicationSlotsManager,
         currentUserStyleRepository: CurrentUserStyleRepository,
         resourceOnlyWatchFacePackageName: String?
-    ): WatchFace = createWatchFace(
-        surfaceHolder,
-        watchState,
-        complicationSlotsManager,
-        currentUserStyleRepository,
-        resourceOnlyWatchFacePackageName!!
-    )
+    ): WatchFace =
+        createWatchFace(
+            surfaceHolder,
+            watchState,
+            complicationSlotsManager,
+            currentUserStyleRepository,
+            resourceOnlyWatchFacePackageName!!
+        )
 
     @Suppress("DocumentExceptions") // NB this method isn't expected to be called from user code.
     final override suspend fun createWatchFace(
diff --git a/wear/watchface/watchface/src/main/res/values/attrs.xml b/wear/watchface/watchface/src/main/res/values/attrs.xml
index 3e8d8b2..02dacba 100644
--- a/wear/watchface/watchface/src/main/res/values/attrs.xml
+++ b/wear/watchface/watchface/src/main/res/values/attrs.xml
@@ -80,6 +80,7 @@
         <enum name="DATA_SOURCE_DAY_OF_WEEK" value="13" />
         <enum name="DATA_SOURCE_FAVORITE_CONTACT" value="14" />
         <enum name="DATA_SOURCE_DAY_AND_DATE" value="16" />
+        <enum name="DATA_SOURCE_WEATHER" value="17" />
     </attr>
 
     <!-- Required. The default [ComplicationType] for the default complication data source.
diff --git a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
index d55140d..3a6c4ec 100644
--- a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
+++ b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
@@ -88,6 +88,7 @@
 import androidx.wear.watchface.data.WatchUiState
 import androidx.wear.watchface.style.CurrentUserStyleRepository
 import androidx.wear.watchface.style.UserStyle
+import androidx.wear.watchface.style.UserStyleFlavors
 import androidx.wear.watchface.style.UserStyleSchema
 import androidx.wear.watchface.style.UserStyleSetting
 import androidx.wear.watchface.style.UserStyleSetting.ComplicationSlotsUserStyleSetting
@@ -6549,11 +6550,12 @@
     @Test
     public fun createHeadlessSessionDelegate_onDestroy() {
         val context = ApplicationProvider.getApplicationContext<Context>()
-        val componentName = ComponentName(context, TestNopCanvasWatchFaceService::class.java)
+        val componentName =
+            ComponentName(context, TestNopCanvasWatchFaceServiceWithHandler::class.java)
         lateinit var delegate: WatchFace.EditorDelegate
 
         // Allows us to programmatically control tasks.
-        TestNopCanvasWatchFaceService.handler = this.handler
+        TestNopCanvasWatchFaceServiceWithHandler.handler = this.handler
 
         CoroutineScope(handler.asCoroutineDispatcher().immediate).launch {
             delegate =
@@ -6609,7 +6611,7 @@
         )
 
         // Allows us to programmatically control tasks.
-        TestNopCanvasWatchFaceService.handler = this.handler
+        TestNopCanvasWatchFaceServiceWithHandler.handler = this.handler
 
         CoroutineScope(handler.asCoroutineDispatcher().immediate).launch {
             delegate =
@@ -6709,6 +6711,115 @@
             .isEqualTo(INTERACTIVE_INSTANCE_ID)
     }
 
+    @Test
+    @Config(sdk = [Build.VERSION_CODES.R])
+    @RequiresApi(Build.VERSION_CODES.R)
+    public fun interactive_wf_has_null_resourceOnlyWatchFacePackageName() {
+        initWallpaperInteractiveWatchFaceInstance(
+            WatchFaceType.ANALOG,
+            emptyList(),
+            UserStyleSchema(emptyList()),
+            WallpaperInteractiveWatchFaceInstanceParams(
+                INTERACTIVE_INSTANCE_ID,
+                DeviceConfig(false, false, 0, 0),
+                WatchUiState(false, 0),
+                UserStyle(emptyMap()).toWireFormat(),
+                null,
+                null,
+                null
+            )
+        )
+
+        assertThat(engineWrapper.resourceOnlyWatchFacePackageName).isNull()
+    }
+
+    @Test
+    @Config(sdk = [Build.VERSION_CODES.R])
+    @RequiresApi(Build.VERSION_CODES.O_MR1)
+    public fun headless_wf_has_null_resourceOnlyWatchFacePackageName() {
+        val service = TestNopCanvasWatchFaceService(context)
+        val componentName = ComponentName("test.watchface.app", "test.watchface.class")
+
+        engineWrapper =
+            service.createHeadlessEngine(componentName) as WatchFaceService.EngineWrapper
+        engineWrapper.createHeadlessInstance(
+            HeadlessWatchFaceInstanceParams(
+                componentName,
+                DeviceConfig(false, false, 100, 200),
+                100,
+                100,
+                null
+            )
+        )
+
+        assertThat(engineWrapper.resourceOnlyWatchFacePackageName).isNull()
+    }
+
+    @Test
+    @Config(sdk = [Build.VERSION_CODES.R])
+    @RequiresApi(Build.VERSION_CODES.R)
+    public fun interactive_wf_runtime_has_non_null_resourceOnlyWatchFacePackageName() {
+        val service = TestNopWatchFaceRuntimeService(context)
+        InteractiveInstanceManager
+            .getExistingInstanceOrSetPendingWallpaperInteractiveWatchFaceInstance(
+                InteractiveInstanceManager.PendingWallpaperInteractiveWatchFaceInstance(
+                    WallpaperInteractiveWatchFaceInstanceParams(
+                        SYSTEM_SUPPORTS_CONSISTENT_IDS_PREFIX + "Interactive",
+                        DeviceConfig(false, false, 0, 0),
+                        WatchUiState(false, 0),
+                        UserStyle(emptyMap()).toWireFormat(),
+                        emptyList(),
+                        "com.resource.only.package",
+                        null
+                    ),
+                    object : IPendingInteractiveWatchFace.Stub() {
+                        override fun getApiVersion() = IPendingInteractiveWatchFace.API_VERSION
+
+                        override fun onInteractiveWatchFaceCreated(
+                            iInteractiveWatchFace: IInteractiveWatchFace
+                        ) {
+                            interactiveWatchFaceInstance = iInteractiveWatchFace
+                        }
+
+                        override fun onInteractiveWatchFaceCrashed(exception: CrashInfoParcel?) {
+                            fail("WatchFace crashed: $exception")
+                        }
+                    }
+                )
+            )
+
+        engineWrapper = service.onCreateEngine() as WatchFaceService.EngineWrapper
+        engineWrapper.onCreate(surfaceHolder)
+        engineWrapper.onSurfaceChanged(surfaceHolder, 0, 100, 100)
+
+        assertThat(engineWrapper.resourceOnlyWatchFacePackageName)
+            .isEqualTo("com.resource.only.package")
+        engineWrapper.onDestroy()
+    }
+
+    @Test
+    @Config(sdk = [Build.VERSION_CODES.R])
+    @RequiresApi(Build.VERSION_CODES.O_MR1)
+    public fun headless_wf_runtime_has_non_null_resourceOnlyWatchFacePackageName() {
+        val service = TestNopWatchFaceRuntimeService(context)
+        val componentName = ComponentName("com.resource.only.package", "null")
+
+        engineWrapper =
+            service.createHeadlessEngine(componentName) as WatchFaceService.EngineWrapper
+        engineWrapper.createHeadlessInstance(
+            HeadlessWatchFaceInstanceParams(
+                componentName,
+                DeviceConfig(false, false, 100, 200),
+                100,
+                100,
+                null
+            )
+        )
+
+        assertThat(engineWrapper.resourceOnlyWatchFacePackageName)
+            .isEqualTo("com.resource.only.package")
+    }
+
     private fun getLeftShortTextComplicationDataText(): CharSequence {
         val complication =
             complicationSlotsManager[LEFT_COMPLICATION_ID]!!.complicationData.value
@@ -6743,7 +6854,115 @@
     private suspend fun <T> Deferred<T>.awaitWithTimeout(): T = withTimeout(1000) { await() }
 }
 
-class TestNopCanvasWatchFaceService : WatchFaceService() {
+class TestNopWatchFaceRuntimeService(testContext: Context) : WatchFaceRuntimeService() {
+    var lastResourceOnlyWatchFacePackageName: String? = null
+
+    init {
+        attachBaseContext(testContext)
+    }
+
+    override fun createUserStyleSchema(resourceOnlyWatchFacePackageName: String) =
+        UserStyleSchema(emptyList())
+
+    override fun createComplicationSlotsManager(
+        currentUserStyleRepository: CurrentUserStyleRepository,
+        resourceOnlyWatchFacePackageName: String
+    ) = ComplicationSlotsManager(emptyList(), currentUserStyleRepository)
+
+    override fun createUserStyleFlavors(
+        currentUserStyleRepository: CurrentUserStyleRepository,
+        complicationSlotsManager: ComplicationSlotsManager,
+        resourceOnlyWatchFacePackageName: String
+    ) = UserStyleFlavors()
+
+    override suspend fun createWatchFace(
+        surfaceHolder: SurfaceHolder,
+        watchState: WatchState,
+        complicationSlotsManager: ComplicationSlotsManager,
+        currentUserStyleRepository: CurrentUserStyleRepository,
+        resourceOnlyWatchFacePackageName: String
+    ): WatchFace {
+        lastResourceOnlyWatchFacePackageName = resourceOnlyWatchFacePackageName
+        System.out.println("<<< createWatchFace " + resourceOnlyWatchFacePackageName)
+        return WatchFace(
+            WatchFaceType.DIGITAL,
+            @Suppress("deprecation")
+            object :
+                Renderer.CanvasRenderer(
+                    surfaceHolder,
+                    currentUserStyleRepository,
+                    watchState,
+                    CanvasType.HARDWARE,
+                    16
+                ) {
+                override fun render(canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime) {
+                    // Intentionally empty.
+                }
+
+                override fun renderHighlightLayer(
+                    canvas: Canvas,
+                    bounds: Rect,
+                    zonedDateTime: ZonedDateTime
+                ) {
+                    // Intentionally empty.
+                }
+            }
+        )
+    }
+
+    override fun getSystemTimeProvider() =
+        object : SystemTimeProvider {
+            override fun getSystemTimeMillis() = 123456789L
+
+            override fun getSystemTimeZoneId() = ZoneId.of("UTC")
+        }
+}
+
+class TestNopCanvasWatchFaceService(testContext: Context) : WatchFaceService() {
+    init {
+        attachBaseContext(testContext)
+    }
+
+    override suspend fun createWatchFace(
+        surfaceHolder: SurfaceHolder,
+        watchState: WatchState,
+        complicationSlotsManager: ComplicationSlotsManager,
+        currentUserStyleRepository: CurrentUserStyleRepository
+    ) =
+        WatchFace(
+            WatchFaceType.DIGITAL,
+            @Suppress("deprecation")
+            object :
+                Renderer.CanvasRenderer(
+                    surfaceHolder,
+                    currentUserStyleRepository,
+                    watchState,
+                    CanvasType.HARDWARE,
+                    16
+                ) {
+                override fun render(canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime) {
+                    // Intentionally empty.
+                }
+
+                override fun renderHighlightLayer(
+                    canvas: Canvas,
+                    bounds: Rect,
+                    zonedDateTime: ZonedDateTime
+                ) {
+                    // Intentionally empty.
+                }
+            }
+        )
+
+    override fun getSystemTimeProvider() =
+        object : SystemTimeProvider {
+            override fun getSystemTimeMillis() = 123456789L
+
+            override fun getSystemTimeZoneId() = ZoneId.of("UTC")
+        }
+}
+
+class TestNopCanvasWatchFaceServiceWithHandler : WatchFaceService() {
     companion object {
         lateinit var handler: Handler
     }
@@ -6796,6 +7015,6 @@
 @RequiresApi(27)
 class TestWatchFaceControlService : WatchFaceControlService() {
     override fun createWatchFaceService(watchFaceName: ComponentName): WatchFaceService? {
-        return TestNopCanvasWatchFaceService()
+        return TestNopCanvasWatchFaceServiceWithHandler()
     }
 }
diff --git a/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/PhoneTypeHelperTest.kt b/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/PhoneTypeHelperTest.kt
index c67bdca..4a5b80f0 100644
--- a/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/PhoneTypeHelperTest.kt
+++ b/wear/wear-phone-interactions/src/test/java/androidx/wear/phone/interactions/PhoneTypeHelperTest.kt
@@ -26,12 +26,13 @@
 import androidx.wear.phone.interactions.PhoneTypeHelper.Companion.getPhoneDeviceType
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers
 import org.mockito.Mock
 import org.mockito.Mockito
-import org.mockito.MockitoAnnotations
+import org.mockito.junit.MockitoJUnit
 import org.robolectric.annotation.Config
 import org.robolectric.annotation.internal.DoNotInstrument
 import org.robolectric.shadows.ShadowContentResolver
@@ -45,14 +46,15 @@
         .path(PhoneTypeHelper.BLUETOOTH_MODE)
         .build()
 
+    @get:Rule
+    val mocks = MockitoJUnit.rule()
+
     @Mock
     var mockContentProvider: ContentProvider? = null
     private var contentResolver: ContentResolver? = null
 
-    @Suppress("DEPRECATION") // b/251211092
     @Before
     fun setUp() {
-        MockitoAnnotations.openMocks(this)
         ShadowContentResolver.registerProviderInternal(
             PhoneTypeHelper.SETTINGS_AUTHORITY,
             mockContentProvider
diff --git a/wear/wear/src/androidTest/java/androidx/wear/widget/ConfirmationOverlayTest.java b/wear/wear/src/androidTest/java/androidx/wear/widget/ConfirmationOverlayTest.java
index b732229..c0be286 100644
--- a/wear/wear/src/androidTest/java/androidx/wear/widget/ConfirmationOverlayTest.java
+++ b/wear/wear/src/androidTest/java/androidx/wear/widget/ConfirmationOverlayTest.java
@@ -34,12 +34,12 @@
 import androidx.wear.R;
 import androidx.wear.widget.util.WakeLockRule;
 
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -55,17 +55,14 @@
     public final ActivityTestRule<ConfirmationOverlayTestActivity> mActivityRule =
             new ActivityTestRule<>(ConfirmationOverlayTestActivity.class, true, true);
 
+    @Rule public final MockitoRule mocks = MockitoJUnit.rule();
+
     @Mock
     private ConfirmationOverlay.OnAnimationFinishedListener mOnAnimationFinishedListener;
 
     private LinearLayout mLinearLayout;
     private TextView mActualTextView;
 
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-    }
-
     private Activity setupActivity() {
         Activity activity = mActivityRule.getActivity();
         mLinearLayout = new LinearLayout(activity);
diff --git a/wear/wear/src/androidTest/java/androidx/wear/widget/ScrollManagerTest.java b/wear/wear/src/androidTest/java/androidx/wear/widget/ScrollManagerTest.java
index 8ce380f..9c78ac1 100644
--- a/wear/wear/src/androidTest/java/androidx/wear/widget/ScrollManagerTest.java
+++ b/wear/wear/src/androidTest/java/androidx/wear/widget/ScrollManagerTest.java
@@ -36,7 +36,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 
 @MediumTest
 @RunWith(AndroidJUnit4.class)
@@ -55,6 +56,8 @@
     public final ActivityTestRule<WearableRecyclerViewTestActivity> mActivityRule =
             new ActivityTestRule<>(WearableRecyclerViewTestActivity.class, true, true);
 
+    @Rule public final MockitoRule mocks = MockitoJUnit.rule();
+
     @Mock
     WearableRecyclerView mMockWearableRecyclerView;
 
@@ -62,7 +65,6 @@
 
     @Before
     public void setUp() throws Throwable {
-        MockitoAnnotations.initMocks(this);
         mScrollManagerUnderTest = new ScrollManager();
         mScrollManagerUnderTest.setRecyclerView(mMockWearableRecyclerView, TEST_WIDTH, TEST_HEIGHT);
     }
diff --git a/wear/wear/src/androidTest/java/androidx/wear/widget/WearableRecyclerViewTest.java b/wear/wear/src/androidTest/java/androidx/wear/widget/WearableRecyclerViewTest.java
index 03e34be..ad4bf84 100644
--- a/wear/wear/src/androidTest/java/androidx/wear/widget/WearableRecyclerViewTest.java
+++ b/wear/wear/src/androidTest/java/androidx/wear/widget/WearableRecyclerViewTest.java
@@ -45,12 +45,12 @@
 import androidx.wear.test.R;
 import androidx.wear.widget.util.WakeLockRule;
 
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 
 @LargeTest
 @RunWith(AndroidJUnit4.class)
@@ -67,10 +67,7 @@
     public final ActivityScenarioRule<WearableRecyclerViewTestActivity> mActivityRule =
             new ActivityScenarioRule<>(WearableRecyclerViewTestActivity.class);
 
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-    }
+    @Rule public final MockitoRule mocks = MockitoJUnit.rule();
 
     @Test
     public void testCaseInitState() {
diff --git a/wear/wear/src/androidTest/java/androidx/wear/widget/drawer/WearableDrawerLayoutEspressoTest.java b/wear/wear/src/androidTest/java/androidx/wear/widget/drawer/WearableDrawerLayoutEspressoTest.java
index 0f61106..7327b13 100644
--- a/wear/wear/src/androidTest/java/androidx/wear/widget/drawer/WearableDrawerLayoutEspressoTest.java
+++ b/wear/wear/src/androidTest/java/androidx/wear/widget/drawer/WearableDrawerLayoutEspressoTest.java
@@ -64,12 +64,12 @@
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
 import org.hamcrest.TypeSafeMatcher;
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 
 import java.util.concurrent.TimeoutException;
 
@@ -91,12 +91,10 @@
     private final Intent mSinglePageIntent =
             new DrawerTestActivity.Builder().setStyle(DrawerStyle.BOTH_DRAWER_NAV_SINGLE_PAGE)
                     .build();
-    @Mock WearableNavigationDrawerView.OnItemSelectedListener mNavDrawerItemSelectedListener;
 
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-    }
+    @Rule public final MockitoRule mocks = MockitoJUnit.rule();
+
+    @Mock WearableNavigationDrawerView.OnItemSelectedListener mNavDrawerItemSelectedListener;
 
     @Test
     public void openingNavigationDrawerDoesNotCloseActionDrawer() {
diff --git a/wear/wear/src/test/java/androidx/wear/utils/WearTypeHelperTest.java b/wear/wear/src/test/java/androidx/wear/utils/WearTypeHelperTest.java
index 47c60499..33f4219 100644
--- a/wear/wear/src/test/java/androidx/wear/utils/WearTypeHelperTest.java
+++ b/wear/wear/src/test/java/androidx/wear/utils/WearTypeHelperTest.java
@@ -26,7 +26,6 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.MockitoAnnotations;
 import org.robolectric.Shadows;
 import org.robolectric.annotation.Config;
 import org.robolectric.annotation.internal.DoNotInstrument;
@@ -38,10 +37,8 @@
     private ShadowPackageManager mShadowPackageManager = null;
     private Context mContext;
 
-    @SuppressWarnings("deprecation") // b/251211092
     @Before
     public void setUp() {
-        MockitoAnnotations.openMocks(this);
         mContext = ApplicationProvider.getApplicationContext();
         mShadowPackageManager = Shadows.shadowOf(mContext.getPackageManager());
     }
diff --git a/webkit/integration-tests/instrumentation/src/androidTest/java/androidx/webkit/internal/AssetHelperTest.java b/webkit/integration-tests/instrumentation/src/androidTest/java/androidx/webkit/internal/AssetHelperTest.java
index 8a9d2e1..95ab916 100644
--- a/webkit/integration-tests/instrumentation/src/androidTest/java/androidx/webkit/internal/AssetHelperTest.java
+++ b/webkit/integration-tests/instrumentation/src/androidTest/java/androidx/webkit/internal/AssetHelperTest.java
@@ -35,6 +35,8 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+import java.net.FileNameMap;
+import java.net.URLConnection;
 
 @RunWith(AndroidJUnit4.class)
 public class AssetHelperTest {
@@ -43,6 +45,7 @@
     private static final String TEST_STRING = "Just a test";
     private AssetHelper mAssetHelper;
     private File mInternalStorageTestDir;
+    private FileNameMap mDefaultFileNameMap;
 
     @Before
     public void setup() {
@@ -50,11 +53,13 @@
         mAssetHelper = new AssetHelper(context);
         mInternalStorageTestDir = new File(context.getFilesDir(), "test_dir");
         mInternalStorageTestDir.mkdirs();
+        mDefaultFileNameMap = URLConnection.getFileNameMap();
     }
 
     @After
     public void tearDown() {
         WebkitUtils.recursivelyDeleteFile(mInternalStorageTestDir);
+        URLConnection.setFileNameMap(mDefaultFileNameMap);
     }
 
     @Test
@@ -220,6 +225,48 @@
         }
     }
 
+    @Test
+    @SmallTest
+    public void testGuessMimeType() {
+        // First check the OS
+        Assert.assertEquals("text/plain", AssetHelper.guessMimeType("aFile.txt"));
+
+        // Then check the first item in our list
+        Assert.assertEquals("video/webm", AssetHelper.guessMimeType("AVideoFile.webm"));
+        // A random list item
+        Assert.assertEquals("application/xhtml+xml", AssetHelper.guessMimeType("TestMimeFile.xht"));
+        // A file path doesn't cause issues
+        Assert.assertEquals(
+                "application/xhtml+xml", AssetHelper.guessMimeType("a/path/to/TestMimeFile.xht"));
+        // A file path doesn't cause issues
+        Assert.assertEquals(
+                "application/xhtml+xml", AssetHelper.guessMimeType("a/path/to/TestMimeFile.xht"));
+
+        // Check case insensitive
+        Assert.assertEquals("video/mpeg", AssetHelper.guessMimeType("aVideo.mPG"));
+
+        // Check a few error conditions fallback to default
+        Assert.assertEquals("text/plain", AssetHelper.guessMimeType(null));
+        Assert.assertEquals("text/plain", AssetHelper.guessMimeType("No full stop!"));
+        Assert.assertEquals("text/plain", AssetHelper.guessMimeType("file."));
+        Assert.assertEquals("text/plain", AssetHelper.guessMimeType("A.myownfiletype"));
+
+        // We added this because javascript mime types weren't being handled
+        // correctly so also adding a test for that to be safe
+        Assert.assertEquals("application/javascript", AssetHelper.guessMimeType("a js file.js"));
+
+        // Check that overridden mime map is prioritized
+        final String expectedMime = "test/mime";
+
+        URLConnection.setFileNameMap(new FileNameMap() {
+            @Override
+            public String getContentTypeFor(String fileName) {
+                return expectedMime;
+            }
+        });
+        Assert.assertEquals(expectedMime, AssetHelper.guessMimeType("aFile.txt"));
+    }
+
     private InputStream assertOpen(String path) throws IOException {
         InputStream stream = mAssetHelper.openAsset(path);
         Assert.assertNotNull("Failed to open \"" + path + "\"", stream);
diff --git a/webkit/webkit/api/1.8.0-beta01.txt b/webkit/webkit/api/1.8.0-beta01.txt
new file mode 100644
index 0000000..748c00e
--- /dev/null
+++ b/webkit/webkit/api/1.8.0-beta01.txt
@@ -0,0 +1,320 @@
+// Signature format: 4.0
+package androidx.webkit {
+
+  public class CookieManagerCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_COOKIE_INFO, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static java.util.List<java.lang.String!> getCookieInfo(android.webkit.CookieManager, String);
+  }
+
+  public final class DropDataContentProvider extends android.content.ContentProvider {
+    ctor public DropDataContentProvider();
+    method public int delete(android.net.Uri, String?, String![]?);
+    method public String? getType(android.net.Uri);
+    method public android.net.Uri? insert(android.net.Uri, android.content.ContentValues?);
+    method public boolean onCreate();
+    method public android.database.Cursor? query(android.net.Uri, String![]?, String?, String![]?, String?);
+    method public int update(android.net.Uri, android.content.ContentValues?, String?, String![]?);
+  }
+
+  public abstract class JavaScriptReplyProxy {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void postMessage(byte[]);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void postMessage(String);
+  }
+
+  public class ProcessGlobalConfig {
+    ctor public ProcessGlobalConfig();
+    method public static void apply(androidx.webkit.ProcessGlobalConfig);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX, enforcement="androidx.webkit.WebViewFeature#isConfigFeatureSupported(String, Context)") public androidx.webkit.ProcessGlobalConfig setDataDirectorySuffix(android.content.Context, String);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS, enforcement="androidx.webkit.WebViewFeature#isConfigFeatureSupported(String, Context)") public androidx.webkit.ProcessGlobalConfig setDirectoryBasePaths(android.content.Context, java.io.File, java.io.File);
+  }
+
+  public final class ProxyConfig {
+    method public java.util.List<java.lang.String!> getBypassRules();
+    method public java.util.List<androidx.webkit.ProxyConfig.ProxyRule!> getProxyRules();
+    method public boolean isReverseBypassEnabled();
+    field public static final String MATCH_ALL_SCHEMES = "*";
+    field public static final String MATCH_HTTP = "http";
+    field public static final String MATCH_HTTPS = "https";
+  }
+
+  public static final class ProxyConfig.Builder {
+    ctor public ProxyConfig.Builder();
+    ctor public ProxyConfig.Builder(androidx.webkit.ProxyConfig);
+    method public androidx.webkit.ProxyConfig.Builder addBypassRule(String);
+    method public androidx.webkit.ProxyConfig.Builder addDirect();
+    method public androidx.webkit.ProxyConfig.Builder addDirect(String);
+    method public androidx.webkit.ProxyConfig.Builder addProxyRule(String);
+    method public androidx.webkit.ProxyConfig.Builder addProxyRule(String, String);
+    method public androidx.webkit.ProxyConfig build();
+    method public androidx.webkit.ProxyConfig.Builder bypassSimpleHostnames();
+    method public androidx.webkit.ProxyConfig.Builder removeImplicitRules();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.PROXY_OVERRIDE_REVERSE_BYPASS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public androidx.webkit.ProxyConfig.Builder setReverseBypassEnabled(boolean);
+  }
+
+  public static final class ProxyConfig.ProxyRule {
+    method public String getSchemeFilter();
+    method public String getUrl();
+  }
+
+  public abstract class ProxyController {
+    method public abstract void clearProxyOverride(java.util.concurrent.Executor, Runnable);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.PROXY_OVERRIDE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.ProxyController getInstance();
+    method public abstract void setProxyOverride(androidx.webkit.ProxyConfig, java.util.concurrent.Executor, Runnable);
+  }
+
+  public abstract class SafeBrowsingResponseCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void backToSafety(boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_RESPONSE_PROCEED, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void proceed(boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void showInterstitial(boolean);
+  }
+
+  public abstract class ServiceWorkerClientCompat {
+    ctor public ServiceWorkerClientCompat();
+    method @WorkerThread public abstract android.webkit.WebResourceResponse? shouldInterceptRequest(android.webkit.WebResourceRequest);
+  }
+
+  public abstract class ServiceWorkerControllerCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.ServiceWorkerControllerCompat getInstance();
+    method public abstract androidx.webkit.ServiceWorkerWebSettingsCompat getServiceWorkerWebSettings();
+    method public abstract void setServiceWorkerClient(androidx.webkit.ServiceWorkerClientCompat?);
+  }
+
+  public abstract class ServiceWorkerWebSettingsCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract boolean getAllowContentAccess();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_FILE_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract boolean getAllowFileAccess();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract boolean getBlockNetworkLoads();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CACHE_MODE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract int getCacheMode();
+    method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract java.util.Set<java.lang.String!> getRequestedWithHeaderOriginAllowList();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setAllowContentAccess(boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_FILE_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setAllowFileAccess(boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setBlockNetworkLoads(boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CACHE_MODE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setCacheMode(int);
+    method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setRequestedWithHeaderOriginAllowList(java.util.Set<java.lang.String!>);
+  }
+
+  public class TracingConfig {
+    method public java.util.List<java.lang.String!> getCustomIncludedCategories();
+    method public int getPredefinedCategories();
+    method public int getTracingMode();
+    field public static final int CATEGORIES_ALL = 1; // 0x1
+    field public static final int CATEGORIES_ANDROID_WEBVIEW = 2; // 0x2
+    field public static final int CATEGORIES_FRAME_VIEWER = 64; // 0x40
+    field public static final int CATEGORIES_INPUT_LATENCY = 8; // 0x8
+    field public static final int CATEGORIES_JAVASCRIPT_AND_RENDERING = 32; // 0x20
+    field public static final int CATEGORIES_NONE = 0; // 0x0
+    field public static final int CATEGORIES_RENDERING = 16; // 0x10
+    field public static final int CATEGORIES_WEB_DEVELOPER = 4; // 0x4
+    field public static final int RECORD_CONTINUOUSLY = 1; // 0x1
+    field public static final int RECORD_UNTIL_FULL = 0; // 0x0
+  }
+
+  public static class TracingConfig.Builder {
+    ctor public TracingConfig.Builder();
+    method public androidx.webkit.TracingConfig.Builder addCategories(int...);
+    method public androidx.webkit.TracingConfig.Builder addCategories(java.lang.String!...);
+    method public androidx.webkit.TracingConfig.Builder addCategories(java.util.Collection<java.lang.String!>);
+    method public androidx.webkit.TracingConfig build();
+    method public androidx.webkit.TracingConfig.Builder setTracingMode(int);
+  }
+
+  public abstract class TracingController {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.TRACING_CONTROLLER_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.TracingController getInstance();
+    method public abstract boolean isTracing();
+    method public abstract void start(androidx.webkit.TracingConfig);
+    method public abstract boolean stop(java.io.OutputStream?, java.util.concurrent.Executor);
+  }
+
+  public class WebMessageCompat {
+    ctor @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public WebMessageCompat(byte[]);
+    ctor @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public WebMessageCompat(byte[], androidx.webkit.WebMessagePortCompat![]?);
+    ctor public WebMessageCompat(String?);
+    ctor public WebMessageCompat(String?, androidx.webkit.WebMessagePortCompat![]?);
+    method public byte[] getArrayBuffer();
+    method public String? getData();
+    method public androidx.webkit.WebMessagePortCompat![]? getPorts();
+    method public int getType();
+    field public static final int TYPE_ARRAY_BUFFER = 1; // 0x1
+    field public static final int TYPE_STRING = 0; // 0x0
+  }
+
+  public abstract class WebMessagePortCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_CLOSE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void close();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_POST_MESSAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void postMessage(androidx.webkit.WebMessageCompat);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setWebMessageCallback(android.os.Handler?, androidx.webkit.WebMessagePortCompat.WebMessageCallbackCompat);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setWebMessageCallback(androidx.webkit.WebMessagePortCompat.WebMessageCallbackCompat);
+  }
+
+  public abstract static class WebMessagePortCompat.WebMessageCallbackCompat {
+    ctor public WebMessagePortCompat.WebMessageCallbackCompat();
+    method public void onMessage(androidx.webkit.WebMessagePortCompat, androidx.webkit.WebMessageCompat?);
+  }
+
+  public abstract class WebResourceErrorCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_RESOURCE_ERROR_GET_DESCRIPTION, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract CharSequence getDescription();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_RESOURCE_ERROR_GET_CODE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract int getErrorCode();
+  }
+
+  public class WebResourceRequestCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_RESOURCE_REQUEST_IS_REDIRECT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isRedirect(android.webkit.WebResourceRequest);
+  }
+
+  public class WebSettingsCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getDisabledActionModeMenuItems(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getEnterpriseAuthenticationAppLinkPolicyEnabled(android.webkit.WebSettings);
+    method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getForceDark(android.webkit.WebSettings);
+    method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK_STRATEGY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getForceDarkStrategy(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.OFF_SCREEN_PRERASTER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getOffscreenPreRaster(android.webkit.WebSettings);
+    method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static java.util.Set<java.lang.String!> getRequestedWithHeaderOriginAllowList(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ENABLE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getSafeBrowsingEnabled(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.ALGORITHMIC_DARKENING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isAlgorithmicDarkeningAllowed(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.ALGORITHMIC_DARKENING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setAlgorithmicDarkeningAllowed(android.webkit.WebSettings, boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setDisabledActionModeMenuItems(android.webkit.WebSettings, int);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setEnterpriseAuthenticationAppLinkPolicyEnabled(android.webkit.WebSettings, boolean);
+    method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setForceDark(android.webkit.WebSettings, int);
+    method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK_STRATEGY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setForceDarkStrategy(android.webkit.WebSettings, int);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.OFF_SCREEN_PRERASTER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setOffscreenPreRaster(android.webkit.WebSettings, boolean);
+    method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setRequestedWithHeaderOriginAllowList(android.webkit.WebSettings, java.util.Set<java.lang.String!>);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ENABLE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingEnabled(android.webkit.WebSettings, boolean);
+    field @Deprecated public static final int DARK_STRATEGY_PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING = 2; // 0x2
+    field @Deprecated public static final int DARK_STRATEGY_USER_AGENT_DARKENING_ONLY = 0; // 0x0
+    field @Deprecated public static final int DARK_STRATEGY_WEB_THEME_DARKENING_ONLY = 1; // 0x1
+    field @Deprecated public static final int FORCE_DARK_AUTO = 1; // 0x1
+    field @Deprecated public static final int FORCE_DARK_OFF = 0; // 0x0
+    field @Deprecated public static final int FORCE_DARK_ON = 2; // 0x2
+  }
+
+  public final class WebViewAssetLoader {
+    method @WorkerThread public android.webkit.WebResourceResponse? shouldInterceptRequest(android.net.Uri);
+    field public static final String DEFAULT_DOMAIN = "appassets.androidplatform.net";
+  }
+
+  public static final class WebViewAssetLoader.AssetsPathHandler implements androidx.webkit.WebViewAssetLoader.PathHandler {
+    ctor public WebViewAssetLoader.AssetsPathHandler(android.content.Context);
+    method @WorkerThread public android.webkit.WebResourceResponse? handle(String);
+  }
+
+  public static final class WebViewAssetLoader.Builder {
+    ctor public WebViewAssetLoader.Builder();
+    method public androidx.webkit.WebViewAssetLoader.Builder addPathHandler(String, androidx.webkit.WebViewAssetLoader.PathHandler);
+    method public androidx.webkit.WebViewAssetLoader build();
+    method public androidx.webkit.WebViewAssetLoader.Builder setDomain(String);
+    method public androidx.webkit.WebViewAssetLoader.Builder setHttpAllowed(boolean);
+  }
+
+  public static final class WebViewAssetLoader.InternalStoragePathHandler implements androidx.webkit.WebViewAssetLoader.PathHandler {
+    ctor public WebViewAssetLoader.InternalStoragePathHandler(android.content.Context, java.io.File);
+    method @WorkerThread public android.webkit.WebResourceResponse handle(String);
+  }
+
+  public static interface WebViewAssetLoader.PathHandler {
+    method @WorkerThread public android.webkit.WebResourceResponse? handle(String);
+  }
+
+  public static final class WebViewAssetLoader.ResourcesPathHandler implements androidx.webkit.WebViewAssetLoader.PathHandler {
+    ctor public WebViewAssetLoader.ResourcesPathHandler(android.content.Context);
+    method @WorkerThread public android.webkit.WebResourceResponse? handle(String);
+  }
+
+  public class WebViewClientCompat extends android.webkit.WebViewClient {
+    ctor public WebViewClientCompat();
+    method @RequiresApi(23) public final void onReceivedError(android.webkit.WebView, android.webkit.WebResourceRequest, android.webkit.WebResourceError);
+    method @RequiresApi(21) @UiThread public void onReceivedError(android.webkit.WebView, android.webkit.WebResourceRequest, androidx.webkit.WebResourceErrorCompat);
+    method @RequiresApi(27) public final void onSafeBrowsingHit(android.webkit.WebView, android.webkit.WebResourceRequest, int, android.webkit.SafeBrowsingResponse);
+    method @UiThread public void onSafeBrowsingHit(android.webkit.WebView, android.webkit.WebResourceRequest, int, androidx.webkit.SafeBrowsingResponseCompat);
+  }
+
+  public class WebViewCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void addWebMessageListener(android.webkit.WebView, String, java.util.Set<java.lang.String!>, androidx.webkit.WebViewCompat.WebMessageListener);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.CREATE_WEB_MESSAGE_CHANNEL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.WebMessagePortCompat![] createWebMessageChannel(android.webkit.WebView);
+    method public static android.content.pm.PackageInfo? getCurrentWebViewPackage(android.content.Context);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_PRIVACY_POLICY_URL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static android.net.Uri getSafeBrowsingPrivacyPolicyUrl();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_VARIATIONS_HEADER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static String getVariationsHeader();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_CHROME_CLIENT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static android.webkit.WebChromeClient? getWebChromeClient(android.webkit.WebView);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_VIEW_CLIENT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static android.webkit.WebViewClient getWebViewClient(android.webkit.WebView);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_VIEW_RENDERER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.WebViewRenderProcess? getWebViewRenderProcess(android.webkit.WebView);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.WebViewRenderProcessClient? getWebViewRenderProcessClient(android.webkit.WebView);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isMultiProcessEnabled();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.VISUAL_STATE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void postVisualStateCallback(android.webkit.WebView, long, androidx.webkit.WebViewCompat.VisualStateCallback);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.POST_WEB_MESSAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void postWebMessage(android.webkit.WebView, androidx.webkit.WebMessageCompat, android.net.Uri);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void removeWebMessageListener(android.webkit.WebView, String);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ALLOWLIST, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingAllowlist(java.util.Set<java.lang.String!>, android.webkit.ValueCallback<java.lang.Boolean!>?);
+    method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_WHITELIST, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingWhitelist(java.util.List<java.lang.String!>, android.webkit.ValueCallback<java.lang.Boolean!>?);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setWebViewRenderProcessClient(android.webkit.WebView, androidx.webkit.WebViewRenderProcessClient?);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setWebViewRenderProcessClient(android.webkit.WebView, java.util.concurrent.Executor, androidx.webkit.WebViewRenderProcessClient);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.START_SAFE_BROWSING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void startSafeBrowsing(android.content.Context, android.webkit.ValueCallback<java.lang.Boolean!>?);
+  }
+
+  public static interface WebViewCompat.VisualStateCallback {
+    method @UiThread public void onComplete(long);
+  }
+
+  public static interface WebViewCompat.WebMessageListener {
+    method @UiThread public void onPostMessage(android.webkit.WebView, androidx.webkit.WebMessageCompat, android.net.Uri, boolean, androidx.webkit.JavaScriptReplyProxy);
+  }
+
+  public class WebViewFeature {
+    method public static boolean isFeatureSupported(String);
+    method public static boolean isStartupFeatureSupported(android.content.Context, String);
+    field public static final String ALGORITHMIC_DARKENING = "ALGORITHMIC_DARKENING";
+    field public static final String CREATE_WEB_MESSAGE_CHANNEL = "CREATE_WEB_MESSAGE_CHANNEL";
+    field public static final String DISABLED_ACTION_MODE_MENU_ITEMS = "DISABLED_ACTION_MODE_MENU_ITEMS";
+    field public static final String ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY = "ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY";
+    field public static final String FORCE_DARK = "FORCE_DARK";
+    field public static final String FORCE_DARK_STRATEGY = "FORCE_DARK_STRATEGY";
+    field public static final String GET_COOKIE_INFO = "GET_COOKIE_INFO";
+    field public static final String GET_VARIATIONS_HEADER = "GET_VARIATIONS_HEADER";
+    field public static final String GET_WEB_CHROME_CLIENT = "GET_WEB_CHROME_CLIENT";
+    field public static final String GET_WEB_VIEW_CLIENT = "GET_WEB_VIEW_CLIENT";
+    field public static final String GET_WEB_VIEW_RENDERER = "GET_WEB_VIEW_RENDERER";
+    field public static final String MULTI_PROCESS = "MULTI_PROCESS";
+    field public static final String OFF_SCREEN_PRERASTER = "OFF_SCREEN_PRERASTER";
+    field public static final String POST_WEB_MESSAGE = "POST_WEB_MESSAGE";
+    field public static final String PROXY_OVERRIDE = "PROXY_OVERRIDE";
+    field public static final String PROXY_OVERRIDE_REVERSE_BYPASS = "PROXY_OVERRIDE_REVERSE_BYPASS";
+    field public static final String RECEIVE_HTTP_ERROR = "RECEIVE_HTTP_ERROR";
+    field public static final String RECEIVE_WEB_RESOURCE_ERROR = "RECEIVE_WEB_RESOURCE_ERROR";
+    field public static final String SAFE_BROWSING_ALLOWLIST = "SAFE_BROWSING_ALLOWLIST";
+    field public static final String SAFE_BROWSING_ENABLE = "SAFE_BROWSING_ENABLE";
+    field public static final String SAFE_BROWSING_HIT = "SAFE_BROWSING_HIT";
+    field public static final String SAFE_BROWSING_PRIVACY_POLICY_URL = "SAFE_BROWSING_PRIVACY_POLICY_URL";
+    field public static final String SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY = "SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY";
+    field public static final String SAFE_BROWSING_RESPONSE_PROCEED = "SAFE_BROWSING_RESPONSE_PROCEED";
+    field public static final String SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL = "SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL";
+    field @Deprecated public static final String SAFE_BROWSING_WHITELIST = "SAFE_BROWSING_WHITELIST";
+    field public static final String SERVICE_WORKER_BASIC_USAGE = "SERVICE_WORKER_BASIC_USAGE";
+    field public static final String SERVICE_WORKER_BLOCK_NETWORK_LOADS = "SERVICE_WORKER_BLOCK_NETWORK_LOADS";
+    field public static final String SERVICE_WORKER_CACHE_MODE = "SERVICE_WORKER_CACHE_MODE";
+    field public static final String SERVICE_WORKER_CONTENT_ACCESS = "SERVICE_WORKER_CONTENT_ACCESS";
+    field public static final String SERVICE_WORKER_FILE_ACCESS = "SERVICE_WORKER_FILE_ACCESS";
+    field public static final String SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST = "SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST";
+    field public static final String SHOULD_OVERRIDE_WITH_REDIRECTS = "SHOULD_OVERRIDE_WITH_REDIRECTS";
+    field public static final String STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX = "STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX";
+    field public static final String STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS = "STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS";
+    field public static final String START_SAFE_BROWSING = "START_SAFE_BROWSING";
+    field public static final String TRACING_CONTROLLER_BASIC_USAGE = "TRACING_CONTROLLER_BASIC_USAGE";
+    field public static final String VISUAL_STATE_CALLBACK = "VISUAL_STATE_CALLBACK";
+    field public static final String WEB_MESSAGE_ARRAY_BUFFER = "WEB_MESSAGE_ARRAY_BUFFER";
+    field public static final String WEB_MESSAGE_CALLBACK_ON_MESSAGE = "WEB_MESSAGE_CALLBACK_ON_MESSAGE";
+    field public static final String WEB_MESSAGE_LISTENER = "WEB_MESSAGE_LISTENER";
+    field public static final String WEB_MESSAGE_PORT_CLOSE = "WEB_MESSAGE_PORT_CLOSE";
+    field public static final String WEB_MESSAGE_PORT_POST_MESSAGE = "WEB_MESSAGE_PORT_POST_MESSAGE";
+    field public static final String WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK = "WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK";
+    field public static final String WEB_RESOURCE_ERROR_GET_CODE = "WEB_RESOURCE_ERROR_GET_CODE";
+    field public static final String WEB_RESOURCE_ERROR_GET_DESCRIPTION = "WEB_RESOURCE_ERROR_GET_DESCRIPTION";
+    field public static final String WEB_RESOURCE_REQUEST_IS_REDIRECT = "WEB_RESOURCE_REQUEST_IS_REDIRECT";
+    field public static final String WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE = "WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE";
+    field public static final String WEB_VIEW_RENDERER_TERMINATE = "WEB_VIEW_RENDERER_TERMINATE";
+  }
+
+  public abstract class WebViewRenderProcess {
+    ctor public WebViewRenderProcess();
+    method public abstract boolean terminate();
+  }
+
+  public abstract class WebViewRenderProcessClient {
+    ctor public WebViewRenderProcessClient();
+    method public abstract void onRenderProcessResponsive(android.webkit.WebView, androidx.webkit.WebViewRenderProcess?);
+    method public abstract void onRenderProcessUnresponsive(android.webkit.WebView, androidx.webkit.WebViewRenderProcess?);
+  }
+
+}
+
diff --git a/webkit/webkit/api/res-1.8.0-beta01.txt b/webkit/webkit/api/res-1.8.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/webkit/webkit/api/res-1.8.0-beta01.txt
diff --git a/webkit/webkit/api/restricted_1.8.0-beta01.txt b/webkit/webkit/api/restricted_1.8.0-beta01.txt
new file mode 100644
index 0000000..748c00e
--- /dev/null
+++ b/webkit/webkit/api/restricted_1.8.0-beta01.txt
@@ -0,0 +1,320 @@
+// Signature format: 4.0
+package androidx.webkit {
+
+  public class CookieManagerCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_COOKIE_INFO, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static java.util.List<java.lang.String!> getCookieInfo(android.webkit.CookieManager, String);
+  }
+
+  public final class DropDataContentProvider extends android.content.ContentProvider {
+    ctor public DropDataContentProvider();
+    method public int delete(android.net.Uri, String?, String![]?);
+    method public String? getType(android.net.Uri);
+    method public android.net.Uri? insert(android.net.Uri, android.content.ContentValues?);
+    method public boolean onCreate();
+    method public android.database.Cursor? query(android.net.Uri, String![]?, String?, String![]?, String?);
+    method public int update(android.net.Uri, android.content.ContentValues?, String?, String![]?);
+  }
+
+  public abstract class JavaScriptReplyProxy {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void postMessage(byte[]);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void postMessage(String);
+  }
+
+  public class ProcessGlobalConfig {
+    ctor public ProcessGlobalConfig();
+    method public static void apply(androidx.webkit.ProcessGlobalConfig);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX, enforcement="androidx.webkit.WebViewFeature#isConfigFeatureSupported(String, Context)") public androidx.webkit.ProcessGlobalConfig setDataDirectorySuffix(android.content.Context, String);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS, enforcement="androidx.webkit.WebViewFeature#isConfigFeatureSupported(String, Context)") public androidx.webkit.ProcessGlobalConfig setDirectoryBasePaths(android.content.Context, java.io.File, java.io.File);
+  }
+
+  public final class ProxyConfig {
+    method public java.util.List<java.lang.String!> getBypassRules();
+    method public java.util.List<androidx.webkit.ProxyConfig.ProxyRule!> getProxyRules();
+    method public boolean isReverseBypassEnabled();
+    field public static final String MATCH_ALL_SCHEMES = "*";
+    field public static final String MATCH_HTTP = "http";
+    field public static final String MATCH_HTTPS = "https";
+  }
+
+  public static final class ProxyConfig.Builder {
+    ctor public ProxyConfig.Builder();
+    ctor public ProxyConfig.Builder(androidx.webkit.ProxyConfig);
+    method public androidx.webkit.ProxyConfig.Builder addBypassRule(String);
+    method public androidx.webkit.ProxyConfig.Builder addDirect();
+    method public androidx.webkit.ProxyConfig.Builder addDirect(String);
+    method public androidx.webkit.ProxyConfig.Builder addProxyRule(String);
+    method public androidx.webkit.ProxyConfig.Builder addProxyRule(String, String);
+    method public androidx.webkit.ProxyConfig build();
+    method public androidx.webkit.ProxyConfig.Builder bypassSimpleHostnames();
+    method public androidx.webkit.ProxyConfig.Builder removeImplicitRules();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.PROXY_OVERRIDE_REVERSE_BYPASS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public androidx.webkit.ProxyConfig.Builder setReverseBypassEnabled(boolean);
+  }
+
+  public static final class ProxyConfig.ProxyRule {
+    method public String getSchemeFilter();
+    method public String getUrl();
+  }
+
+  public abstract class ProxyController {
+    method public abstract void clearProxyOverride(java.util.concurrent.Executor, Runnable);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.PROXY_OVERRIDE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.ProxyController getInstance();
+    method public abstract void setProxyOverride(androidx.webkit.ProxyConfig, java.util.concurrent.Executor, Runnable);
+  }
+
+  public abstract class SafeBrowsingResponseCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void backToSafety(boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_RESPONSE_PROCEED, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void proceed(boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void showInterstitial(boolean);
+  }
+
+  public abstract class ServiceWorkerClientCompat {
+    ctor public ServiceWorkerClientCompat();
+    method @WorkerThread public abstract android.webkit.WebResourceResponse? shouldInterceptRequest(android.webkit.WebResourceRequest);
+  }
+
+  public abstract class ServiceWorkerControllerCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.ServiceWorkerControllerCompat getInstance();
+    method public abstract androidx.webkit.ServiceWorkerWebSettingsCompat getServiceWorkerWebSettings();
+    method public abstract void setServiceWorkerClient(androidx.webkit.ServiceWorkerClientCompat?);
+  }
+
+  public abstract class ServiceWorkerWebSettingsCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract boolean getAllowContentAccess();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_FILE_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract boolean getAllowFileAccess();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract boolean getBlockNetworkLoads();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CACHE_MODE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract int getCacheMode();
+    method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract java.util.Set<java.lang.String!> getRequestedWithHeaderOriginAllowList();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setAllowContentAccess(boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_FILE_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setAllowFileAccess(boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setBlockNetworkLoads(boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CACHE_MODE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setCacheMode(int);
+    method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setRequestedWithHeaderOriginAllowList(java.util.Set<java.lang.String!>);
+  }
+
+  public class TracingConfig {
+    method public java.util.List<java.lang.String!> getCustomIncludedCategories();
+    method public int getPredefinedCategories();
+    method public int getTracingMode();
+    field public static final int CATEGORIES_ALL = 1; // 0x1
+    field public static final int CATEGORIES_ANDROID_WEBVIEW = 2; // 0x2
+    field public static final int CATEGORIES_FRAME_VIEWER = 64; // 0x40
+    field public static final int CATEGORIES_INPUT_LATENCY = 8; // 0x8
+    field public static final int CATEGORIES_JAVASCRIPT_AND_RENDERING = 32; // 0x20
+    field public static final int CATEGORIES_NONE = 0; // 0x0
+    field public static final int CATEGORIES_RENDERING = 16; // 0x10
+    field public static final int CATEGORIES_WEB_DEVELOPER = 4; // 0x4
+    field public static final int RECORD_CONTINUOUSLY = 1; // 0x1
+    field public static final int RECORD_UNTIL_FULL = 0; // 0x0
+  }
+
+  public static class TracingConfig.Builder {
+    ctor public TracingConfig.Builder();
+    method public androidx.webkit.TracingConfig.Builder addCategories(int...);
+    method public androidx.webkit.TracingConfig.Builder addCategories(java.lang.String!...);
+    method public androidx.webkit.TracingConfig.Builder addCategories(java.util.Collection<java.lang.String!>);
+    method public androidx.webkit.TracingConfig build();
+    method public androidx.webkit.TracingConfig.Builder setTracingMode(int);
+  }
+
+  public abstract class TracingController {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.TRACING_CONTROLLER_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.TracingController getInstance();
+    method public abstract boolean isTracing();
+    method public abstract void start(androidx.webkit.TracingConfig);
+    method public abstract boolean stop(java.io.OutputStream?, java.util.concurrent.Executor);
+  }
+
+  public class WebMessageCompat {
+    ctor @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public WebMessageCompat(byte[]);
+    ctor @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public WebMessageCompat(byte[], androidx.webkit.WebMessagePortCompat![]?);
+    ctor public WebMessageCompat(String?);
+    ctor public WebMessageCompat(String?, androidx.webkit.WebMessagePortCompat![]?);
+    method public byte[] getArrayBuffer();
+    method public String? getData();
+    method public androidx.webkit.WebMessagePortCompat![]? getPorts();
+    method public int getType();
+    field public static final int TYPE_ARRAY_BUFFER = 1; // 0x1
+    field public static final int TYPE_STRING = 0; // 0x0
+  }
+
+  public abstract class WebMessagePortCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_CLOSE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void close();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_POST_MESSAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void postMessage(androidx.webkit.WebMessageCompat);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setWebMessageCallback(android.os.Handler?, androidx.webkit.WebMessagePortCompat.WebMessageCallbackCompat);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setWebMessageCallback(androidx.webkit.WebMessagePortCompat.WebMessageCallbackCompat);
+  }
+
+  public abstract static class WebMessagePortCompat.WebMessageCallbackCompat {
+    ctor public WebMessagePortCompat.WebMessageCallbackCompat();
+    method public void onMessage(androidx.webkit.WebMessagePortCompat, androidx.webkit.WebMessageCompat?);
+  }
+
+  public abstract class WebResourceErrorCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_RESOURCE_ERROR_GET_DESCRIPTION, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract CharSequence getDescription();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_RESOURCE_ERROR_GET_CODE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract int getErrorCode();
+  }
+
+  public class WebResourceRequestCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_RESOURCE_REQUEST_IS_REDIRECT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isRedirect(android.webkit.WebResourceRequest);
+  }
+
+  public class WebSettingsCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getDisabledActionModeMenuItems(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getEnterpriseAuthenticationAppLinkPolicyEnabled(android.webkit.WebSettings);
+    method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getForceDark(android.webkit.WebSettings);
+    method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK_STRATEGY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getForceDarkStrategy(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.OFF_SCREEN_PRERASTER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getOffscreenPreRaster(android.webkit.WebSettings);
+    method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static java.util.Set<java.lang.String!> getRequestedWithHeaderOriginAllowList(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ENABLE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getSafeBrowsingEnabled(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.ALGORITHMIC_DARKENING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isAlgorithmicDarkeningAllowed(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.ALGORITHMIC_DARKENING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setAlgorithmicDarkeningAllowed(android.webkit.WebSettings, boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setDisabledActionModeMenuItems(android.webkit.WebSettings, int);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setEnterpriseAuthenticationAppLinkPolicyEnabled(android.webkit.WebSettings, boolean);
+    method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setForceDark(android.webkit.WebSettings, int);
+    method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK_STRATEGY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setForceDarkStrategy(android.webkit.WebSettings, int);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.OFF_SCREEN_PRERASTER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setOffscreenPreRaster(android.webkit.WebSettings, boolean);
+    method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setRequestedWithHeaderOriginAllowList(android.webkit.WebSettings, java.util.Set<java.lang.String!>);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ENABLE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingEnabled(android.webkit.WebSettings, boolean);
+    field @Deprecated public static final int DARK_STRATEGY_PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING = 2; // 0x2
+    field @Deprecated public static final int DARK_STRATEGY_USER_AGENT_DARKENING_ONLY = 0; // 0x0
+    field @Deprecated public static final int DARK_STRATEGY_WEB_THEME_DARKENING_ONLY = 1; // 0x1
+    field @Deprecated public static final int FORCE_DARK_AUTO = 1; // 0x1
+    field @Deprecated public static final int FORCE_DARK_OFF = 0; // 0x0
+    field @Deprecated public static final int FORCE_DARK_ON = 2; // 0x2
+  }
+
+  public final class WebViewAssetLoader {
+    method @WorkerThread public android.webkit.WebResourceResponse? shouldInterceptRequest(android.net.Uri);
+    field public static final String DEFAULT_DOMAIN = "appassets.androidplatform.net";
+  }
+
+  public static final class WebViewAssetLoader.AssetsPathHandler implements androidx.webkit.WebViewAssetLoader.PathHandler {
+    ctor public WebViewAssetLoader.AssetsPathHandler(android.content.Context);
+    method @WorkerThread public android.webkit.WebResourceResponse? handle(String);
+  }
+
+  public static final class WebViewAssetLoader.Builder {
+    ctor public WebViewAssetLoader.Builder();
+    method public androidx.webkit.WebViewAssetLoader.Builder addPathHandler(String, androidx.webkit.WebViewAssetLoader.PathHandler);
+    method public androidx.webkit.WebViewAssetLoader build();
+    method public androidx.webkit.WebViewAssetLoader.Builder setDomain(String);
+    method public androidx.webkit.WebViewAssetLoader.Builder setHttpAllowed(boolean);
+  }
+
+  public static final class WebViewAssetLoader.InternalStoragePathHandler implements androidx.webkit.WebViewAssetLoader.PathHandler {
+    ctor public WebViewAssetLoader.InternalStoragePathHandler(android.content.Context, java.io.File);
+    method @WorkerThread public android.webkit.WebResourceResponse handle(String);
+  }
+
+  public static interface WebViewAssetLoader.PathHandler {
+    method @WorkerThread public android.webkit.WebResourceResponse? handle(String);
+  }
+
+  public static final class WebViewAssetLoader.ResourcesPathHandler implements androidx.webkit.WebViewAssetLoader.PathHandler {
+    ctor public WebViewAssetLoader.ResourcesPathHandler(android.content.Context);
+    method @WorkerThread public android.webkit.WebResourceResponse? handle(String);
+  }
+
+  public class WebViewClientCompat extends android.webkit.WebViewClient {
+    ctor public WebViewClientCompat();
+    method @RequiresApi(23) public final void onReceivedError(android.webkit.WebView, android.webkit.WebResourceRequest, android.webkit.WebResourceError);
+    method @RequiresApi(21) @UiThread public void onReceivedError(android.webkit.WebView, android.webkit.WebResourceRequest, androidx.webkit.WebResourceErrorCompat);
+    method @RequiresApi(27) public final void onSafeBrowsingHit(android.webkit.WebView, android.webkit.WebResourceRequest, int, android.webkit.SafeBrowsingResponse);
+    method @UiThread public void onSafeBrowsingHit(android.webkit.WebView, android.webkit.WebResourceRequest, int, androidx.webkit.SafeBrowsingResponseCompat);
+  }
+
+  public class WebViewCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void addWebMessageListener(android.webkit.WebView, String, java.util.Set<java.lang.String!>, androidx.webkit.WebViewCompat.WebMessageListener);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.CREATE_WEB_MESSAGE_CHANNEL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.WebMessagePortCompat![] createWebMessageChannel(android.webkit.WebView);
+    method public static android.content.pm.PackageInfo? getCurrentWebViewPackage(android.content.Context);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_PRIVACY_POLICY_URL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static android.net.Uri getSafeBrowsingPrivacyPolicyUrl();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_VARIATIONS_HEADER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static String getVariationsHeader();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_CHROME_CLIENT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static android.webkit.WebChromeClient? getWebChromeClient(android.webkit.WebView);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_VIEW_CLIENT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static android.webkit.WebViewClient getWebViewClient(android.webkit.WebView);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_VIEW_RENDERER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.WebViewRenderProcess? getWebViewRenderProcess(android.webkit.WebView);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.WebViewRenderProcessClient? getWebViewRenderProcessClient(android.webkit.WebView);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isMultiProcessEnabled();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.VISUAL_STATE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void postVisualStateCallback(android.webkit.WebView, long, androidx.webkit.WebViewCompat.VisualStateCallback);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.POST_WEB_MESSAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void postWebMessage(android.webkit.WebView, androidx.webkit.WebMessageCompat, android.net.Uri);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void removeWebMessageListener(android.webkit.WebView, String);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ALLOWLIST, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingAllowlist(java.util.Set<java.lang.String!>, android.webkit.ValueCallback<java.lang.Boolean!>?);
+    method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_WHITELIST, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingWhitelist(java.util.List<java.lang.String!>, android.webkit.ValueCallback<java.lang.Boolean!>?);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setWebViewRenderProcessClient(android.webkit.WebView, androidx.webkit.WebViewRenderProcessClient?);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setWebViewRenderProcessClient(android.webkit.WebView, java.util.concurrent.Executor, androidx.webkit.WebViewRenderProcessClient);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.START_SAFE_BROWSING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void startSafeBrowsing(android.content.Context, android.webkit.ValueCallback<java.lang.Boolean!>?);
+  }
+
+  public static interface WebViewCompat.VisualStateCallback {
+    method @UiThread public void onComplete(long);
+  }
+
+  public static interface WebViewCompat.WebMessageListener {
+    method @UiThread public void onPostMessage(android.webkit.WebView, androidx.webkit.WebMessageCompat, android.net.Uri, boolean, androidx.webkit.JavaScriptReplyProxy);
+  }
+
+  public class WebViewFeature {
+    method public static boolean isFeatureSupported(String);
+    method public static boolean isStartupFeatureSupported(android.content.Context, String);
+    field public static final String ALGORITHMIC_DARKENING = "ALGORITHMIC_DARKENING";
+    field public static final String CREATE_WEB_MESSAGE_CHANNEL = "CREATE_WEB_MESSAGE_CHANNEL";
+    field public static final String DISABLED_ACTION_MODE_MENU_ITEMS = "DISABLED_ACTION_MODE_MENU_ITEMS";
+    field public static final String ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY = "ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY";
+    field public static final String FORCE_DARK = "FORCE_DARK";
+    field public static final String FORCE_DARK_STRATEGY = "FORCE_DARK_STRATEGY";
+    field public static final String GET_COOKIE_INFO = "GET_COOKIE_INFO";
+    field public static final String GET_VARIATIONS_HEADER = "GET_VARIATIONS_HEADER";
+    field public static final String GET_WEB_CHROME_CLIENT = "GET_WEB_CHROME_CLIENT";
+    field public static final String GET_WEB_VIEW_CLIENT = "GET_WEB_VIEW_CLIENT";
+    field public static final String GET_WEB_VIEW_RENDERER = "GET_WEB_VIEW_RENDERER";
+    field public static final String MULTI_PROCESS = "MULTI_PROCESS";
+    field public static final String OFF_SCREEN_PRERASTER = "OFF_SCREEN_PRERASTER";
+    field public static final String POST_WEB_MESSAGE = "POST_WEB_MESSAGE";
+    field public static final String PROXY_OVERRIDE = "PROXY_OVERRIDE";
+    field public static final String PROXY_OVERRIDE_REVERSE_BYPASS = "PROXY_OVERRIDE_REVERSE_BYPASS";
+    field public static final String RECEIVE_HTTP_ERROR = "RECEIVE_HTTP_ERROR";
+    field public static final String RECEIVE_WEB_RESOURCE_ERROR = "RECEIVE_WEB_RESOURCE_ERROR";
+    field public static final String SAFE_BROWSING_ALLOWLIST = "SAFE_BROWSING_ALLOWLIST";
+    field public static final String SAFE_BROWSING_ENABLE = "SAFE_BROWSING_ENABLE";
+    field public static final String SAFE_BROWSING_HIT = "SAFE_BROWSING_HIT";
+    field public static final String SAFE_BROWSING_PRIVACY_POLICY_URL = "SAFE_BROWSING_PRIVACY_POLICY_URL";
+    field public static final String SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY = "SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY";
+    field public static final String SAFE_BROWSING_RESPONSE_PROCEED = "SAFE_BROWSING_RESPONSE_PROCEED";
+    field public static final String SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL = "SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL";
+    field @Deprecated public static final String SAFE_BROWSING_WHITELIST = "SAFE_BROWSING_WHITELIST";
+    field public static final String SERVICE_WORKER_BASIC_USAGE = "SERVICE_WORKER_BASIC_USAGE";
+    field public static final String SERVICE_WORKER_BLOCK_NETWORK_LOADS = "SERVICE_WORKER_BLOCK_NETWORK_LOADS";
+    field public static final String SERVICE_WORKER_CACHE_MODE = "SERVICE_WORKER_CACHE_MODE";
+    field public static final String SERVICE_WORKER_CONTENT_ACCESS = "SERVICE_WORKER_CONTENT_ACCESS";
+    field public static final String SERVICE_WORKER_FILE_ACCESS = "SERVICE_WORKER_FILE_ACCESS";
+    field public static final String SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST = "SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST";
+    field public static final String SHOULD_OVERRIDE_WITH_REDIRECTS = "SHOULD_OVERRIDE_WITH_REDIRECTS";
+    field public static final String STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX = "STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX";
+    field public static final String STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS = "STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS";
+    field public static final String START_SAFE_BROWSING = "START_SAFE_BROWSING";
+    field public static final String TRACING_CONTROLLER_BASIC_USAGE = "TRACING_CONTROLLER_BASIC_USAGE";
+    field public static final String VISUAL_STATE_CALLBACK = "VISUAL_STATE_CALLBACK";
+    field public static final String WEB_MESSAGE_ARRAY_BUFFER = "WEB_MESSAGE_ARRAY_BUFFER";
+    field public static final String WEB_MESSAGE_CALLBACK_ON_MESSAGE = "WEB_MESSAGE_CALLBACK_ON_MESSAGE";
+    field public static final String WEB_MESSAGE_LISTENER = "WEB_MESSAGE_LISTENER";
+    field public static final String WEB_MESSAGE_PORT_CLOSE = "WEB_MESSAGE_PORT_CLOSE";
+    field public static final String WEB_MESSAGE_PORT_POST_MESSAGE = "WEB_MESSAGE_PORT_POST_MESSAGE";
+    field public static final String WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK = "WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK";
+    field public static final String WEB_RESOURCE_ERROR_GET_CODE = "WEB_RESOURCE_ERROR_GET_CODE";
+    field public static final String WEB_RESOURCE_ERROR_GET_DESCRIPTION = "WEB_RESOURCE_ERROR_GET_DESCRIPTION";
+    field public static final String WEB_RESOURCE_REQUEST_IS_REDIRECT = "WEB_RESOURCE_REQUEST_IS_REDIRECT";
+    field public static final String WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE = "WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE";
+    field public static final String WEB_VIEW_RENDERER_TERMINATE = "WEB_VIEW_RENDERER_TERMINATE";
+  }
+
+  public abstract class WebViewRenderProcess {
+    ctor public WebViewRenderProcess();
+    method public abstract boolean terminate();
+  }
+
+  public abstract class WebViewRenderProcessClient {
+    ctor public WebViewRenderProcessClient();
+    method public abstract void onRenderProcessResponsive(android.webkit.WebView, androidx.webkit.WebViewRenderProcess?);
+    method public abstract void onRenderProcessUnresponsive(android.webkit.WebView, androidx.webkit.WebViewRenderProcess?);
+  }
+
+}
+
diff --git a/webkit/webkit/src/main/java/androidx/webkit/internal/AssetHelper.java b/webkit/webkit/src/main/java/androidx/webkit/internal/AssetHelper.java
index e6bbf7b..6a09ee4 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/internal/AssetHelper.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/internal/AssetHelper.java
@@ -30,7 +30,6 @@
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.URLConnection;
 import java.util.zip.GZIPInputStream;
 
 /**
@@ -192,7 +191,7 @@
     }
 
     /**
-     * Use {@link URLConnection#guessContentTypeFromName} to guess MIME type or return the
+     * Use {@link MimeUtil#getMimeFromFileName} to guess MIME type or return the
      * {@link DEFAULT_MIME_TYPE} if it can't guess.
      *
      * @param filePath path of the file to guess its MIME type.
@@ -200,8 +199,7 @@
      */
     @NonNull
     public static String guessMimeType(@NonNull String filePath) {
-        String mimeType = URLConnection.guessContentTypeFromName(filePath);
+        String mimeType = MimeUtil.getMimeFromFileName(filePath);
         return mimeType == null ? DEFAULT_MIME_TYPE : mimeType;
     }
-
 }
diff --git a/webkit/webkit/src/main/java/androidx/webkit/internal/MimeUtil.java b/webkit/webkit/src/main/java/androidx/webkit/internal/MimeUtil.java
new file mode 100644
index 0000000..b52a082
--- /dev/null
+++ b/webkit/webkit/src/main/java/androidx/webkit/internal/MimeUtil.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.webkit.internal;
+
+import java.net.URLConnection;
+
+class MimeUtil {
+
+    public static String getMimeFromFileName(String fileName) {
+        if (fileName == null) {
+            return null;
+        }
+
+        // Copying the logic and mapping that Chromium follows.
+        // First we check against the OS (this is a limited list by default)
+        // but app developers can extend this.
+        // We then check against a list of hardcoded mime types above if the
+        // OS didn't provide a result.
+        String mimeType = URLConnection.guessContentTypeFromName(fileName);
+
+        if (mimeType != null) {
+            return mimeType;
+        }
+
+        return guessHardcodedMime(fileName);
+    }
+
+    // We should keep this map in sync with the lists under
+    // //net/base/mime_util.cc in Chromium.
+    // A bunch of the mime types don't really apply to Android land
+    // like word docs so feel free to filter out where necessary.
+    private static String guessHardcodedMime(String fileName) {
+        int finalFullStop = fileName.lastIndexOf('.');
+        if (finalFullStop == -1) {
+            return null;
+        }
+
+        final String extension = fileName.substring(finalFullStop + 1).toLowerCase();
+
+        switch (extension) {
+            case "webm":
+                return "video/webm";
+            case "mpeg":
+            case "mpg":
+                return "video/mpeg";
+            case "mp3":
+                return "audio/mpeg";
+            case "wasm":
+                return "application/wasm";
+            case "xhtml":
+            case "xht":
+            case "xhtm":
+                return "application/xhtml+xml";
+            case "flac":
+                return "audio/flac";
+            case "ogg":
+            case "oga":
+            case "opus":
+                return "audio/ogg";
+            case "wav":
+                return "audio/wav";
+            case "m4a":
+                return "audio/x-m4a";
+            case "gif":
+                return "image/gif";
+            case "jpeg":
+            case "jpg":
+            case "jfif":
+            case "pjpeg":
+            case "pjp":
+                return "image/jpeg";
+            case "png":
+                return "image/png";
+            case "apng":
+                return "image/apng";
+            case "svg":
+            case "svgz":
+                return "image/svg+xml";
+            case "webp":
+                return "image/webp";
+            case "mht":
+            case "mhtml":
+                return "multipart/related";
+            case "css":
+                return "text/css";
+            case "html":
+            case "htm":
+            case "shtml":
+            case "shtm":
+            case "ehtml":
+                return "text/html";
+            case "js":
+            case "mjs":
+                return "application/javascript";
+            case "xml":
+                return "text/xml";
+            case "mp4":
+            case "m4v":
+                return "video/mp4";
+            case "ogv":
+            case "ogm":
+                return "video/ogg";
+            case "ico":
+                return "image/x-icon";
+            case "woff":
+                return "application/font-woff";
+            case "gz":
+            case "tgz":
+                return "application/gzip";
+            case "json":
+                return "application/json";
+            case "pdf":
+                return "application/pdf";
+            case "zip":
+                return "application/zip";
+            case "bmp":
+                return "image/bmp";
+            case "tiff":
+            case "tif":
+                return "image/tiff";
+            default:
+                return null;
+        }
+    }
+}
diff --git a/window/extensions/extensions/build.gradle b/window/extensions/extensions/build.gradle
index 5d477b7..4de2b71 100644
--- a/window/extensions/extensions/build.gradle
+++ b/window/extensions/extensions/build.gradle
@@ -23,9 +23,8 @@
 }
 
 dependencies {
-    api(libs.kotlinStdlib)
     implementation("androidx.annotation:annotation:1.6.0")
-    implementation("androidx.annotation:annotation-experimental:1.1.0")
+    implementation("androidx.annotation:annotation-experimental:1.3.1")
     implementation("androidx.window.extensions.core:core:1.0.0")
 
     testImplementation(libs.robolectric)
diff --git a/window/extensions/extensions/src/main/java/androidx/window/extensions/layout/FoldingFeature.java b/window/extensions/extensions/src/main/java/androidx/window/extensions/layout/FoldingFeature.java
index 0e74057..9f95510 100644
--- a/window/extensions/extensions/src/main/java/androidx/window/extensions/layout/FoldingFeature.java
+++ b/window/extensions/extensions/src/main/java/androidx/window/extensions/layout/FoldingFeature.java
@@ -122,11 +122,11 @@
      */
     private static void validateFeatureBounds(@NonNull Rect bounds) {
         if (bounds.width() == 0 && bounds.height() == 0) {
-            throw new IllegalArgumentException("Bounds must be non zero");
+            throw new IllegalArgumentException("Bounds must be non zero.  Bounds: " + bounds);
         }
         if (bounds.left != 0 && bounds.top != 0) {
             throw new IllegalArgumentException("Bounding rectangle must start at the top or "
-                    + "left window edge for folding features");
+                    + "left window edge for folding features.  Bounds: " + bounds);
         }
     }
 
diff --git a/window/integration-tests/macrobenchmark-target/build.gradle b/window/integration-tests/macrobenchmark-target/build.gradle
new file mode 100644
index 0000000..8bec782
--- /dev/null
+++ b/window/integration-tests/macrobenchmark-target/build.gradle
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.application")
+    id("kotlin-android")
+}
+
+android {
+    defaultConfig {
+        minSdk 28
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled true
+            shrinkResources true
+            proguardFiles getDefaultProguardFile("proguard-android-optimize.txt")
+        }
+    }
+    namespace "androidx.window.integration.macrobenchmark.target"
+}
+
+dependencies {
+    implementation("androidx.core:core-ktx")
+    implementation("androidx.startup:startup-runtime:1.1.1")
+    implementation(libs.constraintLayout)
+    implementation(libs.kotlinStdlib)
+    implementation(libs.material)
+    implementation(project(":activity:activity-ktx"))
+    implementation(project(":profileinstaller:profileinstaller"))
+    implementation(project(":window:window-java"))
+}
diff --git a/window/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml b/window/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..0b22dd2
--- /dev/null
+++ b/window/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <application
+        android:label="WM Jetpack Macrobenchmark Target"
+        android:allowBackup="false"
+        android:supportsRtl="true"
+        android:theme="@android:style/Theme.DeviceDefault"
+        tools:ignore="MissingApplicationIcon">
+
+        <!-- Profileable to enable macrobenchmark profiling -->
+        <profileable android:shell="true"/>
+
+        <property
+            android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED"
+            android:value="true" />
+        <!--
+        Activities need to be exported so the macrobenchmark can discover them
+        under the new package visibility changes for Android 11.
+         -->
+        <activity
+            android:name=".Activity1"
+            android:exported="true">
+            <intent-filter>
+                <action
+                  android:name=
+                    "androidx.window.integration.macrobenchmark.target.ACTIVITY1" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".Activity2" />
+
+        <provider android:name="androidx.startup.InitializationProvider"
+            android:authorities="${applicationId}.androidx-startup"
+            android:exported="false"
+            tools:node="merge">
+            <meta-data  android:name="androidx.window.integration.macrobenchmark.target.ActivityEmbeddingRuleInitializer"
+                android:value="androidx.startup" />
+        </provider>
+
+    </application>
+</manifest>
\ No newline at end of file
diff --git a/window/integration-tests/macrobenchmark-target/src/main/java/androidx/window/integration/macrobenchmark/target/Activity1.kt b/window/integration-tests/macrobenchmark-target/src/main/java/androidx/window/integration/macrobenchmark/target/Activity1.kt
new file mode 100644
index 0000000..60e47af
--- /dev/null
+++ b/window/integration-tests/macrobenchmark-target/src/main/java/androidx/window/integration/macrobenchmark/target/Activity1.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.integration.macrobenchmark.target
+
+import android.content.Intent
+import android.os.Bundle
+
+class Activity1 : BaseActivity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        startActivity(Intent(this, Activity2::class.java))
+    }
+
+    override fun getText() = "WM Jetpack Macrobenchmark Activity1"
+}
diff --git a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence1.java b/window/integration-tests/macrobenchmark-target/src/main/java/androidx/window/integration/macrobenchmark/target/Activity2.kt
similarity index 72%
copy from lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence1.java
copy to window/integration-tests/macrobenchmark-target/src/main/java/androidx/window/integration/macrobenchmark/target/Activity2.kt
index ae9c85f..b1224a5 100644
--- a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedSequence1.java
+++ b/window/integration-tests/macrobenchmark-target/src/main/java/androidx/window/integration/macrobenchmark/target/Activity2.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,10 +14,8 @@
  * limitations under the License.
  */
 
-package androidx.lifecycle.observers;
+package androidx.window.integration.macrobenchmark.target
 
-public class DerivedSequence1 extends Base {
-
-    public void something() {
-    }
+class Activity2 : BaseActivity() {
+    override fun getText() = "WM Jetpack Macrobenchmark Activity2"
 }
diff --git a/window/integration-tests/macrobenchmark-target/src/main/java/androidx/window/integration/macrobenchmark/target/ActivityEmbeddingRuleInitializer.kt b/window/integration-tests/macrobenchmark-target/src/main/java/androidx/window/integration/macrobenchmark/target/ActivityEmbeddingRuleInitializer.kt
new file mode 100644
index 0000000..fb770ba
--- /dev/null
+++ b/window/integration-tests/macrobenchmark-target/src/main/java/androidx/window/integration/macrobenchmark/target/ActivityEmbeddingRuleInitializer.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.integration.macrobenchmark.target
+
+import android.content.ComponentName
+import android.content.Context
+import androidx.startup.Initializer
+import androidx.window.embedding.RuleController
+import androidx.window.embedding.SplitPairFilter
+import androidx.window.embedding.SplitPairRule
+import androidx.window.embedding.SplitRule.FinishBehavior
+
+/**
+ * Initializes activity embedding rules for performance tests.
+ */
+class ActivityEmbeddingRuleInitializer : Initializer<Unit> {
+
+    override fun create(context: Context) {
+        RuleController.getInstance(context).apply {
+            if (USE_XML_RULES) {
+                setRules(RuleController.parseRules(context, R.xml.main_split_config))
+            }
+
+            repeat(DUMMY_RULE_COUNT) { index ->
+                val pairFilters: MutableSet<SplitPairFilter> = HashSet()
+                pairFilters.add(
+                    SplitPairFilter(
+                        ComponentName(context, Activity1::class.java.name),
+                        ComponentName("package $index", "cls $index"), null
+                    )
+                )
+                val rule = SplitPairRule.Builder(pairFilters)
+                    .setMinWidthDp(600)
+                    .setMinHeightDp(0)
+                    .setMinSmallestWidthDp(0)
+                    .setFinishPrimaryWithSecondary(FinishBehavior.NEVER)
+                    .setFinishSecondaryWithPrimary(FinishBehavior.NEVER)
+                    .setClearTop(true)
+                    .build()
+                addRule(rule)
+            }
+        }
+    }
+
+    override fun dependencies(): List<Class<out Initializer<*>>> {
+        return emptyList()
+    }
+
+    companion object {
+        const val DUMMY_RULE_COUNT = 0
+        const val USE_XML_RULES = true
+    }
+}
diff --git a/window/integration-tests/macrobenchmark-target/src/main/java/androidx/window/integration/macrobenchmark/target/BaseActivity.kt b/window/integration-tests/macrobenchmark-target/src/main/java/androidx/window/integration/macrobenchmark/target/BaseActivity.kt
new file mode 100644
index 0000000..0d95a064
--- /dev/null
+++ b/window/integration-tests/macrobenchmark-target/src/main/java/androidx/window/integration/macrobenchmark/target/BaseActivity.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.integration.macrobenchmark.target
+
+import android.app.Activity
+import android.os.Bundle
+import android.widget.TextView
+
+open class BaseActivity : Activity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_start)
+        findViewById<TextView>(R.id.text).text = getText()
+    }
+
+    open fun getText() = "WM Jetpack Macrobenchmark BaseActivity"
+}
diff --git a/window/integration-tests/macrobenchmark-target/src/main/java/androidx/window/integration/macrobenchmark/target/BenchmarkApplication.kt b/window/integration-tests/macrobenchmark-target/src/main/java/androidx/window/integration/macrobenchmark/target/BenchmarkApplication.kt
new file mode 100644
index 0000000..81a810b
--- /dev/null
+++ b/window/integration-tests/macrobenchmark-target/src/main/java/androidx/window/integration/macrobenchmark/target/BenchmarkApplication.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.integration.macrobenchmark.target
+
+import android.app.Application
+import androidx.startup.AppInitializer
+
+class BenchmarkApplication : Application() {
+
+    override fun onCreate() {
+        super.onCreate()
+        AppInitializer.getInstance(this)
+            .initializeComponent(ActivityEmbeddingRuleInitializer::class.java)
+    }
+}
diff --git a/window/integration-tests/macrobenchmark-target/src/main/res/layout/activity_start.xml b/window/integration-tests/macrobenchmark-target/src/main/res/layout/activity_start.xml
new file mode 100644
index 0000000..471f389
--- /dev/null
+++ b/window/integration-tests/macrobenchmark-target/src/main/res/layout/activity_start.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:padding="0dp"
+    tools:context=".BaseActivity">
+
+    <TextView
+        android:id="@+id/text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:text="Preview text" />
+
+</FrameLayout>
\ No newline at end of file
diff --git a/window/integration-tests/macrobenchmark-target/src/main/res/xml/main_split_config.xml b/window/integration-tests/macrobenchmark-target/src/main/res/xml/main_split_config.xml
new file mode 100644
index 0000000..0460570
--- /dev/null
+++ b/window/integration-tests/macrobenchmark-target/src/main/res/xml/main_split_config.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<resources
+    xmlns:window="http://schemas.android.com/apk/res-auto">
+
+    <!-- Rules for SplitActivityList -->
+
+    <SplitPairRule
+        window:finishPrimaryWithSecondary="always"
+        window:finishSecondaryWithPrimary="adjacent">
+        <SplitPairFilter
+            window:primaryActivityName="androidx.window.integration.macrobenchmark.target.Activity1"
+            window:secondaryActivityName="androidx.window.integration.macrobenchmark.target.Activity2"/>
+    </SplitPairRule>
+</resources>
\ No newline at end of file
diff --git a/window/integration-tests/macrobenchmark/build.gradle b/window/integration-tests/macrobenchmark/build.gradle
new file mode 100644
index 0000000..508f1b6
--- /dev/null
+++ b/window/integration-tests/macrobenchmark/build.gradle
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.test")
+    id("kotlin-android")
+}
+
+android {
+    defaultConfig {
+        minSdkVersion 29
+    }
+    namespace "androidx.window.integration.macrobenchmark"
+    targetProjectPath = ":window:integration-tests:macrobenchmark-target"
+    experimentalProperties["android.experimental.self-instrumenting"] = true
+}
+
+// Create a release build type and make sure it's the only one enabled.
+// This is needed because we benchmark the release build type only.
+android.buildTypes { release {} }
+androidComponents { beforeVariants(selector().all()) { enabled = buildType == 'release' } }
+
+dependencies {
+    implementation(project(":benchmark:benchmark-junit4"))
+    implementation(project(":benchmark:benchmark-macro-junit4"))
+    implementation(project(":internal-testutils-macrobenchmark"))
+    implementation(libs.testRules)
+    implementation(libs.testExtJunit)
+    implementation(libs.testCore)
+    implementation(libs.testRunner)
+    implementation(libs.testUiautomator)
+}
diff --git a/window/integration-tests/macrobenchmark/src/main/java/androidx/window/integration/macrobenchmark/ActivityEmbeddingStartupBenchmark.kt b/window/integration-tests/macrobenchmark/src/main/java/androidx/window/integration/macrobenchmark/ActivityEmbeddingStartupBenchmark.kt
new file mode 100644
index 0000000..b69f424
--- /dev/null
+++ b/window/integration-tests/macrobenchmark/src/main/java/androidx/window/integration/macrobenchmark/ActivityEmbeddingStartupBenchmark.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.integration.macrobenchmark
+
+import androidx.benchmark.macro.CompilationMode
+import androidx.benchmark.macro.StartupMode
+import androidx.benchmark.macro.junit4.MacrobenchmarkRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.testutils.measureStartup
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Performance test for Activity Embedding startup.
+ *
+ * To run the test: ./gradlew window:integration-tests:macrobenchmark:connectedReleaseAndroidTest
+ */
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class ActivityEmbeddingStartupBenchmark {
+    @get:Rule
+    val benchmarkRule = MacrobenchmarkRule()
+
+    @Test
+    fun startup() = benchmarkRule.measureStartup(
+        compilationMode = CompilationMode.DEFAULT,
+        startupMode = StartupMode.COLD,
+        packageName = "androidx.window.integration.macrobenchmark.target"
+    ) {
+        action = "androidx.window.integration.macrobenchmark.target.ACTIVITY1"
+    }
+}
diff --git a/window/window-core/api/1.2.0-beta01.txt b/window/window-core/api/1.2.0-beta01.txt
new file mode 100644
index 0000000..624b2df
--- /dev/null
+++ b/window/window-core/api/1.2.0-beta01.txt
@@ -0,0 +1,38 @@
+// Signature format: 4.0
+package androidx.window.core.layout {
+
+  public final class WindowHeightSizeClass {
+    field public static final androidx.window.core.layout.WindowHeightSizeClass COMPACT;
+    field public static final androidx.window.core.layout.WindowHeightSizeClass.Companion Companion;
+    field public static final androidx.window.core.layout.WindowHeightSizeClass EXPANDED;
+    field public static final androidx.window.core.layout.WindowHeightSizeClass MEDIUM;
+  }
+
+  public static final class WindowHeightSizeClass.Companion {
+  }
+
+  public final class WindowSizeClass {
+    method public static androidx.window.core.layout.WindowSizeClass compute(float dpWidth, float dpHeight);
+    method public androidx.window.core.layout.WindowHeightSizeClass getWindowHeightSizeClass();
+    method public androidx.window.core.layout.WindowWidthSizeClass getWindowWidthSizeClass();
+    property public final androidx.window.core.layout.WindowHeightSizeClass windowHeightSizeClass;
+    property public final androidx.window.core.layout.WindowWidthSizeClass windowWidthSizeClass;
+    field public static final androidx.window.core.layout.WindowSizeClass.Companion Companion;
+  }
+
+  public static final class WindowSizeClass.Companion {
+    method public androidx.window.core.layout.WindowSizeClass compute(float dpWidth, float dpHeight);
+  }
+
+  public final class WindowWidthSizeClass {
+    field public static final androidx.window.core.layout.WindowWidthSizeClass COMPACT;
+    field public static final androidx.window.core.layout.WindowWidthSizeClass.Companion Companion;
+    field public static final androidx.window.core.layout.WindowWidthSizeClass EXPANDED;
+    field public static final androidx.window.core.layout.WindowWidthSizeClass MEDIUM;
+  }
+
+  public static final class WindowWidthSizeClass.Companion {
+  }
+
+}
+
diff --git a/window/window-core/api/res-1.2.0-beta01.txt b/window/window-core/api/res-1.2.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/window/window-core/api/res-1.2.0-beta01.txt
diff --git a/window/window-core/api/restricted_1.2.0-beta01.txt b/window/window-core/api/restricted_1.2.0-beta01.txt
new file mode 100644
index 0000000..624b2df
--- /dev/null
+++ b/window/window-core/api/restricted_1.2.0-beta01.txt
@@ -0,0 +1,38 @@
+// Signature format: 4.0
+package androidx.window.core.layout {
+
+  public final class WindowHeightSizeClass {
+    field public static final androidx.window.core.layout.WindowHeightSizeClass COMPACT;
+    field public static final androidx.window.core.layout.WindowHeightSizeClass.Companion Companion;
+    field public static final androidx.window.core.layout.WindowHeightSizeClass EXPANDED;
+    field public static final androidx.window.core.layout.WindowHeightSizeClass MEDIUM;
+  }
+
+  public static final class WindowHeightSizeClass.Companion {
+  }
+
+  public final class WindowSizeClass {
+    method public static androidx.window.core.layout.WindowSizeClass compute(float dpWidth, float dpHeight);
+    method public androidx.window.core.layout.WindowHeightSizeClass getWindowHeightSizeClass();
+    method public androidx.window.core.layout.WindowWidthSizeClass getWindowWidthSizeClass();
+    property public final androidx.window.core.layout.WindowHeightSizeClass windowHeightSizeClass;
+    property public final androidx.window.core.layout.WindowWidthSizeClass windowWidthSizeClass;
+    field public static final androidx.window.core.layout.WindowSizeClass.Companion Companion;
+  }
+
+  public static final class WindowSizeClass.Companion {
+    method public androidx.window.core.layout.WindowSizeClass compute(float dpWidth, float dpHeight);
+  }
+
+  public final class WindowWidthSizeClass {
+    field public static final androidx.window.core.layout.WindowWidthSizeClass COMPACT;
+    field public static final androidx.window.core.layout.WindowWidthSizeClass.Companion Companion;
+    field public static final androidx.window.core.layout.WindowWidthSizeClass EXPANDED;
+    field public static final androidx.window.core.layout.WindowWidthSizeClass MEDIUM;
+  }
+
+  public static final class WindowWidthSizeClass.Companion {
+  }
+
+}
+
diff --git a/window/window-demos/demo/src/main/AndroidManifest.xml b/window/window-demos/demo/src/main/AndroidManifest.xml
index 230cb82..597cb51 100644
--- a/window/window-demos/demo/src/main/AndroidManifest.xml
+++ b/window/window-demos/demo/src/main/AndroidManifest.xml
@@ -58,7 +58,7 @@
             android:exported="false"
             android:configChanges="orientation|screenSize|screenLayout|screenSize"
             android:label="@string/window_metrics"/>
-        <activity android:name=".RearDisplayActivityConfigChanges"
+        <activity android:name=".area.RearDisplayActivityConfigChanges"
             android:exported="true"
             android:configChanges=
                 "orientation|screenLayout|screenSize|layoutDirection|smallestScreenSize"
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/RearDisplayActivityConfigChanges.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/area/RearDisplayActivityConfigChanges.kt
similarity index 98%
rename from window/window-demos/demo/src/main/java/androidx/window/demo/RearDisplayActivityConfigChanges.kt
rename to window/window-demos/demo/src/main/java/androidx/window/demo/area/RearDisplayActivityConfigChanges.kt
index 97ab277..7e12657 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/RearDisplayActivityConfigChanges.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/area/RearDisplayActivityConfigChanges.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.window.demo
+package androidx.window.demo.area
 
 import android.os.Bundle
 import androidx.appcompat.app.AppCompatActivity
@@ -33,6 +33,7 @@
 import androidx.window.area.WindowAreaInfo.Type.Companion.TYPE_REAR_FACING
 import androidx.window.area.WindowAreaSession
 import androidx.window.area.WindowAreaSessionCallback
+import androidx.window.core.ExperimentalWindowApi
 import androidx.window.demo.common.infolog.InfoLogAdapter
 import androidx.window.demo.databinding.ActivityRearDisplayBinding
 import java.text.SimpleDateFormat
@@ -52,6 +53,7 @@
  *
  * This Activity overrides configuration changes for simplicity.
  */
+@OptIn(ExperimentalWindowApi::class)
 class RearDisplayActivityConfigChanges : AppCompatActivity(), WindowAreaSessionCallback {
 
     private lateinit var windowAreaController: WindowAreaController
diff --git a/window/window-java/api/1.2.0-beta01.txt b/window/window-java/api/1.2.0-beta01.txt
new file mode 100644
index 0000000..2e19128
--- /dev/null
+++ b/window/window-java/api/1.2.0-beta01.txt
@@ -0,0 +1,32 @@
+// Signature format: 4.0
+package androidx.window.java.area {
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public final class WindowAreaControllerCallbackAdapter implements androidx.window.area.WindowAreaController {
+    ctor public WindowAreaControllerCallbackAdapter(androidx.window.area.WindowAreaController controller);
+    method public void addWindowAreaInfoListListener(java.util.concurrent.Executor executor, androidx.core.util.Consumer<java.util.List<androidx.window.area.WindowAreaInfo>> listener);
+    method public void removeWindowAreaInfoListListener(androidx.core.util.Consumer<java.util.List<androidx.window.area.WindowAreaInfo>> listener);
+  }
+
+}
+
+package androidx.window.java.embedding {
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public final class SplitControllerCallbackAdapter {
+    ctor public SplitControllerCallbackAdapter(androidx.window.embedding.SplitController controller);
+    method public void addSplitListener(android.app.Activity activity, java.util.concurrent.Executor executor, androidx.core.util.Consumer<java.util.List<androidx.window.embedding.SplitInfo>> consumer);
+    method public void removeSplitListener(androidx.core.util.Consumer<java.util.List<androidx.window.embedding.SplitInfo>> consumer);
+  }
+
+}
+
+package androidx.window.java.layout {
+
+  public final class WindowInfoTrackerCallbackAdapter implements androidx.window.layout.WindowInfoTracker {
+    ctor public WindowInfoTrackerCallbackAdapter(androidx.window.layout.WindowInfoTracker tracker);
+    method public void addWindowLayoutInfoListener(android.app.Activity activity, java.util.concurrent.Executor executor, androidx.core.util.Consumer<androidx.window.layout.WindowLayoutInfo> consumer);
+    method public void addWindowLayoutInfoListener(@UiContext android.content.Context context, java.util.concurrent.Executor executor, androidx.core.util.Consumer<androidx.window.layout.WindowLayoutInfo> consumer);
+    method public void removeWindowLayoutInfoListener(androidx.core.util.Consumer<androidx.window.layout.WindowLayoutInfo> consumer);
+  }
+
+}
+
diff --git a/window/window-java/api/current.txt b/window/window-java/api/current.txt
index 886ea88..2e19128 100644
--- a/window/window-java/api/current.txt
+++ b/window/window-java/api/current.txt
@@ -1,7 +1,7 @@
 // Signature format: 4.0
 package androidx.window.java.area {
 
-  public final class WindowAreaControllerCallbackAdapter implements androidx.window.area.WindowAreaController {
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public final class WindowAreaControllerCallbackAdapter implements androidx.window.area.WindowAreaController {
     ctor public WindowAreaControllerCallbackAdapter(androidx.window.area.WindowAreaController controller);
     method public void addWindowAreaInfoListListener(java.util.concurrent.Executor executor, androidx.core.util.Consumer<java.util.List<androidx.window.area.WindowAreaInfo>> listener);
     method public void removeWindowAreaInfoListListener(androidx.core.util.Consumer<java.util.List<androidx.window.area.WindowAreaInfo>> listener);
diff --git a/window/window-java/api/res-1.2.0-beta01.txt b/window/window-java/api/res-1.2.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/window/window-java/api/res-1.2.0-beta01.txt
diff --git a/window/window-java/api/restricted_1.2.0-beta01.txt b/window/window-java/api/restricted_1.2.0-beta01.txt
new file mode 100644
index 0000000..2e19128
--- /dev/null
+++ b/window/window-java/api/restricted_1.2.0-beta01.txt
@@ -0,0 +1,32 @@
+// Signature format: 4.0
+package androidx.window.java.area {
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public final class WindowAreaControllerCallbackAdapter implements androidx.window.area.WindowAreaController {
+    ctor public WindowAreaControllerCallbackAdapter(androidx.window.area.WindowAreaController controller);
+    method public void addWindowAreaInfoListListener(java.util.concurrent.Executor executor, androidx.core.util.Consumer<java.util.List<androidx.window.area.WindowAreaInfo>> listener);
+    method public void removeWindowAreaInfoListListener(androidx.core.util.Consumer<java.util.List<androidx.window.area.WindowAreaInfo>> listener);
+  }
+
+}
+
+package androidx.window.java.embedding {
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public final class SplitControllerCallbackAdapter {
+    ctor public SplitControllerCallbackAdapter(androidx.window.embedding.SplitController controller);
+    method public void addSplitListener(android.app.Activity activity, java.util.concurrent.Executor executor, androidx.core.util.Consumer<java.util.List<androidx.window.embedding.SplitInfo>> consumer);
+    method public void removeSplitListener(androidx.core.util.Consumer<java.util.List<androidx.window.embedding.SplitInfo>> consumer);
+  }
+
+}
+
+package androidx.window.java.layout {
+
+  public final class WindowInfoTrackerCallbackAdapter implements androidx.window.layout.WindowInfoTracker {
+    ctor public WindowInfoTrackerCallbackAdapter(androidx.window.layout.WindowInfoTracker tracker);
+    method public void addWindowLayoutInfoListener(android.app.Activity activity, java.util.concurrent.Executor executor, androidx.core.util.Consumer<androidx.window.layout.WindowLayoutInfo> consumer);
+    method public void addWindowLayoutInfoListener(@UiContext android.content.Context context, java.util.concurrent.Executor executor, androidx.core.util.Consumer<androidx.window.layout.WindowLayoutInfo> consumer);
+    method public void removeWindowLayoutInfoListener(androidx.core.util.Consumer<androidx.window.layout.WindowLayoutInfo> consumer);
+  }
+
+}
+
diff --git a/window/window-java/api/restricted_current.txt b/window/window-java/api/restricted_current.txt
index 886ea88..2e19128 100644
--- a/window/window-java/api/restricted_current.txt
+++ b/window/window-java/api/restricted_current.txt
@@ -1,7 +1,7 @@
 // Signature format: 4.0
 package androidx.window.java.area {
 
-  public final class WindowAreaControllerCallbackAdapter implements androidx.window.area.WindowAreaController {
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public final class WindowAreaControllerCallbackAdapter implements androidx.window.area.WindowAreaController {
     ctor public WindowAreaControllerCallbackAdapter(androidx.window.area.WindowAreaController controller);
     method public void addWindowAreaInfoListListener(java.util.concurrent.Executor executor, androidx.core.util.Consumer<java.util.List<androidx.window.area.WindowAreaInfo>> listener);
     method public void removeWindowAreaInfoListListener(androidx.core.util.Consumer<java.util.List<androidx.window.area.WindowAreaInfo>> listener);
diff --git a/window/window-java/src/main/java/androidx/window/java/area/WindowAreaControllerCallbackAdapter.kt b/window/window-java/src/main/java/androidx/window/java/area/WindowAreaControllerCallbackAdapter.kt
index a5697db..f461504 100644
--- a/window/window-java/src/main/java/androidx/window/java/area/WindowAreaControllerCallbackAdapter.kt
+++ b/window/window-java/src/main/java/androidx/window/java/area/WindowAreaControllerCallbackAdapter.kt
@@ -19,12 +19,14 @@
 import androidx.core.util.Consumer
 import androidx.window.area.WindowAreaController
 import androidx.window.area.WindowAreaInfo
+import androidx.window.core.ExperimentalWindowApi
 import androidx.window.java.core.CallbackToFlowAdapter
 import java.util.concurrent.Executor
 
 /**
  * An adapter for [WindowAreaController] to provide callback APIs.
  */
+@ExperimentalWindowApi
 class WindowAreaControllerCallbackAdapter private constructor(
     private val controller: WindowAreaController,
     private val callbackToFlowAdapter: CallbackToFlowAdapter
diff --git a/window/window-rxjava2/api/1.2.0-beta01.txt b/window/window-rxjava2/api/1.2.0-beta01.txt
new file mode 100644
index 0000000..5250696
--- /dev/null
+++ b/window/window-rxjava2/api/1.2.0-beta01.txt
@@ -0,0 +1,12 @@
+// Signature format: 4.0
+package androidx.window.rxjava2.layout {
+
+  public final class WindowInfoTrackerRx {
+    method public static io.reactivex.Flowable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoFlowable(androidx.window.layout.WindowInfoTracker, android.app.Activity activity);
+    method public static io.reactivex.Flowable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoFlowable(androidx.window.layout.WindowInfoTracker, @UiContext android.content.Context context);
+    method public static io.reactivex.Observable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoObservable(androidx.window.layout.WindowInfoTracker, android.app.Activity activity);
+    method public static io.reactivex.Observable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoObservable(androidx.window.layout.WindowInfoTracker, @UiContext android.content.Context context);
+  }
+
+}
+
diff --git a/window/window-rxjava2/api/res-1.2.0-beta01.txt b/window/window-rxjava2/api/res-1.2.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/window/window-rxjava2/api/res-1.2.0-beta01.txt
diff --git a/window/window-rxjava2/api/restricted_1.2.0-beta01.txt b/window/window-rxjava2/api/restricted_1.2.0-beta01.txt
new file mode 100644
index 0000000..5250696
--- /dev/null
+++ b/window/window-rxjava2/api/restricted_1.2.0-beta01.txt
@@ -0,0 +1,12 @@
+// Signature format: 4.0
+package androidx.window.rxjava2.layout {
+
+  public final class WindowInfoTrackerRx {
+    method public static io.reactivex.Flowable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoFlowable(androidx.window.layout.WindowInfoTracker, android.app.Activity activity);
+    method public static io.reactivex.Flowable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoFlowable(androidx.window.layout.WindowInfoTracker, @UiContext android.content.Context context);
+    method public static io.reactivex.Observable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoObservable(androidx.window.layout.WindowInfoTracker, android.app.Activity activity);
+    method public static io.reactivex.Observable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoObservable(androidx.window.layout.WindowInfoTracker, @UiContext android.content.Context context);
+  }
+
+}
+
diff --git a/window/window-rxjava3/api/1.2.0-beta01.txt b/window/window-rxjava3/api/1.2.0-beta01.txt
new file mode 100644
index 0000000..23510cc
--- /dev/null
+++ b/window/window-rxjava3/api/1.2.0-beta01.txt
@@ -0,0 +1,12 @@
+// Signature format: 4.0
+package androidx.window.rxjava3.layout {
+
+  public final class WindowInfoTrackerRx {
+    method public static io.reactivex.rxjava3.core.Flowable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoFlowable(androidx.window.layout.WindowInfoTracker, android.app.Activity activity);
+    method public static io.reactivex.rxjava3.core.Flowable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoFlowable(androidx.window.layout.WindowInfoTracker, @UiContext android.content.Context context);
+    method public static io.reactivex.rxjava3.core.Observable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoObservable(androidx.window.layout.WindowInfoTracker, android.app.Activity activity);
+    method public static io.reactivex.rxjava3.core.Observable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoObservable(androidx.window.layout.WindowInfoTracker, @UiContext android.content.Context context);
+  }
+
+}
+
diff --git a/window/window-rxjava3/api/res-1.2.0-beta01.txt b/window/window-rxjava3/api/res-1.2.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/window/window-rxjava3/api/res-1.2.0-beta01.txt
diff --git a/window/window-rxjava3/api/restricted_1.2.0-beta01.txt b/window/window-rxjava3/api/restricted_1.2.0-beta01.txt
new file mode 100644
index 0000000..23510cc
--- /dev/null
+++ b/window/window-rxjava3/api/restricted_1.2.0-beta01.txt
@@ -0,0 +1,12 @@
+// Signature format: 4.0
+package androidx.window.rxjava3.layout {
+
+  public final class WindowInfoTrackerRx {
+    method public static io.reactivex.rxjava3.core.Flowable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoFlowable(androidx.window.layout.WindowInfoTracker, android.app.Activity activity);
+    method public static io.reactivex.rxjava3.core.Flowable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoFlowable(androidx.window.layout.WindowInfoTracker, @UiContext android.content.Context context);
+    method public static io.reactivex.rxjava3.core.Observable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoObservable(androidx.window.layout.WindowInfoTracker, android.app.Activity activity);
+    method public static io.reactivex.rxjava3.core.Observable<androidx.window.layout.WindowLayoutInfo> windowLayoutInfoObservable(androidx.window.layout.WindowInfoTracker, @UiContext android.content.Context context);
+  }
+
+}
+
diff --git a/window/window-testing/api/1.0.0-beta01.txt b/window/window-testing/api/1.0.0-beta01.txt
index f47d528..e901896 100644
--- a/window/window-testing/api/1.0.0-beta01.txt
+++ b/window/window-testing/api/1.0.0-beta01.txt
@@ -2,16 +2,16 @@
 package androidx.window.testing.layout {
 
   public final class DisplayFeatureTesting {
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
     method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
   }
 
   public final class WindowLayoutInfoPublisherRule implements org.junit.rules.TestRule {
     ctor public WindowLayoutInfoPublisherRule();
-    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method @SuppressCompatibility @kotlinx.coroutines.ExperimentalCoroutinesApi public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
     method public void overrideWindowLayoutInfo(androidx.window.layout.WindowLayoutInfo info);
   }
 
diff --git a/window/window-testing/api/1.0.0-beta02.txt b/window/window-testing/api/1.0.0-beta02.txt
index f47d528..107f5ee 100644
--- a/window/window-testing/api/1.0.0-beta02.txt
+++ b/window/window-testing/api/1.0.0-beta02.txt
@@ -2,16 +2,21 @@
 package androidx.window.testing.layout {
 
   public final class DisplayFeatureTesting {
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
     method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
   }
 
   public final class WindowLayoutInfoPublisherRule implements org.junit.rules.TestRule {
     ctor public WindowLayoutInfoPublisherRule();
-    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method @SuppressCompatibility @kotlinx.coroutines.ExperimentalCoroutinesApi public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
     method public void overrideWindowLayoutInfo(androidx.window.layout.WindowLayoutInfo info);
   }
 
diff --git a/window/window-testing/api/1.0.0-beta03.txt b/window/window-testing/api/1.0.0-beta03.txt
index 12a7d20..b5ff664 100644
--- a/window/window-testing/api/1.0.0-beta03.txt
+++ b/window/window-testing/api/1.0.0-beta03.txt
@@ -2,11 +2,16 @@
 package androidx.window.testing.layout {
 
   public final class DisplayFeatureTesting {
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
     method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
   }
 
   public final class WindowLayoutInfoPublisherRule implements org.junit.rules.TestRule {
@@ -16,8 +21,8 @@
   }
 
   public final class WindowLayoutInfoTesting {
-    method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo(optional java.util.List<? extends androidx.window.layout.DisplayFeature> displayFeatures);
     method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo();
+    method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo(optional java.util.List<? extends androidx.window.layout.DisplayFeature> displayFeatures);
   }
 
 }
diff --git a/window/window-testing/api/1.0.0-beta04.txt b/window/window-testing/api/1.0.0-beta04.txt
index 12a7d20..9c2f43f 100644
--- a/window/window-testing/api/1.0.0-beta04.txt
+++ b/window/window-testing/api/1.0.0-beta04.txt
@@ -2,11 +2,21 @@
 package androidx.window.testing.layout {
 
   public final class DisplayFeatureTesting {
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
     method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+  }
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public final class StubWindowMetricsCalculatorRule implements org.junit.rules.TestRule {
+    ctor public StubWindowMetricsCalculatorRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
   }
 
   public final class WindowLayoutInfoPublisherRule implements org.junit.rules.TestRule {
@@ -16,8 +26,8 @@
   }
 
   public final class WindowLayoutInfoTesting {
-    method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo(optional java.util.List<? extends androidx.window.layout.DisplayFeature> displayFeatures);
     method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo();
+    method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo(optional java.util.List<? extends androidx.window.layout.DisplayFeature> displayFeatures);
   }
 
 }
diff --git a/window/window-testing/api/1.1.0-beta01.txt b/window/window-testing/api/1.1.0-beta01.txt
index 12a7d20..7cbdbd6 100644
--- a/window/window-testing/api/1.1.0-beta01.txt
+++ b/window/window-testing/api/1.1.0-beta01.txt
@@ -1,12 +1,56 @@
 // Signature format: 4.0
+package androidx.window.testing.embedding {
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public final class ActivityEmbeddingTestRule implements org.junit.rules.TestRule {
+    ctor public ActivityEmbeddingTestRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void overrideIsActivityEmbedded(android.app.Activity activity, boolean isActivityEmbedded);
+    method public void overrideSplitInfo(android.app.Activity activity, java.util.List<androidx.window.embedding.SplitInfo> splitInfoList);
+    method public void overrideSplitSupportStatus(androidx.window.embedding.SplitController.SplitSupportStatus status);
+  }
+
+  public final class TestActivityStack {
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.ActivityStack createTestActivityStack();
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.ActivityStack createTestActivityStack(optional java.util.List<? extends android.app.Activity> activitiesInProcess);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.ActivityStack createTestActivityStack(optional java.util.List<? extends android.app.Activity> activitiesInProcess, optional boolean isEmpty);
+  }
+
+  public final class TestSplitAttributesCalculatorParams {
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes, optional boolean areDefaultConstraintsSatisfied);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes, optional boolean areDefaultConstraintsSatisfied, optional String? splitRuleTag);
+  }
+
+  public final class TestSplitInfo {
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitInfo createTestSplitInfo();
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack, optional androidx.window.embedding.ActivityStack secondActivityStack);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack, optional androidx.window.embedding.ActivityStack secondActivityStack, optional androidx.window.embedding.SplitAttributes splitAttributes);
+  }
+
+}
+
 package androidx.window.testing.layout {
 
   public final class DisplayFeatureTesting {
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
     method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+  }
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public final class StubWindowMetricsCalculatorRule implements org.junit.rules.TestRule {
+    ctor public StubWindowMetricsCalculatorRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
   }
 
   public final class WindowLayoutInfoPublisherRule implements org.junit.rules.TestRule {
@@ -16,8 +60,8 @@
   }
 
   public final class WindowLayoutInfoTesting {
-    method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo(optional java.util.List<? extends androidx.window.layout.DisplayFeature> displayFeatures);
     method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo();
+    method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo(optional java.util.List<? extends androidx.window.layout.DisplayFeature> displayFeatures);
   }
 
 }
diff --git a/window/window-testing/api/1.1.0-beta02.txt b/window/window-testing/api/1.1.0-beta02.txt
index 12a7d20..7cbdbd6 100644
--- a/window/window-testing/api/1.1.0-beta02.txt
+++ b/window/window-testing/api/1.1.0-beta02.txt
@@ -1,12 +1,56 @@
 // Signature format: 4.0
+package androidx.window.testing.embedding {
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public final class ActivityEmbeddingTestRule implements org.junit.rules.TestRule {
+    ctor public ActivityEmbeddingTestRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void overrideIsActivityEmbedded(android.app.Activity activity, boolean isActivityEmbedded);
+    method public void overrideSplitInfo(android.app.Activity activity, java.util.List<androidx.window.embedding.SplitInfo> splitInfoList);
+    method public void overrideSplitSupportStatus(androidx.window.embedding.SplitController.SplitSupportStatus status);
+  }
+
+  public final class TestActivityStack {
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.ActivityStack createTestActivityStack();
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.ActivityStack createTestActivityStack(optional java.util.List<? extends android.app.Activity> activitiesInProcess);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.ActivityStack createTestActivityStack(optional java.util.List<? extends android.app.Activity> activitiesInProcess, optional boolean isEmpty);
+  }
+
+  public final class TestSplitAttributesCalculatorParams {
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes, optional boolean areDefaultConstraintsSatisfied);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes, optional boolean areDefaultConstraintsSatisfied, optional String? splitRuleTag);
+  }
+
+  public final class TestSplitInfo {
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitInfo createTestSplitInfo();
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack, optional androidx.window.embedding.ActivityStack secondActivityStack);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack, optional androidx.window.embedding.ActivityStack secondActivityStack, optional androidx.window.embedding.SplitAttributes splitAttributes);
+  }
+
+}
+
 package androidx.window.testing.layout {
 
   public final class DisplayFeatureTesting {
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
     method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+  }
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public final class StubWindowMetricsCalculatorRule implements org.junit.rules.TestRule {
+    ctor public StubWindowMetricsCalculatorRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
   }
 
   public final class WindowLayoutInfoPublisherRule implements org.junit.rules.TestRule {
@@ -16,8 +60,8 @@
   }
 
   public final class WindowLayoutInfoTesting {
-    method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo(optional java.util.List<? extends androidx.window.layout.DisplayFeature> displayFeatures);
     method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo();
+    method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo(optional java.util.List<? extends androidx.window.layout.DisplayFeature> displayFeatures);
   }
 
 }
diff --git a/window/window-testing/api/1.2.0-beta01.txt b/window/window-testing/api/1.2.0-beta01.txt
new file mode 100644
index 0000000..847a9e8
--- /dev/null
+++ b/window/window-testing/api/1.2.0-beta01.txt
@@ -0,0 +1,73 @@
+// Signature format: 4.0
+package androidx.window.testing.embedding {
+
+  public final class ActivityEmbeddingRule implements org.junit.rules.TestRule {
+    ctor public ActivityEmbeddingRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void overrideIsActivityEmbedded(android.app.Activity activity, boolean isActivityEmbedded);
+    method public void overrideSplitInfo(android.app.Activity activity, java.util.List<androidx.window.embedding.SplitInfo> splitInfoList);
+    method public void overrideSplitSupportStatus(androidx.window.embedding.SplitController.SplitSupportStatus status);
+  }
+
+  public final class TestActivityStack {
+    method public static androidx.window.embedding.ActivityStack createTestActivityStack();
+    method public static androidx.window.embedding.ActivityStack createTestActivityStack(optional java.util.List<? extends android.app.Activity> activitiesInProcess);
+    method public static androidx.window.embedding.ActivityStack createTestActivityStack(optional java.util.List<? extends android.app.Activity> activitiesInProcess, optional boolean isEmpty);
+  }
+
+  public final class TestSplitAttributesCalculatorParams {
+    method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics);
+    method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration);
+    method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo);
+    method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes);
+    method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes, optional boolean areDefaultConstraintsSatisfied);
+    method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes, optional boolean areDefaultConstraintsSatisfied, optional String? splitRuleTag);
+  }
+
+  public final class TestSplitInfo {
+    method public static androidx.window.embedding.SplitInfo createTestSplitInfo();
+    method public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack);
+    method public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack, optional androidx.window.embedding.ActivityStack secondActivityStack);
+    method public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack, optional androidx.window.embedding.ActivityStack secondActivityStack, optional androidx.window.embedding.SplitAttributes splitAttributes);
+  }
+
+}
+
+package androidx.window.testing.layout {
+
+  public final class DisplayFeatureTesting {
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional @IntRange(from=-1L) int center);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional @IntRange(from=-1L) int center, optional int size);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional @IntRange(from=-1L) int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional @IntRange(from=-1L) int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional @IntRange(from=-1L) int center);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional @IntRange(from=-1L) int center, optional int size);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional @IntRange(from=-1L) int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional @IntRange(from=-1L) int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+  }
+
+  public final class FoldingFeatureTestingConstants {
+    field public static final int FOLDING_FEATURE_CENTER_DEFAULT = -1; // 0xffffffff
+    field public static final androidx.window.testing.layout.FoldingFeatureTestingConstants INSTANCE;
+  }
+
+  public final class WindowLayoutInfoPublisherRule implements org.junit.rules.TestRule {
+    ctor public WindowLayoutInfoPublisherRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void overrideWindowLayoutInfo(androidx.window.layout.WindowLayoutInfo info);
+  }
+
+  public final class WindowLayoutInfoTesting {
+    method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo();
+    method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo(optional java.util.List<? extends androidx.window.layout.DisplayFeature> displayFeatures);
+  }
+
+  public final class WindowMetricsCalculatorRule implements org.junit.rules.TestRule {
+    ctor public WindowMetricsCalculatorRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+  }
+
+}
+
diff --git a/window/window-testing/api/res-1.2.0-beta01.txt b/window/window-testing/api/res-1.2.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/window/window-testing/api/res-1.2.0-beta01.txt
diff --git a/window/window-testing/api/restricted_1.0.0-beta01.txt b/window/window-testing/api/restricted_1.0.0-beta01.txt
index f47d528..e901896 100644
--- a/window/window-testing/api/restricted_1.0.0-beta01.txt
+++ b/window/window-testing/api/restricted_1.0.0-beta01.txt
@@ -2,16 +2,16 @@
 package androidx.window.testing.layout {
 
   public final class DisplayFeatureTesting {
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
     method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
   }
 
   public final class WindowLayoutInfoPublisherRule implements org.junit.rules.TestRule {
     ctor public WindowLayoutInfoPublisherRule();
-    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method @SuppressCompatibility @kotlinx.coroutines.ExperimentalCoroutinesApi public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
     method public void overrideWindowLayoutInfo(androidx.window.layout.WindowLayoutInfo info);
   }
 
diff --git a/window/window-testing/api/restricted_1.0.0-beta02.txt b/window/window-testing/api/restricted_1.0.0-beta02.txt
index f47d528..107f5ee 100644
--- a/window/window-testing/api/restricted_1.0.0-beta02.txt
+++ b/window/window-testing/api/restricted_1.0.0-beta02.txt
@@ -2,16 +2,21 @@
 package androidx.window.testing.layout {
 
   public final class DisplayFeatureTesting {
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
     method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
   }
 
   public final class WindowLayoutInfoPublisherRule implements org.junit.rules.TestRule {
     ctor public WindowLayoutInfoPublisherRule();
-    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method @SuppressCompatibility @kotlinx.coroutines.ExperimentalCoroutinesApi public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
     method public void overrideWindowLayoutInfo(androidx.window.layout.WindowLayoutInfo info);
   }
 
diff --git a/window/window-testing/api/restricted_1.0.0-beta03.txt b/window/window-testing/api/restricted_1.0.0-beta03.txt
index 12a7d20..b5ff664 100644
--- a/window/window-testing/api/restricted_1.0.0-beta03.txt
+++ b/window/window-testing/api/restricted_1.0.0-beta03.txt
@@ -2,11 +2,16 @@
 package androidx.window.testing.layout {
 
   public final class DisplayFeatureTesting {
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
     method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
   }
 
   public final class WindowLayoutInfoPublisherRule implements org.junit.rules.TestRule {
@@ -16,8 +21,8 @@
   }
 
   public final class WindowLayoutInfoTesting {
-    method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo(optional java.util.List<? extends androidx.window.layout.DisplayFeature> displayFeatures);
     method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo();
+    method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo(optional java.util.List<? extends androidx.window.layout.DisplayFeature> displayFeatures);
   }
 
 }
diff --git a/window/window-testing/api/restricted_1.0.0-beta04.txt b/window/window-testing/api/restricted_1.0.0-beta04.txt
index 12a7d20..9c2f43f 100644
--- a/window/window-testing/api/restricted_1.0.0-beta04.txt
+++ b/window/window-testing/api/restricted_1.0.0-beta04.txt
@@ -2,11 +2,21 @@
 package androidx.window.testing.layout {
 
   public final class DisplayFeatureTesting {
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
     method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+  }
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public final class StubWindowMetricsCalculatorRule implements org.junit.rules.TestRule {
+    ctor public StubWindowMetricsCalculatorRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
   }
 
   public final class WindowLayoutInfoPublisherRule implements org.junit.rules.TestRule {
@@ -16,8 +26,8 @@
   }
 
   public final class WindowLayoutInfoTesting {
-    method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo(optional java.util.List<? extends androidx.window.layout.DisplayFeature> displayFeatures);
     method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo();
+    method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo(optional java.util.List<? extends androidx.window.layout.DisplayFeature> displayFeatures);
   }
 
 }
diff --git a/window/window-testing/api/restricted_1.1.0-beta01.txt b/window/window-testing/api/restricted_1.1.0-beta01.txt
index 12a7d20..7cbdbd6 100644
--- a/window/window-testing/api/restricted_1.1.0-beta01.txt
+++ b/window/window-testing/api/restricted_1.1.0-beta01.txt
@@ -1,12 +1,56 @@
 // Signature format: 4.0
+package androidx.window.testing.embedding {
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public final class ActivityEmbeddingTestRule implements org.junit.rules.TestRule {
+    ctor public ActivityEmbeddingTestRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void overrideIsActivityEmbedded(android.app.Activity activity, boolean isActivityEmbedded);
+    method public void overrideSplitInfo(android.app.Activity activity, java.util.List<androidx.window.embedding.SplitInfo> splitInfoList);
+    method public void overrideSplitSupportStatus(androidx.window.embedding.SplitController.SplitSupportStatus status);
+  }
+
+  public final class TestActivityStack {
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.ActivityStack createTestActivityStack();
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.ActivityStack createTestActivityStack(optional java.util.List<? extends android.app.Activity> activitiesInProcess);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.ActivityStack createTestActivityStack(optional java.util.List<? extends android.app.Activity> activitiesInProcess, optional boolean isEmpty);
+  }
+
+  public final class TestSplitAttributesCalculatorParams {
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes, optional boolean areDefaultConstraintsSatisfied);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes, optional boolean areDefaultConstraintsSatisfied, optional String? splitRuleTag);
+  }
+
+  public final class TestSplitInfo {
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitInfo createTestSplitInfo();
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack, optional androidx.window.embedding.ActivityStack secondActivityStack);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack, optional androidx.window.embedding.ActivityStack secondActivityStack, optional androidx.window.embedding.SplitAttributes splitAttributes);
+  }
+
+}
+
 package androidx.window.testing.layout {
 
   public final class DisplayFeatureTesting {
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
     method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+  }
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public final class StubWindowMetricsCalculatorRule implements org.junit.rules.TestRule {
+    ctor public StubWindowMetricsCalculatorRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
   }
 
   public final class WindowLayoutInfoPublisherRule implements org.junit.rules.TestRule {
@@ -16,8 +60,8 @@
   }
 
   public final class WindowLayoutInfoTesting {
-    method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo(optional java.util.List<? extends androidx.window.layout.DisplayFeature> displayFeatures);
     method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo();
+    method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo(optional java.util.List<? extends androidx.window.layout.DisplayFeature> displayFeatures);
   }
 
 }
diff --git a/window/window-testing/api/restricted_1.1.0-beta02.txt b/window/window-testing/api/restricted_1.1.0-beta02.txt
index 12a7d20..7cbdbd6 100644
--- a/window/window-testing/api/restricted_1.1.0-beta02.txt
+++ b/window/window-testing/api/restricted_1.1.0-beta02.txt
@@ -1,12 +1,56 @@
 // Signature format: 4.0
+package androidx.window.testing.embedding {
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public final class ActivityEmbeddingTestRule implements org.junit.rules.TestRule {
+    ctor public ActivityEmbeddingTestRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void overrideIsActivityEmbedded(android.app.Activity activity, boolean isActivityEmbedded);
+    method public void overrideSplitInfo(android.app.Activity activity, java.util.List<androidx.window.embedding.SplitInfo> splitInfoList);
+    method public void overrideSplitSupportStatus(androidx.window.embedding.SplitController.SplitSupportStatus status);
+  }
+
+  public final class TestActivityStack {
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.ActivityStack createTestActivityStack();
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.ActivityStack createTestActivityStack(optional java.util.List<? extends android.app.Activity> activitiesInProcess);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.ActivityStack createTestActivityStack(optional java.util.List<? extends android.app.Activity> activitiesInProcess, optional boolean isEmpty);
+  }
+
+  public final class TestSplitAttributesCalculatorParams {
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes, optional boolean areDefaultConstraintsSatisfied);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes, optional boolean areDefaultConstraintsSatisfied, optional String? splitRuleTag);
+  }
+
+  public final class TestSplitInfo {
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitInfo createTestSplitInfo();
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack, optional androidx.window.embedding.ActivityStack secondActivityStack);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack, optional androidx.window.embedding.ActivityStack secondActivityStack, optional androidx.window.embedding.SplitAttributes splitAttributes);
+  }
+
+}
+
 package androidx.window.testing.layout {
 
   public final class DisplayFeatureTesting {
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
-    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
     method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+  }
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public final class StubWindowMetricsCalculatorRule implements org.junit.rules.TestRule {
+    ctor public StubWindowMetricsCalculatorRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
   }
 
   public final class WindowLayoutInfoPublisherRule implements org.junit.rules.TestRule {
@@ -16,8 +60,8 @@
   }
 
   public final class WindowLayoutInfoTesting {
-    method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo(optional java.util.List<? extends androidx.window.layout.DisplayFeature> displayFeatures);
     method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo();
+    method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo(optional java.util.List<? extends androidx.window.layout.DisplayFeature> displayFeatures);
   }
 
 }
diff --git a/window/window-testing/api/restricted_1.2.0-beta01.txt b/window/window-testing/api/restricted_1.2.0-beta01.txt
new file mode 100644
index 0000000..847a9e8
--- /dev/null
+++ b/window/window-testing/api/restricted_1.2.0-beta01.txt
@@ -0,0 +1,73 @@
+// Signature format: 4.0
+package androidx.window.testing.embedding {
+
+  public final class ActivityEmbeddingRule implements org.junit.rules.TestRule {
+    ctor public ActivityEmbeddingRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void overrideIsActivityEmbedded(android.app.Activity activity, boolean isActivityEmbedded);
+    method public void overrideSplitInfo(android.app.Activity activity, java.util.List<androidx.window.embedding.SplitInfo> splitInfoList);
+    method public void overrideSplitSupportStatus(androidx.window.embedding.SplitController.SplitSupportStatus status);
+  }
+
+  public final class TestActivityStack {
+    method public static androidx.window.embedding.ActivityStack createTestActivityStack();
+    method public static androidx.window.embedding.ActivityStack createTestActivityStack(optional java.util.List<? extends android.app.Activity> activitiesInProcess);
+    method public static androidx.window.embedding.ActivityStack createTestActivityStack(optional java.util.List<? extends android.app.Activity> activitiesInProcess, optional boolean isEmpty);
+  }
+
+  public final class TestSplitAttributesCalculatorParams {
+    method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics);
+    method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration);
+    method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo);
+    method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes);
+    method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes, optional boolean areDefaultConstraintsSatisfied);
+    method public static androidx.window.embedding.SplitAttributesCalculatorParams createTestSplitAttributesCalculatorParams(androidx.window.layout.WindowMetrics parentWindowMetrics, optional android.content.res.Configuration parentConfiguration, optional androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo, optional androidx.window.embedding.SplitAttributes defaultSplitAttributes, optional boolean areDefaultConstraintsSatisfied, optional String? splitRuleTag);
+  }
+
+  public final class TestSplitInfo {
+    method public static androidx.window.embedding.SplitInfo createTestSplitInfo();
+    method public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack);
+    method public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack, optional androidx.window.embedding.ActivityStack secondActivityStack);
+    method public static androidx.window.embedding.SplitInfo createTestSplitInfo(optional androidx.window.embedding.ActivityStack primaryActivityStack, optional androidx.window.embedding.ActivityStack secondActivityStack, optional androidx.window.embedding.SplitAttributes splitAttributes);
+  }
+
+}
+
+package androidx.window.testing.layout {
+
+  public final class DisplayFeatureTesting {
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional @IntRange(from=-1L) int center);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional @IntRange(from=-1L) int center, optional int size);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional @IntRange(from=-1L) int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.app.Activity activity, optional @IntRange(from=-1L) int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional @IntRange(from=-1L) int center);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional @IntRange(from=-1L) int center, optional int size);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional @IntRange(from=-1L) int center, optional int size, optional androidx.window.layout.FoldingFeature.State state);
+    method public static androidx.window.layout.FoldingFeature createFoldingFeature(android.graphics.Rect windowBounds, optional @IntRange(from=-1L) int center, optional int size, optional androidx.window.layout.FoldingFeature.State state, optional androidx.window.layout.FoldingFeature.Orientation orientation);
+  }
+
+  public final class FoldingFeatureTestingConstants {
+    field public static final int FOLDING_FEATURE_CENTER_DEFAULT = -1; // 0xffffffff
+    field public static final androidx.window.testing.layout.FoldingFeatureTestingConstants INSTANCE;
+  }
+
+  public final class WindowLayoutInfoPublisherRule implements org.junit.rules.TestRule {
+    ctor public WindowLayoutInfoPublisherRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void overrideWindowLayoutInfo(androidx.window.layout.WindowLayoutInfo info);
+  }
+
+  public final class WindowLayoutInfoTesting {
+    method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo();
+    method public static androidx.window.layout.WindowLayoutInfo createWindowLayoutInfo(optional java.util.List<? extends androidx.window.layout.DisplayFeature> displayFeatures);
+  }
+
+  public final class WindowMetricsCalculatorRule implements org.junit.rules.TestRule {
+    ctor public WindowMetricsCalculatorRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+  }
+
+}
+
diff --git a/window/window/api/1.2.0-beta01.txt b/window/window/api/1.2.0-beta01.txt
new file mode 100644
index 0000000..7c79bde
--- /dev/null
+++ b/window/window/api/1.2.0-beta01.txt
@@ -0,0 +1,465 @@
+// Signature format: 4.0
+package androidx.window {
+
+  public final class WindowProperties {
+    field public static final androidx.window.WindowProperties INSTANCE;
+    field public static final String PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE = "android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE";
+    field public static final String PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED = "android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED";
+    field public static final String PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED = "android.window.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED";
+    field public static final String PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE";
+    field public static final String PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES = "android.window.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES";
+  }
+
+}
+
+package androidx.window.area {
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public final class WindowAreaCapability {
+    method public androidx.window.area.WindowAreaCapability.Operation getOperation();
+    method public androidx.window.area.WindowAreaCapability.Status getStatus();
+    property public final androidx.window.area.WindowAreaCapability.Operation operation;
+    property public final androidx.window.area.WindowAreaCapability.Status status;
+  }
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static final class WindowAreaCapability.Operation {
+    field public static final androidx.window.area.WindowAreaCapability.Operation.Companion Companion;
+    field public static final androidx.window.area.WindowAreaCapability.Operation OPERATION_PRESENT_ON_AREA;
+    field public static final androidx.window.area.WindowAreaCapability.Operation OPERATION_TRANSFER_ACTIVITY_TO_AREA;
+  }
+
+  public static final class WindowAreaCapability.Operation.Companion {
+  }
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static final class WindowAreaCapability.Status {
+    field public static final androidx.window.area.WindowAreaCapability.Status.Companion Companion;
+    field public static final androidx.window.area.WindowAreaCapability.Status WINDOW_AREA_STATUS_ACTIVE;
+    field public static final androidx.window.area.WindowAreaCapability.Status WINDOW_AREA_STATUS_AVAILABLE;
+    field public static final androidx.window.area.WindowAreaCapability.Status WINDOW_AREA_STATUS_UNAVAILABLE;
+    field public static final androidx.window.area.WindowAreaCapability.Status WINDOW_AREA_STATUS_UNSUPPORTED;
+  }
+
+  public static final class WindowAreaCapability.Status.Companion {
+  }
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public interface WindowAreaController {
+    method public static androidx.window.area.WindowAreaController getOrCreate();
+    method public kotlinx.coroutines.flow.Flow<java.util.List<androidx.window.area.WindowAreaInfo>> getWindowAreaInfos();
+    method public void presentContentOnWindowArea(android.os.Binder token, android.app.Activity activity, java.util.concurrent.Executor executor, androidx.window.area.WindowAreaPresentationSessionCallback windowAreaPresentationSessionCallback);
+    method public void transferActivityToWindowArea(android.os.Binder token, android.app.Activity activity, java.util.concurrent.Executor executor, androidx.window.area.WindowAreaSessionCallback windowAreaSessionCallback);
+    property public abstract kotlinx.coroutines.flow.Flow<java.util.List<androidx.window.area.WindowAreaInfo>> windowAreaInfos;
+    field public static final androidx.window.area.WindowAreaController.Companion Companion;
+  }
+
+  public static final class WindowAreaController.Companion {
+    method public androidx.window.area.WindowAreaController getOrCreate();
+  }
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public final class WindowAreaInfo {
+    method public androidx.window.area.WindowAreaSession? getActiveSession(androidx.window.area.WindowAreaCapability.Operation operation);
+    method public androidx.window.area.WindowAreaCapability? getCapability(androidx.window.area.WindowAreaCapability.Operation operation);
+    method public androidx.window.layout.WindowMetrics getMetrics();
+    method public android.os.Binder getToken();
+    method public androidx.window.area.WindowAreaInfo.Type getType();
+    method public void setMetrics(androidx.window.layout.WindowMetrics);
+    property public final androidx.window.layout.WindowMetrics metrics;
+    property public final android.os.Binder token;
+    property public final androidx.window.area.WindowAreaInfo.Type type;
+  }
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static final class WindowAreaInfo.Type {
+    field public static final androidx.window.area.WindowAreaInfo.Type.Companion Companion;
+    field public static final androidx.window.area.WindowAreaInfo.Type TYPE_REAR_FACING;
+  }
+
+  public static final class WindowAreaInfo.Type.Companion {
+  }
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public interface WindowAreaPresentationSessionCallback {
+    method public void onContainerVisibilityChanged(boolean isVisible);
+    method public void onSessionEnded(Throwable? t);
+    method public void onSessionStarted(androidx.window.area.WindowAreaSessionPresenter session);
+  }
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public interface WindowAreaSession {
+    method public void close();
+  }
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public interface WindowAreaSessionCallback {
+    method public void onSessionEnded(Throwable? t);
+    method public void onSessionStarted(androidx.window.area.WindowAreaSession session);
+  }
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public interface WindowAreaSessionPresenter extends androidx.window.area.WindowAreaSession {
+    method public android.content.Context getContext();
+    method public void setContentView(android.view.View view);
+    property public abstract android.content.Context context;
+  }
+
+}
+
+package androidx.window.core {
+
+  @SuppressCompatibility @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level.WARNING) @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalWindowApi {
+  }
+
+}
+
+package androidx.window.embedding {
+
+  public final class ActivityEmbeddingController {
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public void finishActivityStacks(java.util.Set<androidx.window.embedding.ActivityStack> activityStacks);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public androidx.window.embedding.ActivityStack? getActivityStack(android.app.Activity activity);
+    method public static androidx.window.embedding.ActivityEmbeddingController getInstance(android.content.Context context);
+    method public boolean isActivityEmbedded(android.app.Activity activity);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public boolean isFinishingActivityStacksSupported();
+    field public static final androidx.window.embedding.ActivityEmbeddingController.Companion Companion;
+  }
+
+  public static final class ActivityEmbeddingController.Companion {
+    method public androidx.window.embedding.ActivityEmbeddingController getInstance(android.content.Context context);
+  }
+
+  public final class ActivityEmbeddingOptions {
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static boolean isSetLaunchingActivityStackSupported(android.app.ActivityOptions);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static android.app.ActivityOptions setLaunchingActivityStack(android.app.ActivityOptions, android.app.Activity activity);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static android.app.ActivityOptions setLaunchingActivityStack(android.app.ActivityOptions, android.content.Context context, androidx.window.embedding.ActivityStack activityStack);
+  }
+
+  public final class ActivityFilter {
+    ctor public ActivityFilter(android.content.ComponentName componentName, String? intentAction);
+    method public android.content.ComponentName getComponentName();
+    method public String? getIntentAction();
+    method public boolean matchesActivity(android.app.Activity activity);
+    method public boolean matchesIntent(android.content.Intent intent);
+    property public final android.content.ComponentName componentName;
+    property public final String? intentAction;
+  }
+
+  public final class ActivityRule extends androidx.window.embedding.EmbeddingRule {
+    method public boolean getAlwaysExpand();
+    method public java.util.Set<androidx.window.embedding.ActivityFilter> getFilters();
+    property public final boolean alwaysExpand;
+    property public final java.util.Set<androidx.window.embedding.ActivityFilter> filters;
+  }
+
+  public static final class ActivityRule.Builder {
+    ctor public ActivityRule.Builder(java.util.Set<androidx.window.embedding.ActivityFilter> filters);
+    method public androidx.window.embedding.ActivityRule build();
+    method public androidx.window.embedding.ActivityRule.Builder setAlwaysExpand(boolean alwaysExpand);
+    method public androidx.window.embedding.ActivityRule.Builder setTag(String? tag);
+  }
+
+  public final class ActivityStack {
+    method public operator boolean contains(android.app.Activity activity);
+    method public boolean isEmpty();
+    property public final boolean isEmpty;
+  }
+
+  public final class EmbeddingAspectRatio {
+    method public static androidx.window.embedding.EmbeddingAspectRatio ratio(@FloatRange(from=1.0, fromInclusive=false) float ratio);
+    field public static final androidx.window.embedding.EmbeddingAspectRatio ALWAYS_ALLOW;
+    field public static final androidx.window.embedding.EmbeddingAspectRatio ALWAYS_DISALLOW;
+    field public static final androidx.window.embedding.EmbeddingAspectRatio.Companion Companion;
+  }
+
+  public static final class EmbeddingAspectRatio.Companion {
+    method public androidx.window.embedding.EmbeddingAspectRatio ratio(@FloatRange(from=1.0, fromInclusive=false) float ratio);
+  }
+
+  public abstract class EmbeddingRule {
+    method public final String? getTag();
+    property public final String? tag;
+  }
+
+  public final class RuleController {
+    method public void addRule(androidx.window.embedding.EmbeddingRule rule);
+    method public void clearRules();
+    method public static androidx.window.embedding.RuleController getInstance(android.content.Context context);
+    method public java.util.Set<androidx.window.embedding.EmbeddingRule> getRules();
+    method public static java.util.Set<androidx.window.embedding.EmbeddingRule> parseRules(android.content.Context context, @XmlRes int staticRuleResourceId);
+    method public void removeRule(androidx.window.embedding.EmbeddingRule rule);
+    method public void setRules(java.util.Set<? extends androidx.window.embedding.EmbeddingRule> rules);
+    field public static final androidx.window.embedding.RuleController.Companion Companion;
+  }
+
+  public static final class RuleController.Companion {
+    method public androidx.window.embedding.RuleController getInstance(android.content.Context context);
+    method public java.util.Set<androidx.window.embedding.EmbeddingRule> parseRules(android.content.Context context, @XmlRes int staticRuleResourceId);
+  }
+
+  public final class SplitAttributes {
+    method public androidx.window.embedding.SplitAttributes.LayoutDirection getLayoutDirection();
+    method public androidx.window.embedding.SplitAttributes.SplitType getSplitType();
+    property public final androidx.window.embedding.SplitAttributes.LayoutDirection layoutDirection;
+    property public final androidx.window.embedding.SplitAttributes.SplitType splitType;
+    field public static final androidx.window.embedding.SplitAttributes.Companion Companion;
+  }
+
+  public static final class SplitAttributes.Builder {
+    ctor public SplitAttributes.Builder();
+    method public androidx.window.embedding.SplitAttributes build();
+    method public androidx.window.embedding.SplitAttributes.Builder setLayoutDirection(androidx.window.embedding.SplitAttributes.LayoutDirection layoutDirection);
+    method public androidx.window.embedding.SplitAttributes.Builder setSplitType(androidx.window.embedding.SplitAttributes.SplitType type);
+  }
+
+  public static final class SplitAttributes.Companion {
+  }
+
+  public static final class SplitAttributes.LayoutDirection {
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection BOTTOM_TO_TOP;
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection.Companion Companion;
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection LEFT_TO_RIGHT;
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection LOCALE;
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection RIGHT_TO_LEFT;
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection TOP_TO_BOTTOM;
+  }
+
+  public static final class SplitAttributes.LayoutDirection.Companion {
+  }
+
+  public static final class SplitAttributes.SplitType {
+    method public static androidx.window.embedding.SplitAttributes.SplitType ratio(@FloatRange(from=0.0, to=1.0, fromInclusive=false, toInclusive=false) float ratio);
+    field public static final androidx.window.embedding.SplitAttributes.SplitType.Companion Companion;
+    field public static final androidx.window.embedding.SplitAttributes.SplitType SPLIT_TYPE_EQUAL;
+    field public static final androidx.window.embedding.SplitAttributes.SplitType SPLIT_TYPE_EXPAND;
+    field public static final androidx.window.embedding.SplitAttributes.SplitType SPLIT_TYPE_HINGE;
+  }
+
+  public static final class SplitAttributes.SplitType.Companion {
+    method public androidx.window.embedding.SplitAttributes.SplitType ratio(@FloatRange(from=0.0, to=1.0, fromInclusive=false, toInclusive=false) float ratio);
+  }
+
+  public final class SplitAttributesCalculatorParams {
+    method public boolean getAreDefaultConstraintsSatisfied();
+    method public androidx.window.embedding.SplitAttributes getDefaultSplitAttributes();
+    method public android.content.res.Configuration getParentConfiguration();
+    method public androidx.window.layout.WindowLayoutInfo getParentWindowLayoutInfo();
+    method public androidx.window.layout.WindowMetrics getParentWindowMetrics();
+    method public String? getSplitRuleTag();
+    property public final boolean areDefaultConstraintsSatisfied;
+    property public final androidx.window.embedding.SplitAttributes defaultSplitAttributes;
+    property public final android.content.res.Configuration parentConfiguration;
+    property public final androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo;
+    property public final androidx.window.layout.WindowMetrics parentWindowMetrics;
+    property public final String? splitRuleTag;
+  }
+
+  public final class SplitController {
+    method public void clearSplitAttributesCalculator();
+    method public static androidx.window.embedding.SplitController getInstance(android.content.Context context);
+    method public androidx.window.embedding.SplitController.SplitSupportStatus getSplitSupportStatus();
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public void invalidateTopVisibleSplitAttributes();
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public boolean isInvalidatingTopVisibleSplitAttributesSupported();
+    method public boolean isSplitAttributesCalculatorSupported();
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public boolean isUpdatingSplitAttributesSupported();
+    method public void setSplitAttributesCalculator(kotlin.jvm.functions.Function1<? super androidx.window.embedding.SplitAttributesCalculatorParams,androidx.window.embedding.SplitAttributes> calculator);
+    method public kotlinx.coroutines.flow.Flow<java.util.List<androidx.window.embedding.SplitInfo>> splitInfoList(android.app.Activity activity);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public void updateSplitAttributes(androidx.window.embedding.SplitInfo splitInfo, androidx.window.embedding.SplitAttributes splitAttributes);
+    property public final androidx.window.embedding.SplitController.SplitSupportStatus splitSupportStatus;
+    field public static final androidx.window.embedding.SplitController.Companion Companion;
+  }
+
+  public static final class SplitController.Companion {
+    method public androidx.window.embedding.SplitController getInstance(android.content.Context context);
+  }
+
+  public static final class SplitController.SplitSupportStatus {
+    field public static final androidx.window.embedding.SplitController.SplitSupportStatus.Companion Companion;
+    field public static final androidx.window.embedding.SplitController.SplitSupportStatus SPLIT_AVAILABLE;
+    field public static final androidx.window.embedding.SplitController.SplitSupportStatus SPLIT_ERROR_PROPERTY_NOT_DECLARED;
+    field public static final androidx.window.embedding.SplitController.SplitSupportStatus SPLIT_UNAVAILABLE;
+  }
+
+  public static final class SplitController.SplitSupportStatus.Companion {
+  }
+
+  public final class SplitInfo {
+    method public operator boolean contains(android.app.Activity activity);
+    method public androidx.window.embedding.ActivityStack getPrimaryActivityStack();
+    method public androidx.window.embedding.ActivityStack getSecondaryActivityStack();
+    method public androidx.window.embedding.SplitAttributes getSplitAttributes();
+    property public final androidx.window.embedding.ActivityStack primaryActivityStack;
+    property public final androidx.window.embedding.ActivityStack secondaryActivityStack;
+    property public final androidx.window.embedding.SplitAttributes splitAttributes;
+  }
+
+  public final class SplitPairFilter {
+    ctor public SplitPairFilter(android.content.ComponentName primaryActivityName, android.content.ComponentName secondaryActivityName, String? secondaryActivityIntentAction);
+    method public android.content.ComponentName getPrimaryActivityName();
+    method public String? getSecondaryActivityIntentAction();
+    method public android.content.ComponentName getSecondaryActivityName();
+    method public boolean matchesActivityIntentPair(android.app.Activity primaryActivity, android.content.Intent secondaryActivityIntent);
+    method public boolean matchesActivityPair(android.app.Activity primaryActivity, android.app.Activity secondaryActivity);
+    property public final android.content.ComponentName primaryActivityName;
+    property public final String? secondaryActivityIntentAction;
+    property public final android.content.ComponentName secondaryActivityName;
+  }
+
+  public final class SplitPairRule extends androidx.window.embedding.SplitRule {
+    method public boolean getClearTop();
+    method public java.util.Set<androidx.window.embedding.SplitPairFilter> getFilters();
+    method public androidx.window.embedding.SplitRule.FinishBehavior getFinishPrimaryWithSecondary();
+    method public androidx.window.embedding.SplitRule.FinishBehavior getFinishSecondaryWithPrimary();
+    property public final boolean clearTop;
+    property public final java.util.Set<androidx.window.embedding.SplitPairFilter> filters;
+    property public final androidx.window.embedding.SplitRule.FinishBehavior finishPrimaryWithSecondary;
+    property public final androidx.window.embedding.SplitRule.FinishBehavior finishSecondaryWithPrimary;
+  }
+
+  public static final class SplitPairRule.Builder {
+    ctor public SplitPairRule.Builder(java.util.Set<androidx.window.embedding.SplitPairFilter> filters);
+    method public androidx.window.embedding.SplitPairRule build();
+    method public androidx.window.embedding.SplitPairRule.Builder setClearTop(boolean clearTop);
+    method public androidx.window.embedding.SplitPairRule.Builder setDefaultSplitAttributes(androidx.window.embedding.SplitAttributes defaultSplitAttributes);
+    method public androidx.window.embedding.SplitPairRule.Builder setFinishPrimaryWithSecondary(androidx.window.embedding.SplitRule.FinishBehavior finishPrimaryWithSecondary);
+    method public androidx.window.embedding.SplitPairRule.Builder setFinishSecondaryWithPrimary(androidx.window.embedding.SplitRule.FinishBehavior finishSecondaryWithPrimary);
+    method public androidx.window.embedding.SplitPairRule.Builder setMaxAspectRatioInLandscape(androidx.window.embedding.EmbeddingAspectRatio aspectRatio);
+    method public androidx.window.embedding.SplitPairRule.Builder setMaxAspectRatioInPortrait(androidx.window.embedding.EmbeddingAspectRatio aspectRatio);
+    method public androidx.window.embedding.SplitPairRule.Builder setMinHeightDp(@IntRange(from=0L) int minHeightDp);
+    method public androidx.window.embedding.SplitPairRule.Builder setMinSmallestWidthDp(@IntRange(from=0L) int minSmallestWidthDp);
+    method public androidx.window.embedding.SplitPairRule.Builder setMinWidthDp(@IntRange(from=0L) int minWidthDp);
+    method public androidx.window.embedding.SplitPairRule.Builder setTag(String? tag);
+  }
+
+  public final class SplitPlaceholderRule extends androidx.window.embedding.SplitRule {
+    method public java.util.Set<androidx.window.embedding.ActivityFilter> getFilters();
+    method public androidx.window.embedding.SplitRule.FinishBehavior getFinishPrimaryWithPlaceholder();
+    method public android.content.Intent getPlaceholderIntent();
+    method public boolean isSticky();
+    property public final java.util.Set<androidx.window.embedding.ActivityFilter> filters;
+    property public final androidx.window.embedding.SplitRule.FinishBehavior finishPrimaryWithPlaceholder;
+    property public final boolean isSticky;
+    property public final android.content.Intent placeholderIntent;
+  }
+
+  public static final class SplitPlaceholderRule.Builder {
+    ctor public SplitPlaceholderRule.Builder(java.util.Set<androidx.window.embedding.ActivityFilter> filters, android.content.Intent placeholderIntent);
+    method public androidx.window.embedding.SplitPlaceholderRule build();
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setDefaultSplitAttributes(androidx.window.embedding.SplitAttributes defaultSplitAttributes);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setFinishPrimaryWithPlaceholder(androidx.window.embedding.SplitRule.FinishBehavior finishPrimaryWithPlaceholder);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setMaxAspectRatioInLandscape(androidx.window.embedding.EmbeddingAspectRatio aspectRatio);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setMaxAspectRatioInPortrait(androidx.window.embedding.EmbeddingAspectRatio aspectRatio);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setMinHeightDp(@IntRange(from=0L) int minHeightDp);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setMinSmallestWidthDp(@IntRange(from=0L) int minSmallestWidthDp);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setMinWidthDp(@IntRange(from=0L) int minWidthDp);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setSticky(boolean isSticky);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setTag(String? tag);
+  }
+
+  public class SplitRule extends androidx.window.embedding.EmbeddingRule {
+    method public final androidx.window.embedding.SplitAttributes getDefaultSplitAttributes();
+    method public final androidx.window.embedding.EmbeddingAspectRatio getMaxAspectRatioInLandscape();
+    method public final androidx.window.embedding.EmbeddingAspectRatio getMaxAspectRatioInPortrait();
+    method public final int getMinHeightDp();
+    method public final int getMinSmallestWidthDp();
+    method public final int getMinWidthDp();
+    property public final androidx.window.embedding.SplitAttributes defaultSplitAttributes;
+    property public final androidx.window.embedding.EmbeddingAspectRatio maxAspectRatioInLandscape;
+    property public final androidx.window.embedding.EmbeddingAspectRatio maxAspectRatioInPortrait;
+    property public final int minHeightDp;
+    property public final int minSmallestWidthDp;
+    property public final int minWidthDp;
+    field public static final androidx.window.embedding.SplitRule.Companion Companion;
+    field public static final androidx.window.embedding.EmbeddingAspectRatio SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT;
+    field public static final androidx.window.embedding.EmbeddingAspectRatio SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT;
+    field public static final int SPLIT_MIN_DIMENSION_ALWAYS_ALLOW = 0; // 0x0
+    field public static final int SPLIT_MIN_DIMENSION_DP_DEFAULT = 600; // 0x258
+  }
+
+  public static final class SplitRule.Companion {
+  }
+
+  public static final class SplitRule.FinishBehavior {
+    field public static final androidx.window.embedding.SplitRule.FinishBehavior ADJACENT;
+    field public static final androidx.window.embedding.SplitRule.FinishBehavior ALWAYS;
+    field public static final androidx.window.embedding.SplitRule.FinishBehavior.Companion Companion;
+    field public static final androidx.window.embedding.SplitRule.FinishBehavior NEVER;
+  }
+
+  public static final class SplitRule.FinishBehavior.Companion {
+  }
+
+}
+
+package androidx.window.layout {
+
+  public interface DisplayFeature {
+    method public android.graphics.Rect getBounds();
+    property public abstract android.graphics.Rect bounds;
+  }
+
+  public interface FoldingFeature extends androidx.window.layout.DisplayFeature {
+    method public androidx.window.layout.FoldingFeature.OcclusionType getOcclusionType();
+    method public androidx.window.layout.FoldingFeature.Orientation getOrientation();
+    method public androidx.window.layout.FoldingFeature.State getState();
+    method public boolean isSeparating();
+    property public abstract boolean isSeparating;
+    property public abstract androidx.window.layout.FoldingFeature.OcclusionType occlusionType;
+    property public abstract androidx.window.layout.FoldingFeature.Orientation orientation;
+    property public abstract androidx.window.layout.FoldingFeature.State state;
+  }
+
+  public static final class FoldingFeature.OcclusionType {
+    field public static final androidx.window.layout.FoldingFeature.OcclusionType.Companion Companion;
+    field public static final androidx.window.layout.FoldingFeature.OcclusionType FULL;
+    field public static final androidx.window.layout.FoldingFeature.OcclusionType NONE;
+  }
+
+  public static final class FoldingFeature.OcclusionType.Companion {
+  }
+
+  public static final class FoldingFeature.Orientation {
+    field public static final androidx.window.layout.FoldingFeature.Orientation.Companion Companion;
+    field public static final androidx.window.layout.FoldingFeature.Orientation HORIZONTAL;
+    field public static final androidx.window.layout.FoldingFeature.Orientation VERTICAL;
+  }
+
+  public static final class FoldingFeature.Orientation.Companion {
+  }
+
+  public static final class FoldingFeature.State {
+    field public static final androidx.window.layout.FoldingFeature.State.Companion Companion;
+    field public static final androidx.window.layout.FoldingFeature.State FLAT;
+    field public static final androidx.window.layout.FoldingFeature.State HALF_OPENED;
+  }
+
+  public static final class FoldingFeature.State.Companion {
+  }
+
+  public interface WindowInfoTracker {
+    method public static androidx.window.layout.WindowInfoTracker getOrCreate(android.content.Context context);
+    method public kotlinx.coroutines.flow.Flow<androidx.window.layout.WindowLayoutInfo> windowLayoutInfo(android.app.Activity activity);
+    method public default kotlinx.coroutines.flow.Flow<androidx.window.layout.WindowLayoutInfo> windowLayoutInfo(@UiContext android.content.Context context);
+    field public static final androidx.window.layout.WindowInfoTracker.Companion Companion;
+  }
+
+  public static final class WindowInfoTracker.Companion {
+    method public androidx.window.layout.WindowInfoTracker getOrCreate(android.content.Context context);
+  }
+
+  public final class WindowLayoutInfo {
+    method public java.util.List<androidx.window.layout.DisplayFeature> getDisplayFeatures();
+    property public final java.util.List<androidx.window.layout.DisplayFeature> displayFeatures;
+  }
+
+  public final class WindowMetrics {
+    method public android.graphics.Rect getBounds();
+    method @SuppressCompatibility @RequiresApi(android.os.Build.VERSION_CODES.R) @androidx.window.core.ExperimentalWindowApi public androidx.core.view.WindowInsetsCompat getWindowInsets();
+    property public final android.graphics.Rect bounds;
+  }
+
+  public interface WindowMetricsCalculator {
+    method public androidx.window.layout.WindowMetrics computeCurrentWindowMetrics(android.app.Activity activity);
+    method public default androidx.window.layout.WindowMetrics computeCurrentWindowMetrics(@UiContext android.content.Context context);
+    method public androidx.window.layout.WindowMetrics computeMaximumWindowMetrics(android.app.Activity activity);
+    method public default androidx.window.layout.WindowMetrics computeMaximumWindowMetrics(@UiContext android.content.Context context);
+    method public static androidx.window.layout.WindowMetricsCalculator getOrCreate();
+    field public static final androidx.window.layout.WindowMetricsCalculator.Companion Companion;
+  }
+
+  public static final class WindowMetricsCalculator.Companion {
+    method public androidx.window.layout.WindowMetricsCalculator getOrCreate();
+  }
+
+}
+
diff --git a/window/window/api/current.txt b/window/window/api/current.txt
index 2c51c8d..7c79bde 100644
--- a/window/window/api/current.txt
+++ b/window/window/api/current.txt
@@ -14,14 +14,14 @@
 
 package androidx.window.area {
 
-  public final class WindowAreaCapability {
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public final class WindowAreaCapability {
     method public androidx.window.area.WindowAreaCapability.Operation getOperation();
     method public androidx.window.area.WindowAreaCapability.Status getStatus();
     property public final androidx.window.area.WindowAreaCapability.Operation operation;
     property public final androidx.window.area.WindowAreaCapability.Status status;
   }
 
-  public static final class WindowAreaCapability.Operation {
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static final class WindowAreaCapability.Operation {
     field public static final androidx.window.area.WindowAreaCapability.Operation.Companion Companion;
     field public static final androidx.window.area.WindowAreaCapability.Operation OPERATION_PRESENT_ON_AREA;
     field public static final androidx.window.area.WindowAreaCapability.Operation OPERATION_TRANSFER_ACTIVITY_TO_AREA;
@@ -30,7 +30,7 @@
   public static final class WindowAreaCapability.Operation.Companion {
   }
 
-  public static final class WindowAreaCapability.Status {
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static final class WindowAreaCapability.Status {
     field public static final androidx.window.area.WindowAreaCapability.Status.Companion Companion;
     field public static final androidx.window.area.WindowAreaCapability.Status WINDOW_AREA_STATUS_ACTIVE;
     field public static final androidx.window.area.WindowAreaCapability.Status WINDOW_AREA_STATUS_AVAILABLE;
@@ -41,7 +41,7 @@
   public static final class WindowAreaCapability.Status.Companion {
   }
 
-  public interface WindowAreaController {
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public interface WindowAreaController {
     method public static androidx.window.area.WindowAreaController getOrCreate();
     method public kotlinx.coroutines.flow.Flow<java.util.List<androidx.window.area.WindowAreaInfo>> getWindowAreaInfos();
     method public void presentContentOnWindowArea(android.os.Binder token, android.app.Activity activity, java.util.concurrent.Executor executor, androidx.window.area.WindowAreaPresentationSessionCallback windowAreaPresentationSessionCallback);
@@ -54,7 +54,7 @@
     method public androidx.window.area.WindowAreaController getOrCreate();
   }
 
-  public final class WindowAreaInfo {
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public final class WindowAreaInfo {
     method public androidx.window.area.WindowAreaSession? getActiveSession(androidx.window.area.WindowAreaCapability.Operation operation);
     method public androidx.window.area.WindowAreaCapability? getCapability(androidx.window.area.WindowAreaCapability.Operation operation);
     method public androidx.window.layout.WindowMetrics getMetrics();
@@ -66,7 +66,7 @@
     property public final androidx.window.area.WindowAreaInfo.Type type;
   }
 
-  public static final class WindowAreaInfo.Type {
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static final class WindowAreaInfo.Type {
     field public static final androidx.window.area.WindowAreaInfo.Type.Companion Companion;
     field public static final androidx.window.area.WindowAreaInfo.Type TYPE_REAR_FACING;
   }
@@ -74,22 +74,22 @@
   public static final class WindowAreaInfo.Type.Companion {
   }
 
-  public interface WindowAreaPresentationSessionCallback {
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public interface WindowAreaPresentationSessionCallback {
     method public void onContainerVisibilityChanged(boolean isVisible);
     method public void onSessionEnded(Throwable? t);
     method public void onSessionStarted(androidx.window.area.WindowAreaSessionPresenter session);
   }
 
-  public interface WindowAreaSession {
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public interface WindowAreaSession {
     method public void close();
   }
 
-  public interface WindowAreaSessionCallback {
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public interface WindowAreaSessionCallback {
     method public void onSessionEnded(Throwable? t);
     method public void onSessionStarted(androidx.window.area.WindowAreaSession session);
   }
 
-  public interface WindowAreaSessionPresenter extends androidx.window.area.WindowAreaSession {
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public interface WindowAreaSessionPresenter extends androidx.window.area.WindowAreaSession {
     method public android.content.Context getContext();
     method public void setContentView(android.view.View view);
     property public abstract android.content.Context context;
diff --git a/window/window/api/res-1.2.0-beta01.txt b/window/window/api/res-1.2.0-beta01.txt
new file mode 100644
index 0000000..185352b
--- /dev/null
+++ b/window/window/api/res-1.2.0-beta01.txt
@@ -0,0 +1,21 @@
+attr activityAction
+attr activityName
+attr alwaysExpand
+attr animationBackgroundColor
+attr clearTop
+attr finishPrimaryWithPlaceholder
+attr finishPrimaryWithSecondary
+attr finishSecondaryWithPrimary
+attr placeholderActivityName
+attr primaryActivityName
+attr secondaryActivityAction
+attr secondaryActivityName
+attr splitLayoutDirection
+attr splitMaxAspectRatioInLandscape
+attr splitMaxAspectRatioInPortrait
+attr splitMinHeightDp
+attr splitMinSmallestWidthDp
+attr splitMinWidthDp
+attr splitRatio
+attr stickyPlaceholder
+attr tag
diff --git a/window/window/api/restricted_1.2.0-beta01.txt b/window/window/api/restricted_1.2.0-beta01.txt
new file mode 100644
index 0000000..7c79bde
--- /dev/null
+++ b/window/window/api/restricted_1.2.0-beta01.txt
@@ -0,0 +1,465 @@
+// Signature format: 4.0
+package androidx.window {
+
+  public final class WindowProperties {
+    field public static final androidx.window.WindowProperties INSTANCE;
+    field public static final String PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE = "android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE";
+    field public static final String PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED = "android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED";
+    field public static final String PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED = "android.window.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED";
+    field public static final String PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE";
+    field public static final String PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES = "android.window.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES";
+  }
+
+}
+
+package androidx.window.area {
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public final class WindowAreaCapability {
+    method public androidx.window.area.WindowAreaCapability.Operation getOperation();
+    method public androidx.window.area.WindowAreaCapability.Status getStatus();
+    property public final androidx.window.area.WindowAreaCapability.Operation operation;
+    property public final androidx.window.area.WindowAreaCapability.Status status;
+  }
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static final class WindowAreaCapability.Operation {
+    field public static final androidx.window.area.WindowAreaCapability.Operation.Companion Companion;
+    field public static final androidx.window.area.WindowAreaCapability.Operation OPERATION_PRESENT_ON_AREA;
+    field public static final androidx.window.area.WindowAreaCapability.Operation OPERATION_TRANSFER_ACTIVITY_TO_AREA;
+  }
+
+  public static final class WindowAreaCapability.Operation.Companion {
+  }
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static final class WindowAreaCapability.Status {
+    field public static final androidx.window.area.WindowAreaCapability.Status.Companion Companion;
+    field public static final androidx.window.area.WindowAreaCapability.Status WINDOW_AREA_STATUS_ACTIVE;
+    field public static final androidx.window.area.WindowAreaCapability.Status WINDOW_AREA_STATUS_AVAILABLE;
+    field public static final androidx.window.area.WindowAreaCapability.Status WINDOW_AREA_STATUS_UNAVAILABLE;
+    field public static final androidx.window.area.WindowAreaCapability.Status WINDOW_AREA_STATUS_UNSUPPORTED;
+  }
+
+  public static final class WindowAreaCapability.Status.Companion {
+  }
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public interface WindowAreaController {
+    method public static androidx.window.area.WindowAreaController getOrCreate();
+    method public kotlinx.coroutines.flow.Flow<java.util.List<androidx.window.area.WindowAreaInfo>> getWindowAreaInfos();
+    method public void presentContentOnWindowArea(android.os.Binder token, android.app.Activity activity, java.util.concurrent.Executor executor, androidx.window.area.WindowAreaPresentationSessionCallback windowAreaPresentationSessionCallback);
+    method public void transferActivityToWindowArea(android.os.Binder token, android.app.Activity activity, java.util.concurrent.Executor executor, androidx.window.area.WindowAreaSessionCallback windowAreaSessionCallback);
+    property public abstract kotlinx.coroutines.flow.Flow<java.util.List<androidx.window.area.WindowAreaInfo>> windowAreaInfos;
+    field public static final androidx.window.area.WindowAreaController.Companion Companion;
+  }
+
+  public static final class WindowAreaController.Companion {
+    method public androidx.window.area.WindowAreaController getOrCreate();
+  }
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public final class WindowAreaInfo {
+    method public androidx.window.area.WindowAreaSession? getActiveSession(androidx.window.area.WindowAreaCapability.Operation operation);
+    method public androidx.window.area.WindowAreaCapability? getCapability(androidx.window.area.WindowAreaCapability.Operation operation);
+    method public androidx.window.layout.WindowMetrics getMetrics();
+    method public android.os.Binder getToken();
+    method public androidx.window.area.WindowAreaInfo.Type getType();
+    method public void setMetrics(androidx.window.layout.WindowMetrics);
+    property public final androidx.window.layout.WindowMetrics metrics;
+    property public final android.os.Binder token;
+    property public final androidx.window.area.WindowAreaInfo.Type type;
+  }
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static final class WindowAreaInfo.Type {
+    field public static final androidx.window.area.WindowAreaInfo.Type.Companion Companion;
+    field public static final androidx.window.area.WindowAreaInfo.Type TYPE_REAR_FACING;
+  }
+
+  public static final class WindowAreaInfo.Type.Companion {
+  }
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public interface WindowAreaPresentationSessionCallback {
+    method public void onContainerVisibilityChanged(boolean isVisible);
+    method public void onSessionEnded(Throwable? t);
+    method public void onSessionStarted(androidx.window.area.WindowAreaSessionPresenter session);
+  }
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public interface WindowAreaSession {
+    method public void close();
+  }
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public interface WindowAreaSessionCallback {
+    method public void onSessionEnded(Throwable? t);
+    method public void onSessionStarted(androidx.window.area.WindowAreaSession session);
+  }
+
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public interface WindowAreaSessionPresenter extends androidx.window.area.WindowAreaSession {
+    method public android.content.Context getContext();
+    method public void setContentView(android.view.View view);
+    property public abstract android.content.Context context;
+  }
+
+}
+
+package androidx.window.core {
+
+  @SuppressCompatibility @kotlin.RequiresOptIn(level=kotlin.RequiresOptIn.Level.WARNING) @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalWindowApi {
+  }
+
+}
+
+package androidx.window.embedding {
+
+  public final class ActivityEmbeddingController {
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public void finishActivityStacks(java.util.Set<androidx.window.embedding.ActivityStack> activityStacks);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public androidx.window.embedding.ActivityStack? getActivityStack(android.app.Activity activity);
+    method public static androidx.window.embedding.ActivityEmbeddingController getInstance(android.content.Context context);
+    method public boolean isActivityEmbedded(android.app.Activity activity);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public boolean isFinishingActivityStacksSupported();
+    field public static final androidx.window.embedding.ActivityEmbeddingController.Companion Companion;
+  }
+
+  public static final class ActivityEmbeddingController.Companion {
+    method public androidx.window.embedding.ActivityEmbeddingController getInstance(android.content.Context context);
+  }
+
+  public final class ActivityEmbeddingOptions {
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static boolean isSetLaunchingActivityStackSupported(android.app.ActivityOptions);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static android.app.ActivityOptions setLaunchingActivityStack(android.app.ActivityOptions, android.app.Activity activity);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static android.app.ActivityOptions setLaunchingActivityStack(android.app.ActivityOptions, android.content.Context context, androidx.window.embedding.ActivityStack activityStack);
+  }
+
+  public final class ActivityFilter {
+    ctor public ActivityFilter(android.content.ComponentName componentName, String? intentAction);
+    method public android.content.ComponentName getComponentName();
+    method public String? getIntentAction();
+    method public boolean matchesActivity(android.app.Activity activity);
+    method public boolean matchesIntent(android.content.Intent intent);
+    property public final android.content.ComponentName componentName;
+    property public final String? intentAction;
+  }
+
+  public final class ActivityRule extends androidx.window.embedding.EmbeddingRule {
+    method public boolean getAlwaysExpand();
+    method public java.util.Set<androidx.window.embedding.ActivityFilter> getFilters();
+    property public final boolean alwaysExpand;
+    property public final java.util.Set<androidx.window.embedding.ActivityFilter> filters;
+  }
+
+  public static final class ActivityRule.Builder {
+    ctor public ActivityRule.Builder(java.util.Set<androidx.window.embedding.ActivityFilter> filters);
+    method public androidx.window.embedding.ActivityRule build();
+    method public androidx.window.embedding.ActivityRule.Builder setAlwaysExpand(boolean alwaysExpand);
+    method public androidx.window.embedding.ActivityRule.Builder setTag(String? tag);
+  }
+
+  public final class ActivityStack {
+    method public operator boolean contains(android.app.Activity activity);
+    method public boolean isEmpty();
+    property public final boolean isEmpty;
+  }
+
+  public final class EmbeddingAspectRatio {
+    method public static androidx.window.embedding.EmbeddingAspectRatio ratio(@FloatRange(from=1.0, fromInclusive=false) float ratio);
+    field public static final androidx.window.embedding.EmbeddingAspectRatio ALWAYS_ALLOW;
+    field public static final androidx.window.embedding.EmbeddingAspectRatio ALWAYS_DISALLOW;
+    field public static final androidx.window.embedding.EmbeddingAspectRatio.Companion Companion;
+  }
+
+  public static final class EmbeddingAspectRatio.Companion {
+    method public androidx.window.embedding.EmbeddingAspectRatio ratio(@FloatRange(from=1.0, fromInclusive=false) float ratio);
+  }
+
+  public abstract class EmbeddingRule {
+    method public final String? getTag();
+    property public final String? tag;
+  }
+
+  public final class RuleController {
+    method public void addRule(androidx.window.embedding.EmbeddingRule rule);
+    method public void clearRules();
+    method public static androidx.window.embedding.RuleController getInstance(android.content.Context context);
+    method public java.util.Set<androidx.window.embedding.EmbeddingRule> getRules();
+    method public static java.util.Set<androidx.window.embedding.EmbeddingRule> parseRules(android.content.Context context, @XmlRes int staticRuleResourceId);
+    method public void removeRule(androidx.window.embedding.EmbeddingRule rule);
+    method public void setRules(java.util.Set<? extends androidx.window.embedding.EmbeddingRule> rules);
+    field public static final androidx.window.embedding.RuleController.Companion Companion;
+  }
+
+  public static final class RuleController.Companion {
+    method public androidx.window.embedding.RuleController getInstance(android.content.Context context);
+    method public java.util.Set<androidx.window.embedding.EmbeddingRule> parseRules(android.content.Context context, @XmlRes int staticRuleResourceId);
+  }
+
+  public final class SplitAttributes {
+    method public androidx.window.embedding.SplitAttributes.LayoutDirection getLayoutDirection();
+    method public androidx.window.embedding.SplitAttributes.SplitType getSplitType();
+    property public final androidx.window.embedding.SplitAttributes.LayoutDirection layoutDirection;
+    property public final androidx.window.embedding.SplitAttributes.SplitType splitType;
+    field public static final androidx.window.embedding.SplitAttributes.Companion Companion;
+  }
+
+  public static final class SplitAttributes.Builder {
+    ctor public SplitAttributes.Builder();
+    method public androidx.window.embedding.SplitAttributes build();
+    method public androidx.window.embedding.SplitAttributes.Builder setLayoutDirection(androidx.window.embedding.SplitAttributes.LayoutDirection layoutDirection);
+    method public androidx.window.embedding.SplitAttributes.Builder setSplitType(androidx.window.embedding.SplitAttributes.SplitType type);
+  }
+
+  public static final class SplitAttributes.Companion {
+  }
+
+  public static final class SplitAttributes.LayoutDirection {
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection BOTTOM_TO_TOP;
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection.Companion Companion;
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection LEFT_TO_RIGHT;
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection LOCALE;
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection RIGHT_TO_LEFT;
+    field public static final androidx.window.embedding.SplitAttributes.LayoutDirection TOP_TO_BOTTOM;
+  }
+
+  public static final class SplitAttributes.LayoutDirection.Companion {
+  }
+
+  public static final class SplitAttributes.SplitType {
+    method public static androidx.window.embedding.SplitAttributes.SplitType ratio(@FloatRange(from=0.0, to=1.0, fromInclusive=false, toInclusive=false) float ratio);
+    field public static final androidx.window.embedding.SplitAttributes.SplitType.Companion Companion;
+    field public static final androidx.window.embedding.SplitAttributes.SplitType SPLIT_TYPE_EQUAL;
+    field public static final androidx.window.embedding.SplitAttributes.SplitType SPLIT_TYPE_EXPAND;
+    field public static final androidx.window.embedding.SplitAttributes.SplitType SPLIT_TYPE_HINGE;
+  }
+
+  public static final class SplitAttributes.SplitType.Companion {
+    method public androidx.window.embedding.SplitAttributes.SplitType ratio(@FloatRange(from=0.0, to=1.0, fromInclusive=false, toInclusive=false) float ratio);
+  }
+
+  public final class SplitAttributesCalculatorParams {
+    method public boolean getAreDefaultConstraintsSatisfied();
+    method public androidx.window.embedding.SplitAttributes getDefaultSplitAttributes();
+    method public android.content.res.Configuration getParentConfiguration();
+    method public androidx.window.layout.WindowLayoutInfo getParentWindowLayoutInfo();
+    method public androidx.window.layout.WindowMetrics getParentWindowMetrics();
+    method public String? getSplitRuleTag();
+    property public final boolean areDefaultConstraintsSatisfied;
+    property public final androidx.window.embedding.SplitAttributes defaultSplitAttributes;
+    property public final android.content.res.Configuration parentConfiguration;
+    property public final androidx.window.layout.WindowLayoutInfo parentWindowLayoutInfo;
+    property public final androidx.window.layout.WindowMetrics parentWindowMetrics;
+    property public final String? splitRuleTag;
+  }
+
+  public final class SplitController {
+    method public void clearSplitAttributesCalculator();
+    method public static androidx.window.embedding.SplitController getInstance(android.content.Context context);
+    method public androidx.window.embedding.SplitController.SplitSupportStatus getSplitSupportStatus();
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public void invalidateTopVisibleSplitAttributes();
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public boolean isInvalidatingTopVisibleSplitAttributesSupported();
+    method public boolean isSplitAttributesCalculatorSupported();
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public boolean isUpdatingSplitAttributesSupported();
+    method public void setSplitAttributesCalculator(kotlin.jvm.functions.Function1<? super androidx.window.embedding.SplitAttributesCalculatorParams,androidx.window.embedding.SplitAttributes> calculator);
+    method public kotlinx.coroutines.flow.Flow<java.util.List<androidx.window.embedding.SplitInfo>> splitInfoList(android.app.Activity activity);
+    method @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public void updateSplitAttributes(androidx.window.embedding.SplitInfo splitInfo, androidx.window.embedding.SplitAttributes splitAttributes);
+    property public final androidx.window.embedding.SplitController.SplitSupportStatus splitSupportStatus;
+    field public static final androidx.window.embedding.SplitController.Companion Companion;
+  }
+
+  public static final class SplitController.Companion {
+    method public androidx.window.embedding.SplitController getInstance(android.content.Context context);
+  }
+
+  public static final class SplitController.SplitSupportStatus {
+    field public static final androidx.window.embedding.SplitController.SplitSupportStatus.Companion Companion;
+    field public static final androidx.window.embedding.SplitController.SplitSupportStatus SPLIT_AVAILABLE;
+    field public static final androidx.window.embedding.SplitController.SplitSupportStatus SPLIT_ERROR_PROPERTY_NOT_DECLARED;
+    field public static final androidx.window.embedding.SplitController.SplitSupportStatus SPLIT_UNAVAILABLE;
+  }
+
+  public static final class SplitController.SplitSupportStatus.Companion {
+  }
+
+  public final class SplitInfo {
+    method public operator boolean contains(android.app.Activity activity);
+    method public androidx.window.embedding.ActivityStack getPrimaryActivityStack();
+    method public androidx.window.embedding.ActivityStack getSecondaryActivityStack();
+    method public androidx.window.embedding.SplitAttributes getSplitAttributes();
+    property public final androidx.window.embedding.ActivityStack primaryActivityStack;
+    property public final androidx.window.embedding.ActivityStack secondaryActivityStack;
+    property public final androidx.window.embedding.SplitAttributes splitAttributes;
+  }
+
+  public final class SplitPairFilter {
+    ctor public SplitPairFilter(android.content.ComponentName primaryActivityName, android.content.ComponentName secondaryActivityName, String? secondaryActivityIntentAction);
+    method public android.content.ComponentName getPrimaryActivityName();
+    method public String? getSecondaryActivityIntentAction();
+    method public android.content.ComponentName getSecondaryActivityName();
+    method public boolean matchesActivityIntentPair(android.app.Activity primaryActivity, android.content.Intent secondaryActivityIntent);
+    method public boolean matchesActivityPair(android.app.Activity primaryActivity, android.app.Activity secondaryActivity);
+    property public final android.content.ComponentName primaryActivityName;
+    property public final String? secondaryActivityIntentAction;
+    property public final android.content.ComponentName secondaryActivityName;
+  }
+
+  public final class SplitPairRule extends androidx.window.embedding.SplitRule {
+    method public boolean getClearTop();
+    method public java.util.Set<androidx.window.embedding.SplitPairFilter> getFilters();
+    method public androidx.window.embedding.SplitRule.FinishBehavior getFinishPrimaryWithSecondary();
+    method public androidx.window.embedding.SplitRule.FinishBehavior getFinishSecondaryWithPrimary();
+    property public final boolean clearTop;
+    property public final java.util.Set<androidx.window.embedding.SplitPairFilter> filters;
+    property public final androidx.window.embedding.SplitRule.FinishBehavior finishPrimaryWithSecondary;
+    property public final androidx.window.embedding.SplitRule.FinishBehavior finishSecondaryWithPrimary;
+  }
+
+  public static final class SplitPairRule.Builder {
+    ctor public SplitPairRule.Builder(java.util.Set<androidx.window.embedding.SplitPairFilter> filters);
+    method public androidx.window.embedding.SplitPairRule build();
+    method public androidx.window.embedding.SplitPairRule.Builder setClearTop(boolean clearTop);
+    method public androidx.window.embedding.SplitPairRule.Builder setDefaultSplitAttributes(androidx.window.embedding.SplitAttributes defaultSplitAttributes);
+    method public androidx.window.embedding.SplitPairRule.Builder setFinishPrimaryWithSecondary(androidx.window.embedding.SplitRule.FinishBehavior finishPrimaryWithSecondary);
+    method public androidx.window.embedding.SplitPairRule.Builder setFinishSecondaryWithPrimary(androidx.window.embedding.SplitRule.FinishBehavior finishSecondaryWithPrimary);
+    method public androidx.window.embedding.SplitPairRule.Builder setMaxAspectRatioInLandscape(androidx.window.embedding.EmbeddingAspectRatio aspectRatio);
+    method public androidx.window.embedding.SplitPairRule.Builder setMaxAspectRatioInPortrait(androidx.window.embedding.EmbeddingAspectRatio aspectRatio);
+    method public androidx.window.embedding.SplitPairRule.Builder setMinHeightDp(@IntRange(from=0L) int minHeightDp);
+    method public androidx.window.embedding.SplitPairRule.Builder setMinSmallestWidthDp(@IntRange(from=0L) int minSmallestWidthDp);
+    method public androidx.window.embedding.SplitPairRule.Builder setMinWidthDp(@IntRange(from=0L) int minWidthDp);
+    method public androidx.window.embedding.SplitPairRule.Builder setTag(String? tag);
+  }
+
+  public final class SplitPlaceholderRule extends androidx.window.embedding.SplitRule {
+    method public java.util.Set<androidx.window.embedding.ActivityFilter> getFilters();
+    method public androidx.window.embedding.SplitRule.FinishBehavior getFinishPrimaryWithPlaceholder();
+    method public android.content.Intent getPlaceholderIntent();
+    method public boolean isSticky();
+    property public final java.util.Set<androidx.window.embedding.ActivityFilter> filters;
+    property public final androidx.window.embedding.SplitRule.FinishBehavior finishPrimaryWithPlaceholder;
+    property public final boolean isSticky;
+    property public final android.content.Intent placeholderIntent;
+  }
+
+  public static final class SplitPlaceholderRule.Builder {
+    ctor public SplitPlaceholderRule.Builder(java.util.Set<androidx.window.embedding.ActivityFilter> filters, android.content.Intent placeholderIntent);
+    method public androidx.window.embedding.SplitPlaceholderRule build();
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setDefaultSplitAttributes(androidx.window.embedding.SplitAttributes defaultSplitAttributes);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setFinishPrimaryWithPlaceholder(androidx.window.embedding.SplitRule.FinishBehavior finishPrimaryWithPlaceholder);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setMaxAspectRatioInLandscape(androidx.window.embedding.EmbeddingAspectRatio aspectRatio);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setMaxAspectRatioInPortrait(androidx.window.embedding.EmbeddingAspectRatio aspectRatio);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setMinHeightDp(@IntRange(from=0L) int minHeightDp);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setMinSmallestWidthDp(@IntRange(from=0L) int minSmallestWidthDp);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setMinWidthDp(@IntRange(from=0L) int minWidthDp);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setSticky(boolean isSticky);
+    method public androidx.window.embedding.SplitPlaceholderRule.Builder setTag(String? tag);
+  }
+
+  public class SplitRule extends androidx.window.embedding.EmbeddingRule {
+    method public final androidx.window.embedding.SplitAttributes getDefaultSplitAttributes();
+    method public final androidx.window.embedding.EmbeddingAspectRatio getMaxAspectRatioInLandscape();
+    method public final androidx.window.embedding.EmbeddingAspectRatio getMaxAspectRatioInPortrait();
+    method public final int getMinHeightDp();
+    method public final int getMinSmallestWidthDp();
+    method public final int getMinWidthDp();
+    property public final androidx.window.embedding.SplitAttributes defaultSplitAttributes;
+    property public final androidx.window.embedding.EmbeddingAspectRatio maxAspectRatioInLandscape;
+    property public final androidx.window.embedding.EmbeddingAspectRatio maxAspectRatioInPortrait;
+    property public final int minHeightDp;
+    property public final int minSmallestWidthDp;
+    property public final int minWidthDp;
+    field public static final androidx.window.embedding.SplitRule.Companion Companion;
+    field public static final androidx.window.embedding.EmbeddingAspectRatio SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT;
+    field public static final androidx.window.embedding.EmbeddingAspectRatio SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT;
+    field public static final int SPLIT_MIN_DIMENSION_ALWAYS_ALLOW = 0; // 0x0
+    field public static final int SPLIT_MIN_DIMENSION_DP_DEFAULT = 600; // 0x258
+  }
+
+  public static final class SplitRule.Companion {
+  }
+
+  public static final class SplitRule.FinishBehavior {
+    field public static final androidx.window.embedding.SplitRule.FinishBehavior ADJACENT;
+    field public static final androidx.window.embedding.SplitRule.FinishBehavior ALWAYS;
+    field public static final androidx.window.embedding.SplitRule.FinishBehavior.Companion Companion;
+    field public static final androidx.window.embedding.SplitRule.FinishBehavior NEVER;
+  }
+
+  public static final class SplitRule.FinishBehavior.Companion {
+  }
+
+}
+
+package androidx.window.layout {
+
+  public interface DisplayFeature {
+    method public android.graphics.Rect getBounds();
+    property public abstract android.graphics.Rect bounds;
+  }
+
+  public interface FoldingFeature extends androidx.window.layout.DisplayFeature {
+    method public androidx.window.layout.FoldingFeature.OcclusionType getOcclusionType();
+    method public androidx.window.layout.FoldingFeature.Orientation getOrientation();
+    method public androidx.window.layout.FoldingFeature.State getState();
+    method public boolean isSeparating();
+    property public abstract boolean isSeparating;
+    property public abstract androidx.window.layout.FoldingFeature.OcclusionType occlusionType;
+    property public abstract androidx.window.layout.FoldingFeature.Orientation orientation;
+    property public abstract androidx.window.layout.FoldingFeature.State state;
+  }
+
+  public static final class FoldingFeature.OcclusionType {
+    field public static final androidx.window.layout.FoldingFeature.OcclusionType.Companion Companion;
+    field public static final androidx.window.layout.FoldingFeature.OcclusionType FULL;
+    field public static final androidx.window.layout.FoldingFeature.OcclusionType NONE;
+  }
+
+  public static final class FoldingFeature.OcclusionType.Companion {
+  }
+
+  public static final class FoldingFeature.Orientation {
+    field public static final androidx.window.layout.FoldingFeature.Orientation.Companion Companion;
+    field public static final androidx.window.layout.FoldingFeature.Orientation HORIZONTAL;
+    field public static final androidx.window.layout.FoldingFeature.Orientation VERTICAL;
+  }
+
+  public static final class FoldingFeature.Orientation.Companion {
+  }
+
+  public static final class FoldingFeature.State {
+    field public static final androidx.window.layout.FoldingFeature.State.Companion Companion;
+    field public static final androidx.window.layout.FoldingFeature.State FLAT;
+    field public static final androidx.window.layout.FoldingFeature.State HALF_OPENED;
+  }
+
+  public static final class FoldingFeature.State.Companion {
+  }
+
+  public interface WindowInfoTracker {
+    method public static androidx.window.layout.WindowInfoTracker getOrCreate(android.content.Context context);
+    method public kotlinx.coroutines.flow.Flow<androidx.window.layout.WindowLayoutInfo> windowLayoutInfo(android.app.Activity activity);
+    method public default kotlinx.coroutines.flow.Flow<androidx.window.layout.WindowLayoutInfo> windowLayoutInfo(@UiContext android.content.Context context);
+    field public static final androidx.window.layout.WindowInfoTracker.Companion Companion;
+  }
+
+  public static final class WindowInfoTracker.Companion {
+    method public androidx.window.layout.WindowInfoTracker getOrCreate(android.content.Context context);
+  }
+
+  public final class WindowLayoutInfo {
+    method public java.util.List<androidx.window.layout.DisplayFeature> getDisplayFeatures();
+    property public final java.util.List<androidx.window.layout.DisplayFeature> displayFeatures;
+  }
+
+  public final class WindowMetrics {
+    method public android.graphics.Rect getBounds();
+    method @SuppressCompatibility @RequiresApi(android.os.Build.VERSION_CODES.R) @androidx.window.core.ExperimentalWindowApi public androidx.core.view.WindowInsetsCompat getWindowInsets();
+    property public final android.graphics.Rect bounds;
+  }
+
+  public interface WindowMetricsCalculator {
+    method public androidx.window.layout.WindowMetrics computeCurrentWindowMetrics(android.app.Activity activity);
+    method public default androidx.window.layout.WindowMetrics computeCurrentWindowMetrics(@UiContext android.content.Context context);
+    method public androidx.window.layout.WindowMetrics computeMaximumWindowMetrics(android.app.Activity activity);
+    method public default androidx.window.layout.WindowMetrics computeMaximumWindowMetrics(@UiContext android.content.Context context);
+    method public static androidx.window.layout.WindowMetricsCalculator getOrCreate();
+    field public static final androidx.window.layout.WindowMetricsCalculator.Companion Companion;
+  }
+
+  public static final class WindowMetricsCalculator.Companion {
+    method public androidx.window.layout.WindowMetricsCalculator getOrCreate();
+  }
+
+}
+
diff --git a/window/window/api/restricted_current.txt b/window/window/api/restricted_current.txt
index 2c51c8d..7c79bde 100644
--- a/window/window/api/restricted_current.txt
+++ b/window/window/api/restricted_current.txt
@@ -14,14 +14,14 @@
 
 package androidx.window.area {
 
-  public final class WindowAreaCapability {
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public final class WindowAreaCapability {
     method public androidx.window.area.WindowAreaCapability.Operation getOperation();
     method public androidx.window.area.WindowAreaCapability.Status getStatus();
     property public final androidx.window.area.WindowAreaCapability.Operation operation;
     property public final androidx.window.area.WindowAreaCapability.Status status;
   }
 
-  public static final class WindowAreaCapability.Operation {
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static final class WindowAreaCapability.Operation {
     field public static final androidx.window.area.WindowAreaCapability.Operation.Companion Companion;
     field public static final androidx.window.area.WindowAreaCapability.Operation OPERATION_PRESENT_ON_AREA;
     field public static final androidx.window.area.WindowAreaCapability.Operation OPERATION_TRANSFER_ACTIVITY_TO_AREA;
@@ -30,7 +30,7 @@
   public static final class WindowAreaCapability.Operation.Companion {
   }
 
-  public static final class WindowAreaCapability.Status {
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static final class WindowAreaCapability.Status {
     field public static final androidx.window.area.WindowAreaCapability.Status.Companion Companion;
     field public static final androidx.window.area.WindowAreaCapability.Status WINDOW_AREA_STATUS_ACTIVE;
     field public static final androidx.window.area.WindowAreaCapability.Status WINDOW_AREA_STATUS_AVAILABLE;
@@ -41,7 +41,7 @@
   public static final class WindowAreaCapability.Status.Companion {
   }
 
-  public interface WindowAreaController {
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public interface WindowAreaController {
     method public static androidx.window.area.WindowAreaController getOrCreate();
     method public kotlinx.coroutines.flow.Flow<java.util.List<androidx.window.area.WindowAreaInfo>> getWindowAreaInfos();
     method public void presentContentOnWindowArea(android.os.Binder token, android.app.Activity activity, java.util.concurrent.Executor executor, androidx.window.area.WindowAreaPresentationSessionCallback windowAreaPresentationSessionCallback);
@@ -54,7 +54,7 @@
     method public androidx.window.area.WindowAreaController getOrCreate();
   }
 
-  public final class WindowAreaInfo {
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public final class WindowAreaInfo {
     method public androidx.window.area.WindowAreaSession? getActiveSession(androidx.window.area.WindowAreaCapability.Operation operation);
     method public androidx.window.area.WindowAreaCapability? getCapability(androidx.window.area.WindowAreaCapability.Operation operation);
     method public androidx.window.layout.WindowMetrics getMetrics();
@@ -66,7 +66,7 @@
     property public final androidx.window.area.WindowAreaInfo.Type type;
   }
 
-  public static final class WindowAreaInfo.Type {
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public static final class WindowAreaInfo.Type {
     field public static final androidx.window.area.WindowAreaInfo.Type.Companion Companion;
     field public static final androidx.window.area.WindowAreaInfo.Type TYPE_REAR_FACING;
   }
@@ -74,22 +74,22 @@
   public static final class WindowAreaInfo.Type.Companion {
   }
 
-  public interface WindowAreaPresentationSessionCallback {
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public interface WindowAreaPresentationSessionCallback {
     method public void onContainerVisibilityChanged(boolean isVisible);
     method public void onSessionEnded(Throwable? t);
     method public void onSessionStarted(androidx.window.area.WindowAreaSessionPresenter session);
   }
 
-  public interface WindowAreaSession {
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public interface WindowAreaSession {
     method public void close();
   }
 
-  public interface WindowAreaSessionCallback {
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public interface WindowAreaSessionCallback {
     method public void onSessionEnded(Throwable? t);
     method public void onSessionStarted(androidx.window.area.WindowAreaSession session);
   }
 
-  public interface WindowAreaSessionPresenter extends androidx.window.area.WindowAreaSession {
+  @SuppressCompatibility @androidx.window.core.ExperimentalWindowApi public interface WindowAreaSessionPresenter extends androidx.window.area.WindowAreaSession {
     method public android.content.Context getContext();
     method public void setContentView(android.view.View view);
     property public abstract android.content.Context context;
diff --git a/window/window/src/androidTest/java/androidx/window/area/WindowAreaControllerImplTest.kt b/window/window/src/androidTest/java/androidx/window/area/WindowAreaControllerImplTest.kt
index 4ae7871..92b327a 100644
--- a/window/window/src/androidTest/java/androidx/window/area/WindowAreaControllerImplTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/area/WindowAreaControllerImplTest.kt
@@ -32,6 +32,7 @@
 import androidx.window.area.WindowAreaCapability.Operation.Companion.OPERATION_TRANSFER_ACTIVITY_TO_AREA
 import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_AVAILABLE
 import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_UNAVAILABLE
+import androidx.window.core.ExperimentalWindowApi
 import androidx.window.extensions.area.ExtensionWindowAreaPresentation
 import androidx.window.extensions.area.ExtensionWindowAreaStatus
 import androidx.window.extensions.area.WindowAreaComponent
@@ -59,7 +60,7 @@
 import org.junit.Rule
 import org.junit.Test
 
-@OptIn(ExperimentalCoroutinesApi::class)
+@OptIn(ExperimentalCoroutinesApi::class, ExperimentalWindowApi::class)
 class WindowAreaControllerImplTest {
 
     @get:Rule
diff --git a/window/window/src/androidTest/java/androidx/window/layout/WindowInfoTrackerImplTest.kt b/window/window/src/androidTest/java/androidx/window/layout/WindowInfoTrackerImplTest.kt
index 9ff7856..69882ce 100644
--- a/window/window/src/androidTest/java/androidx/window/layout/WindowInfoTrackerImplTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/layout/WindowInfoTrackerImplTest.kt
@@ -24,19 +24,20 @@
 import androidx.window.TestConsumer
 import androidx.window.WindowTestUtils
 import androidx.window.WindowTestUtils.Companion.assumeAtLeastVendorApiLevel
-import androidx.window.core.ExperimentalWindowApi
 import androidx.window.layout.adapter.WindowBackend
 import java.util.concurrent.Executor
+import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
 import org.junit.Rule
 import org.junit.Test
 
-@OptIn(ExperimentalCoroutinesApi::class, ExperimentalWindowApi::class)
+@OptIn(ExperimentalCoroutinesApi::class)
 public class WindowInfoTrackerImplTest {
 
     @get:Rule
@@ -45,6 +46,10 @@
 
     private val testScope = TestScope(UnconfinedTestDispatcher())
 
+    init {
+        Dispatchers.setMain(UnconfinedTestDispatcher())
+    }
+
     @Test
     public fun testWindowLayoutFeatures(): Unit = testScope.runTest {
         activityScenario.scenario.onActivity { testActivity ->
diff --git a/window/window/src/main/java/androidx/window/area/EmptyWindowAreaControllerImpl.kt b/window/window/src/main/java/androidx/window/area/EmptyWindowAreaControllerImpl.kt
index 07fcfd5..7c0cb61 100644
--- a/window/window/src/main/java/androidx/window/area/EmptyWindowAreaControllerImpl.kt
+++ b/window/window/src/main/java/androidx/window/area/EmptyWindowAreaControllerImpl.kt
@@ -18,6 +18,7 @@
 
 import android.app.Activity
 import android.os.Binder
+import androidx.window.core.ExperimentalWindowApi
 import java.util.concurrent.Executor
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flowOf
@@ -25,6 +26,7 @@
 /**
  * Empty Implementation for devices that do not support the [WindowAreaController] functionality
  */
+@ExperimentalWindowApi
 internal class EmptyWindowAreaControllerImpl : WindowAreaController {
 
     override val windowAreaInfos: Flow<List<WindowAreaInfo>>
diff --git a/window/window/src/main/java/androidx/window/area/RearDisplayPresentationSessionPresenterImpl.kt b/window/window/src/main/java/androidx/window/area/RearDisplayPresentationSessionPresenterImpl.kt
index 60aabc8..e0e9574 100644
--- a/window/window/src/main/java/androidx/window/area/RearDisplayPresentationSessionPresenterImpl.kt
+++ b/window/window/src/main/java/androidx/window/area/RearDisplayPresentationSessionPresenterImpl.kt
@@ -18,9 +18,11 @@
 
 import android.content.Context
 import android.view.View
+import androidx.window.core.ExperimentalWindowApi
 import androidx.window.extensions.area.ExtensionWindowAreaPresentation
 import androidx.window.extensions.area.WindowAreaComponent
 
+@ExperimentalWindowApi
 internal class RearDisplayPresentationSessionPresenterImpl(
     private val windowAreaComponent: WindowAreaComponent,
     private val presentation: ExtensionWindowAreaPresentation
diff --git a/window/window/src/main/java/androidx/window/area/RearDisplaySessionImpl.kt b/window/window/src/main/java/androidx/window/area/RearDisplaySessionImpl.kt
index 5a4a9a3..9a5bbd3 100644
--- a/window/window/src/main/java/androidx/window/area/RearDisplaySessionImpl.kt
+++ b/window/window/src/main/java/androidx/window/area/RearDisplaySessionImpl.kt
@@ -16,8 +16,10 @@
 
 package androidx.window.area
 
+import androidx.window.core.ExperimentalWindowApi
 import androidx.window.extensions.area.WindowAreaComponent
 
+@ExperimentalWindowApi
 internal class RearDisplaySessionImpl(
     private val windowAreaComponent: WindowAreaComponent
 ) : WindowAreaSession {
diff --git a/window/window/src/main/java/androidx/window/area/WindowAreaAdapter.kt b/window/window/src/main/java/androidx/window/area/WindowAreaAdapter.kt
index c884dc6..ba76dfd 100644
--- a/window/window/src/main/java/androidx/window/area/WindowAreaAdapter.kt
+++ b/window/window/src/main/java/androidx/window/area/WindowAreaAdapter.kt
@@ -20,6 +20,7 @@
 import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_AVAILABLE
 import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_UNAVAILABLE
 import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_UNSUPPORTED
+import androidx.window.core.ExperimentalWindowApi
 import androidx.window.extensions.area.WindowAreaComponent
 import androidx.window.extensions.area.WindowAreaComponent.STATUS_ACTIVE
 import androidx.window.extensions.area.WindowAreaComponent.STATUS_AVAILABLE
@@ -30,6 +31,7 @@
  * Adapter object to assist in translating values received from [WindowAreaComponent]
  * to developer friendly values in [WindowAreaController]
  */
+@ExperimentalWindowApi
 internal object WindowAreaAdapter {
 
     internal fun translate(
diff --git a/window/window/src/main/java/androidx/window/area/WindowAreaCapability.kt b/window/window/src/main/java/androidx/window/area/WindowAreaCapability.kt
index 7992fbd..cc0cde3 100644
--- a/window/window/src/main/java/androidx/window/area/WindowAreaCapability.kt
+++ b/window/window/src/main/java/androidx/window/area/WindowAreaCapability.kt
@@ -17,10 +17,12 @@
 package androidx.window.area
 
 import android.app.Activity
+import androidx.window.core.ExperimentalWindowApi
 
 /**
  * Represents a capability for a [WindowAreaInfo].
  */
+@ExperimentalWindowApi
 class WindowAreaCapability internal constructor(val operation: Operation, val status: Status) {
     override fun toString(): String {
         return "Operation: $operation: Status: $status"
@@ -29,6 +31,7 @@
     /**
      * Represents the status of availability for a specific [WindowAreaCapability]
      */
+    @ExperimentalWindowApi
     class Status private constructor(private val description: String) {
         override fun toString(): String {
             return description
@@ -75,6 +78,7 @@
     /**
      * Represents an operation that a [WindowAreaInfo] may support.
      */
+    @ExperimentalWindowApi
     class Operation private constructor(private val description: String) {
         override fun toString(): String {
             return description
diff --git a/window/window/src/main/java/androidx/window/area/WindowAreaController.kt b/window/window/src/main/java/androidx/window/area/WindowAreaController.kt
index e02ab52..ae2500c 100644
--- a/window/window/src/main/java/androidx/window/area/WindowAreaController.kt
+++ b/window/window/src/main/java/androidx/window/area/WindowAreaController.kt
@@ -24,6 +24,7 @@
 import androidx.window.area.WindowAreaInfo.Type.Companion.TYPE_REAR_FACING
 import androidx.window.area.utils.DeviceUtils
 import androidx.window.core.BuildConfig
+import androidx.window.core.ExperimentalWindowApi
 import androidx.window.core.ExtensionsUtil
 import androidx.window.core.VerificationMode
 import java.util.concurrent.Executor
@@ -34,6 +35,7 @@
  * displays or display areas on a device.
  *
  */
+@ExperimentalWindowApi
 interface WindowAreaController {
 
     /**
@@ -181,6 +183,7 @@
  * Decorator that allows us to provide different functionality
  * in our window-testing artifact.
  */
+@ExperimentalWindowApi
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 interface WindowAreaControllerDecorator {
     /**
@@ -190,6 +193,7 @@
     public fun decorate(controller: WindowAreaController): WindowAreaController
 }
 
+@ExperimentalWindowApi
 private object EmptyDecorator : WindowAreaControllerDecorator {
     override fun decorate(controller: WindowAreaController): WindowAreaController {
         return controller
diff --git a/window/window/src/main/java/androidx/window/area/WindowAreaControllerImpl.kt b/window/window/src/main/java/androidx/window/area/WindowAreaControllerImpl.kt
index 641a277..c815a24 100644
--- a/window/window/src/main/java/androidx/window/area/WindowAreaControllerImpl.kt
+++ b/window/window/src/main/java/androidx/window/area/WindowAreaControllerImpl.kt
@@ -27,6 +27,7 @@
 import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_UNSUPPORTED
 import androidx.window.area.utils.DeviceUtils
 import androidx.window.core.BuildConfig
+import androidx.window.core.ExperimentalWindowApi
 import androidx.window.core.VerificationMode
 import androidx.window.extensions.area.ExtensionWindowAreaStatus
 import androidx.window.extensions.area.WindowAreaComponent
@@ -55,6 +56,7 @@
  * [Build.VERSION_CODES.S] as that's the min level of support for
  * this functionality.
  */
+@ExperimentalWindowApi
 @RequiresApi(Build.VERSION_CODES.Q)
 internal class WindowAreaControllerImpl(
     private val windowAreaComponent: WindowAreaComponent,
diff --git a/window/window/src/main/java/androidx/window/area/WindowAreaInfo.kt b/window/window/src/main/java/androidx/window/area/WindowAreaInfo.kt
index dcfd01f..4aa63a1b 100644
--- a/window/window/src/main/java/androidx/window/area/WindowAreaInfo.kt
+++ b/window/window/src/main/java/androidx/window/area/WindowAreaInfo.kt
@@ -20,6 +20,7 @@
 import androidx.window.area.WindowAreaCapability.Operation.Companion.OPERATION_PRESENT_ON_AREA
 import androidx.window.area.WindowAreaCapability.Operation.Companion.OPERATION_TRANSFER_ACTIVITY_TO_AREA
 import androidx.window.area.WindowAreaCapability.Status.Companion.WINDOW_AREA_STATUS_ACTIVE
+import androidx.window.core.ExperimentalWindowApi
 import androidx.window.extensions.area.WindowAreaComponent
 import androidx.window.layout.WindowMetrics
 
@@ -28,6 +29,7 @@
  * display in the system. These values can be used to modify the UI to show/hide controls and
  * determine when features can be enabled.
  */
+@ExperimentalWindowApi
 class WindowAreaInfo internal constructor(
 
     /**
@@ -96,6 +98,7 @@
     /**
      * Represents a type of [WindowAreaInfo]
      */
+    @ExperimentalWindowApi
     class Type private constructor(private val description: String) {
         override fun toString(): String {
             return description
diff --git a/window/window/src/main/java/androidx/window/area/WindowAreaPresentationSessionCallback.kt b/window/window/src/main/java/androidx/window/area/WindowAreaPresentationSessionCallback.kt
index 05b6d2d..6cd548a 100644
--- a/window/window/src/main/java/androidx/window/area/WindowAreaPresentationSessionCallback.kt
+++ b/window/window/src/main/java/androidx/window/area/WindowAreaPresentationSessionCallback.kt
@@ -18,12 +18,14 @@
 
 import android.content.Context
 import android.view.View
+import androidx.window.core.ExperimentalWindowApi
 
 /**
  * A callback to notify about the lifecycle of a window area presentation session.
  *
  * @see WindowAreaController.presentContentOnWindowArea
  */
+@ExperimentalWindowApi
 interface WindowAreaPresentationSessionCallback {
 
     /**
diff --git a/window/window/src/main/java/androidx/window/area/WindowAreaSession.kt b/window/window/src/main/java/androidx/window/area/WindowAreaSession.kt
index 7743ef0..05b659c 100644
--- a/window/window/src/main/java/androidx/window/area/WindowAreaSession.kt
+++ b/window/window/src/main/java/androidx/window/area/WindowAreaSession.kt
@@ -16,11 +16,14 @@
 
 package androidx.window.area
 
+import androidx.window.core.ExperimentalWindowApi
+
 /**
  * Session interface to represent an active window area feature.
  *
  * @see WindowAreaSessionCallback.onSessionStarted
  */
+@ExperimentalWindowApi
 interface WindowAreaSession {
 
     /**
diff --git a/window/window/src/main/java/androidx/window/area/WindowAreaSessionCallback.kt b/window/window/src/main/java/androidx/window/area/WindowAreaSessionCallback.kt
index e551660..d02083e 100644
--- a/window/window/src/main/java/androidx/window/area/WindowAreaSessionCallback.kt
+++ b/window/window/src/main/java/androidx/window/area/WindowAreaSessionCallback.kt
@@ -16,11 +16,14 @@
 
 package androidx.window.area
 
+import androidx.window.core.ExperimentalWindowApi
+
 /**
  *  Callback to update the client on the WindowArea Session being
  * started and ended.
  * TODO(b/207720511) Move to window-java module when Kotlin API Finalized
  */
+@ExperimentalWindowApi
 interface WindowAreaSessionCallback {
 
     /**
diff --git a/window/window/src/main/java/androidx/window/area/WindowAreaSessionPresenter.kt b/window/window/src/main/java/androidx/window/area/WindowAreaSessionPresenter.kt
index b06cbbd..4cc05a9 100644
--- a/window/window/src/main/java/androidx/window/area/WindowAreaSessionPresenter.kt
+++ b/window/window/src/main/java/androidx/window/area/WindowAreaSessionPresenter.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.view.View
+import androidx.window.core.ExperimentalWindowApi
 
 /**
  * A container that allows getting access to and showing content on a window area. The container is
@@ -26,6 +27,7 @@
  * application window, or can be closed by calling [WindowAreaSessionPresenter.close].
  * @see WindowAreaController.presentContentOnWindowArea
  */
+@ExperimentalWindowApi
 interface WindowAreaSessionPresenter : WindowAreaSession {
     /**
      * Returns the [Context] associated with the window area.
diff --git a/window/window/src/main/java/androidx/window/layout/WindowInfoTrackerImpl.kt b/window/window/src/main/java/androidx/window/layout/WindowInfoTrackerImpl.kt
index 26dd99b..9424cae 100644
--- a/window/window/src/main/java/androidx/window/layout/WindowInfoTrackerImpl.kt
+++ b/window/window/src/main/java/androidx/window/layout/WindowInfoTrackerImpl.kt
@@ -20,11 +20,12 @@
 import android.content.Context
 import androidx.annotation.UiContext
 import androidx.core.util.Consumer
-import androidx.window.core.ExperimentalWindowApi
 import androidx.window.layout.adapter.WindowBackend
+import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.flowOn
 
 /**
  * An implementation of [WindowInfoTracker] that provides the [WindowLayoutInfo] and
@@ -42,7 +43,6 @@
      * A [Flow] of window layout changes in the current visual [UiContext]. A context has to be
      * either an [Activity] or created with [Context#createWindowContext].
      */
-    @ExperimentalWindowApi
     override fun windowLayoutInfo(@UiContext context: Context): Flow<WindowLayoutInfo> {
         return callbackFlow {
             val listener = Consumer { info: WindowLayoutInfo -> trySend(info) }
@@ -50,7 +50,7 @@
             awaitClose {
                 windowBackend.unregisterLayoutChangeCallback(listener)
             }
-        }
+        }.flowOn(Dispatchers.Main)
     }
 
     /**
@@ -63,6 +63,6 @@
             awaitClose {
                 windowBackend.unregisterLayoutChangeCallback(listener)
             }
-        }
+        }.flowOn(Dispatchers.Main)
     }
 }